Repository: pypa/cibuildwheel

Issue:
feat: include size in the final printout
Would it be helpful to include the size in the final wheel printout? I find myself checking things like this:

```
11 wheels produced in 14 minutes:
  cmake_example-0.0.1-cp27-cp27m-macosx_10_9_x86_64.whl
  cmake_example-0.0.1-cp35-cp35m-macosx_10_9_x86_64.whl
  cmake_example-0.0.1-cp36-cp36m-macosx_10_9_x86_64.whl
  cmake_example-0.0.1-cp37-cp37m-macosx_10_9_x86_64.whl
  cmake_example-0.0.1-cp38-cp38-macosx_10_9_universal2.whl
  cmake_example-0.0.1-cp38-cp38-macosx_10_9_x86_64.whl
  cmake_example-0.0.1-cp39-cp39-macosx_10_9_universal2.whl
  cmake_example-0.0.1-cp39-cp39-macosx_10_9_x86_64.whl
  cmake_example-0.0.1-pp27-pypy_73-macosx_10_9_x86_64.whl
  cmake_example-0.0.1-pp36-pypy36_pp73-macosx_10_9_x86_64.whl
  cmake_example-0.0.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl
```

And wondering if the Universal2 part worked. If the size was listed at the end, that would help.

Discussion:
mayeut: closing/re-opening to trigger CI again (PR started during PyPI outage)

Patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 65ecf230..88226ed0 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -14,7 +14,7 @@ repos:
   - id: trailing-whitespace
 
 - repo: https://github.com/asottile/pyupgrade
-  rev: v2.29.1
+  rev: v2.31.0
   hooks:
   - id: pyupgrade
     name: PyUpgrade 3.6+
diff --git a/cibuildwheel/projectfiles.py b/cibuildwheel/projectfiles.py
index c4f63c17..fece392f 100644
--- a/cibuildwheel/projectfiles.py
+++ b/cibuildwheel/projectfiles.py
@@ -70,7 +70,7 @@ def get_requires_python_str(package_dir: Path) -> Optional[str]:
         pass
 
     try:
-        with (package_dir / "setup.py").open() as f2:
+        with (package_dir / "setup.py").open(encoding="utf8") as f2:
             return setup_py_python_requires(f2.read())
     except FileNotFoundError:
         pass
diff --git a/cibuildwheel/resources/constraints-python36.txt b/cibuildwheel/resources/constraints-python36.txt
index 6e35fbee..83fb265f 100644
--- a/cibuildwheel/resources/constraints-python36.txt
+++ b/cibuildwheel/resources/constraints-python36.txt
@@ -4,18 +4,14 @@
 #
 #    bin/update_dependencies.py
 #
-backports.entry-points-selectable==1.1.1
-    # via virtualenv
-delocate==0.10.1
+delocate==0.10.2
     # via -r cibuildwheel/resources/constraints.in
 distlib==0.3.4
     # via virtualenv
-filelock==3.4.0
+filelock==3.4.1
+    # via virtualenv
+importlib-metadata==4.8.3
     # via virtualenv
-importlib-metadata==4.8.2
-    # via
-    #   backports.entry-points-selectable
-    #   virtualenv
 importlib-resources==5.4.0
     # via virtualenv
 platformdirs==2.4.0
@@ -26,9 +22,9 @@ typing-extensions==4.0.1
     # via
     #   delocate
     #   importlib-metadata
-virtualenv==20.10.0
+virtualenv==20.13.0
     # via -r cibuildwheel/resources/constraints.in
-wheel==0.37.0
+wheel==0.37.1
     # via
     #   -r cibuildwheel/resources/constraints.in
     #   delocate
diff --git a/cibuildwheel/resources/constraints-python37.txt b/cibuildwheel/resources/constraints-python37.txt
index b0cb28c0..83aac3fc 100644
--- a/cibuildwheel/resources/constraints-python37.txt
+++ b/cibuildwheel/resources/constraints-python37.txt
@@ -4,19 +4,15 @@
 #
 #    bin/update_dependencies.py
 #
-backports.entry-points-selectable==1.1.1
-    # via virtualenv
-delocate==0.10.1
+delocate==0.10.2
     # via -r cibuildwheel/resources/constraints.in
 distlib==0.3.4
     # via virtualenv
-filelock==3.4.0
+filelock==3.4.2
     # via virtualenv
-importlib-metadata==4.8.2
-    # via
-    #   backports.entry-points-selectable
-    #   virtualenv
-platformdirs==2.4.0
+importlib-metadata==4.10.0
+    # via virtualenv
+platformdirs==2.4.1
     # via virtualenv
 six==1.16.0
     # via virtualenv
@@ -24,17 +20,17 @@ typing-extensions==4.0.1
     # via
     #   delocate
     #   importlib-metadata
-virtualenv==20.10.0
+virtualenv==20.13.0
     # via -r cibuildwheel/resources/constraints.in
-wheel==0.37.0
+wheel==0.37.1
     # via
     #   -r cibuildwheel/resources/constraints.in
     #   delocate
-zipp==3.6.0
+zipp==3.7.0
     # via importlib-metadata
 
 # The following packages are considered to be unsafe in a requirements file:
 pip==21.3.1
     # via -r cibuildwheel/resources/constraints.in
-setuptools==59.6.0
+setuptools==60.2.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python38.txt b/cibuildwheel/resources/constraints-python38.txt
index 266754d8..2026e038 100644
--- a/cibuildwheel/resources/constraints-python38.txt
+++ b/cibuildwheel/resources/constraints-python38.txt
@@ -4,23 +4,21 @@
 #
 #    bin/update_dependencies.py
 #
-backports.entry-points-selectable==1.1.1
-    # via virtualenv
-delocate==0.10.1
+delocate==0.10.2
     # via -r cibuildwheel/resources/constraints.in
 distlib==0.3.4
     # via virtualenv
-filelock==3.4.0
+filelock==3.4.2
     # via virtualenv
-platformdirs==2.4.0
+platformdirs==2.4.1
     # via virtualenv
 six==1.16.0
     # via virtualenv
 typing-extensions==4.0.1
     # via delocate
-virtualenv==20.10.0
+virtualenv==20.13.0
     # via -r cibuildwheel/resources/constraints.in
-wheel==0.37.0
+wheel==0.37.1
     # via
     #   -r cibuildwheel/resources/constraints.in
     #   delocate
@@ -28,5 +26,5 @@ wheel==0.37.0
 # The following packages are considered to be unsafe in a requirements file:
 pip==21.3.1
     # via -r cibuildwheel/resources/constraints.in
-setuptools==59.6.0
+setuptools==60.2.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints-python39.txt b/cibuildwheel/resources/constraints-python39.txt
index bb8c6134..af7a22e4 100644
--- a/cibuildwheel/resources/constraints-python39.txt
+++ b/cibuildwheel/resources/constraints-python39.txt
@@ -4,23 +4,21 @@
 #
 #    bin/update_dependencies.py
 #
-backports.entry-points-selectable==1.1.1
-    # via virtualenv
-delocate==0.10.1
+delocate==0.10.2
     # via -r cibuildwheel/resources/constraints.in
 distlib==0.3.4
     # via virtualenv
-filelock==3.4.0
+filelock==3.4.2
     # via virtualenv
-platformdirs==2.4.0
+platformdirs==2.4.1
     # via virtualenv
 six==1.16.0
     # via virtualenv
 typing-extensions==4.0.1
     # via delocate
-virtualenv==20.10.0
+virtualenv==20.13.0
     # via -r cibuildwheel/resources/constraints.in
-wheel==0.37.0
+wheel==0.37.1
     # via
     #   -r cibuildwheel/resources/constraints.in
     #   delocate
@@ -28,5 +26,5 @@ wheel==0.37.0
 # The following packages are considered to be unsafe in a requirements file:
 pip==21.3.1
     # via -r cibuildwheel/resources/constraints.in
-setuptools==59.6.0
+setuptools==60.2.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/constraints.txt b/cibuildwheel/resources/constraints.txt
index bb8c6134..af7a22e4 100644
--- a/cibuildwheel/resources/constraints.txt
+++ b/cibuildwheel/resources/constraints.txt
@@ -4,23 +4,21 @@
 #
 #    bin/update_dependencies.py
 #
-backports.entry-points-selectable==1.1.1
-    # via virtualenv
-delocate==0.10.1
+delocate==0.10.2
     # via -r cibuildwheel/resources/constraints.in
 distlib==0.3.4
     # via virtualenv
-filelock==3.4.0
+filelock==3.4.2
     # via virtualenv
-platformdirs==2.4.0
+platformdirs==2.4.1
     # via virtualenv
 six==1.16.0
     # via virtualenv
 typing-extensions==4.0.1
     # via delocate
-virtualenv==20.10.0
+virtualenv==20.13.0
     # via -r cibuildwheel/resources/constraints.in
-wheel==0.37.0
+wheel==0.37.1
     # via
     #   -r cibuildwheel/resources/constraints.in
     #   delocate
@@ -28,5 +26,5 @@ wheel==0.37.0
 # The following packages are considered to be unsafe in a requirements file:
 pip==21.3.1
     # via -r cibuildwheel/resources/constraints.in
-setuptools==59.6.0
+setuptools==60.2.0
     # via -r cibuildwheel/resources/constraints.in
diff --git a/cibuildwheel/resources/pinned_docker_images.cfg b/cibuildwheel/resources/pinned_docker_images.cfg
index 82afe6ee..8d25f7f7 100644
--- a/cibuildwheel/resources/pinned_docker_images.cfg
+++ b/cibuildwheel/resources/pinned_docker_images.cfg
@@ -1,43 +1,43 @@
 [x86_64]
-manylinux1 = quay.io/pypa/manylinux1_x86_64:2021-12-11-951973d
-manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2021-12-12-e5100b5
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2021-12-12-e5100b5
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2021-12-12-e5100b5
-musllinux_1_1 = quay.io/pypa/musllinux_1_1_x86_64:2021-12-12-e5100b5
+manylinux1 = quay.io/pypa/manylinux1_x86_64:2022-01-02-f55f613
+manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-01-02-2b36e6e
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2022-01-02-2b36e6e
+manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-01-02-2b36e6e
+musllinux_1_1 = quay.io/pypa/musllinux_1_1_x86_64:2022-01-02-2b36e6e
 
 [i686]
-manylinux1 = quay.io/pypa/manylinux1_i686:2021-12-11-951973d
-manylinux2010 = quay.io/pypa/manylinux2010_i686:2021-12-12-e5100b5
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2021-12-12-e5100b5
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2021-12-12-e5100b5
-musllinux_1_1 = quay.io/pypa/musllinux_1_1_i686:2021-12-12-e5100b5
+manylinux1 = quay.io/pypa/manylinux1_i686:2022-01-02-f55f613
+manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-01-02-2b36e6e
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2022-01-02-2b36e6e
+manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-01-02-2b36e6e
+musllinux_1_1 = quay.io/pypa/musllinux_1_1_i686:2022-01-02-2b36e6e
 
 [pypy_x86_64]
-manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2021-12-12-e5100b5
-manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2021-12-12-e5100b5
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2021-12-12-e5100b5
+manylinux2010 = quay.io/pypa/manylinux2010_x86_64:2022-01-02-2b36e6e
+manylinux2014 = quay.io/pypa/manylinux2014_x86_64:2022-01-02-2b36e6e
+manylinux_2_24 = quay.io/pypa/manylinux_2_24_x86_64:2022-01-02-2b36e6e
 
 [pypy_i686]
-manylinux2010 = quay.io/pypa/manylinux2010_i686:2021-12-12-e5100b5
-manylinux2014 = quay.io/pypa/manylinux2014_i686:2021-12-12-e5100b5
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2021-12-12-e5100b5
+manylinux2010 = quay.io/pypa/manylinux2010_i686:2022-01-02-2b36e6e
+manylinux2014 = quay.io/pypa/manylinux2014_i686:2022-01-02-2b36e6e
+manylinux_2_24 = quay.io/pypa/manylinux_2_24_i686:2022-01-02-2b36e6e
 
 [aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2021-12-12-e5100b5
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2021-12-12-e5100b5
-musllinux_1_1 = quay.io/pypa/musllinux_1_1_aarch64:2021-12-12-e5100b5
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2022-01-02-2b36e6e
+manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-01-02-2b36e6e
+musllinux_1_1 = quay.io/pypa/musllinux_1_1_aarch64:2022-01-02-2b36e6e
 
 [ppc64le]
-manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2021-12-12-e5100b5
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_ppc64le:2021-12-12-e5100b5
-musllinux_1_1 = quay.io/pypa/musllinux_1_1_ppc64le:2021-12-12-e5100b5
+manylinux2014 = quay.io/pypa/manylinux2014_ppc64le:2022-01-02-2b36e6e
+manylinux_2_24 = quay.io/pypa/manylinux_2_24_ppc64le:2022-01-02-2b36e6e
+musllinux_1_1 = quay.io/pypa/musllinux_1_1_ppc64le:2022-01-02-2b36e6e
 
 [s390x]
-manylinux2014 = quay.io/pypa/manylinux2014_s390x:2021-12-12-e5100b5
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_s390x:2021-12-12-e5100b5
-musllinux_1_1 = quay.io/pypa/musllinux_1_1_s390x:2021-12-12-e5100b5
+manylinux2014 = quay.io/pypa/manylinux2014_s390x:2022-01-02-2b36e6e
+manylinux_2_24 = quay.io/pypa/manylinux_2_24_s390x:2022-01-02-2b36e6e
+musllinux_1_1 = quay.io/pypa/musllinux_1_1_s390x:2022-01-02-2b36e6e
 
 [pypy_aarch64]
-manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2021-12-12-e5100b5
-manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2021-12-12-e5100b5
+manylinux2014 = quay.io/pypa/manylinux2014_aarch64:2022-01-02-2b36e6e
+manylinux_2_24 = quay.io/pypa/manylinux_2_24_aarch64:2022-01-02-2b36e6e
 
diff --git a/cibuildwheel/util.py b/cibuildwheel/util.py
index 2b1c2844..36298c01 100644
--- a/cibuildwheel/util.py
+++ b/cibuildwheel/util.py
@@ -12,7 +12,7 @@
 from enum import Enum
 from pathlib import Path
 from time import sleep
-from typing import Any, Dict, Iterable, Iterator, List, Optional, TextIO
+from typing import Any, Dict, Iterable, Iterator, List, NamedTuple, Optional, TextIO
 
 import bracex
 import certifi
@@ -352,11 +352,27 @@ def print_new_wheels(msg: str, output_dir: Path) -> Iterator[None]:
     existing_contents = set(output_dir.iterdir())
     yield
     final_contents = set(output_dir.iterdir())
-    new_contents = final_contents - existing_contents
+
+    class FileReport(NamedTuple):
+        name: str
+        size: str
+
+    new_contents = [
+        FileReport(wheel.name, f"{(wheel.stat().st_size + 1023) // 1024:,d}")
+        for wheel in final_contents - existing_contents
+    ]
+    max_name_len = max(len(f.name) for f in new_contents)
+    max_size_len = max(len(f.size) for f in new_contents)
     n = len(new_contents)
     s = time.time() - start_time
     m = s / 60
-    print(msg.format(n=n, s=s, m=m), *sorted(f"  {f.name}" for f in new_contents), sep="\n")
+    print(
+        msg.format(n=n, s=s, m=m),
+        *sorted(
+            f"  {f.name:<{max_name_len}s}   {f.size:>{max_size_len}s} kB" for f in new_contents
+        ),
+        sep="\n",
+    )
 
 
 def get_pip_version(env: Dict[str, str]) -> str:
diff --git a/docs/working-examples.md b/docs/working-examples.md
index 03f57941..d42c8c91 100644
--- a/docs/working-examples.md
+++ b/docs/working-examples.md
@@ -29,8 +29,8 @@
 | [Triton][]                        | ![github icon][] | ![linux icon][] | Self hosted runners |
 | [PyOxidizer][]                    | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | A modern Python application packaging and distribution tool |
 | [OpenSpiel][]                     | ![github icon][] | ![apple icon][] ![linux icon][] | OpenSpiel is a collection of environments and algorithms for research in general reinforcement learning and search/planning in games. |
-| [pyzmq][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python bindings for zeromq, the networking library. Uses Cython and CFFI.  |
 | [River][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | 🌊 Online machine learning in Python |
+| [pyzmq][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python bindings for zeromq, the networking library. Uses Cython and CFFI.  |
 | [vispy][]                         | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Main repository for Vispy |
 | [aiortc][]                        | ![github icon][] | ![apple icon][] ![linux icon][] | WebRTC and ORTC implementation for Python using asyncio. |
 | [Confluent client for Kafka][]    | ![travisci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | setup in `tools/wheels/build-wheels.bat` |
@@ -67,8 +67,8 @@
 | [cyvcf2][]                        | ![github icon][] | ![apple icon][] ![linux icon][] | cython + htslib == fast VCF and BCF processing |
 | [sourmash][]                      | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Quickly search, compare, and analyze genomic and metagenomic data sets. |
 | [time-machine][]                  | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Time mocking library using only the CPython C API. |
-| [matrixprofile][]                 | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python 3 library making time series data mining tasks, utilizing matrix profile algorithms, accessible to everyone. |
 | [CTranslate2][]                   | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Includes libraries from the [Intel oneAPI toolkit](https://www.intel.com/content/www/us/en/developer/tools/oneapi/base-toolkit.html) and CUDA kernels compiled for multiple GPU architectures. |
+| [matrixprofile][]                 | ![travisci icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A Python 3 library making time series data mining tasks, utilizing matrix profile algorithms, accessible to everyone. |
 | [jq.py][]                         | ![travisci icon][] | ![apple icon][] ![linux icon][] | Python bindings for jq |
 | [iminuit][]                       | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Jupyter-friendly Python interface for C++ MINUIT2 |
 | [Tokenizer][]                     | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast and customizable text tokenization library with BPE and SentencePiece support |
@@ -80,18 +80,18 @@
 | [pybase64][]                      | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Fast Base64 encoding/decoding in Python |
 | [etebase-py][]                    | ![travisci icon][] | ![linux icon][] | Python bindings to a Rust library using `setuptools-rust`, and `sccache` for improved speed. |
 | [fathon][]                        | ![travisci icon][] | ![apple icon][] ![linux icon][] | python package for DFA (Detrended Fluctuation Analysis) and related algorithms |
-| [pyjet][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The interface between FastJet and NumPy |
 | [numpythia][]                     | ![github icon][] | ![apple icon][] ![linux icon][] | The interface between PYTHIA and NumPy |
-| [polaroid][]                      | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Full range of wheels for setuptools rust, with auto release and PyPI deploy. |
+| [pyjet][]                         | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | The interface between FastJet and NumPy |
 | [Imagecodecs (fork)][]            | ![azurepipelines icon][] | ![apple icon][] ![linux icon][] | Over 20 external dependencies in compiled libraries, custom docker image, `libomp`, `openblas` and `install_name_tool` for macOS. |
+| [polaroid][]                      | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Full range of wheels for setuptools rust, with auto release and PyPI deploy. |
 | [clang-format][]                  | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Scikit-build wrapper around LLVM's CMake, all platforms, generic wheels. |
 | [ninja][]                         | ![github icon][] ![travisci icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Multitagged binary builds for all supported platforms, using cibw 2 config configuration. |
 | [pybind11 scikit_build_example][] | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | An example combining scikit-build and pybind11 |
 | [GSD][]                           | ![github icon][] | ![apple icon][] ![linux icon][] ![windows icon][] | Cython and NumPy project with 64-bit wheels. |
 | [pyinstrument_cext][]             | ![travisci icon][] ![appveyor icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | A simple C extension, without external dependencies |
 | [xmlstarlet][]                    | ![github icon][] | ![windows icon][] ![apple icon][] ![linux icon][] | Python 3.6+ CFFI bindings with true MSVC build. |
-| [CorrectionLib][]                 | ![github icon][] | ![apple icon][] ![linux icon][] | Structured JSON powered correction library for HEP, designed for the CMS experiment at CERN. |
 | [pillow-heif][]                   | ![github icon][] | ![apple icon][] ![linux icon][] | Python CFFI binding to libheif library with third party dependencies like `libde265`, `x265`, `libaom` with test & publishing on PyPi. |
+| [CorrectionLib][]                 | ![github icon][] | ![apple icon][] ![linux icon][] | Structured JSON powered correction library for HEP, designed for the CMS experiment at CERN. |
 | [SiPM][]                          | ![github icon][] | ![apple icon][] ![linux icon][] | High performance library for SiPM detectors simulation using C++17, OpenMP and AVX2 intrinsics. |
 
 [scikit-learn]: https://github.com/scikit-learn/scikit-learn
@@ -115,8 +115,8 @@
 [Triton]: https://github.com/openai/triton
 [PyOxidizer]: https://github.com/indygreg/PyOxidizer
 [OpenSpiel]: https://github.com/deepmind/open_spiel
-[pyzmq]: https://github.com/zeromq/pyzmq
 [River]: https://github.com/online-ml/river
+[pyzmq]: https://github.com/zeromq/pyzmq
 [vispy]: https://github.com/vispy/vispy
 [aiortc]: https://github.com/aiortc/aiortc
 [Confluent client for Kafka]: https://github.com/confluentinc/confluent-kafka-python
@@ -153,8 +153,8 @@
 [cyvcf2]: https://github.com/brentp/cyvcf2
 [sourmash]: https://github.com/dib-lab/sourmash
 [time-machine]: https://github.com/adamchainz/time-machine
-[matrixprofile]: https://github.com/matrix-profile-foundation/matrixprofile
 [CTranslate2]: https://github.com/OpenNMT/CTranslate2
+[matrixprofile]: https://github.com/matrix-profile-foundation/matrixprofile
 [jq.py]: https://github.com/mwilliamson/jq.py
 [iminuit]: https://github.com/scikit-hep/iminuit
 [Tokenizer]: https://github.com/OpenNMT/Tokenizer
@@ -166,18 +166,18 @@
 [pybase64]: https://github.com/mayeut/pybase64
 [etebase-py]: https://github.com/etesync/etebase-py
 [fathon]: https://github.com/stfbnc/fathon
-[pyjet]: https://github.com/scikit-hep/pyjet
 [numpythia]: https://github.com/scikit-hep/numpythia
-[polaroid]: https://github.com/daggy1234/polaroid
+[pyjet]: https://github.com/scikit-hep/pyjet
 [Imagecodecs (fork)]: https://github.com/czaki/imagecodecs_build
+[polaroid]: https://github.com/daggy1234/polaroid
 [clang-format]: https://github.com/ssciwr/clang-format-wheel
 [ninja]: https://github.com/scikit-build/ninja-python-distributions
 [pybind11 scikit_build_example]: https://github.com/pybind/scikit_build_example
 [GSD]: https://github.com/glotzerlab/gsd
 [pyinstrument_cext]: https://github.com/joerick/pyinstrument_cext
 [xmlstarlet]: https://github.com/dimitern/xmlstarlet
-[CorrectionLib]: https://github.com/cms-nanoAOD/correctionlib
 [pillow-heif]: https://github.com/bigcat88/pillow_heif
+[CorrectionLib]: https://github.com/cms-nanoAOD/correctionlib
 [SiPM]: https://github.com/EdoPro98/SimSiPM
 
 [appveyor icon]: data/readme_icons/appveyor.svg
@@ -190,91 +190,91 @@
 [apple icon]: data/readme_icons/apple.svg
 [linux icon]: data/readme_icons/linux.svg
 
-<!-- scikit-learn: 48285, last pushed 0 days ago -->
-<!-- Tornado: 20312, last pushed 6 days ago -->
-<!-- pytorch-fairseq: 14826, last pushed 0 days ago -->
-<!-- Matplotlib: 14713, last pushed 0 days ago -->
-<!-- MyPy: 11972, last pushed 0 days ago -->
-<!-- pydantic: 8297, last pushed 0 days ago -->
-<!-- uvloop: 8234, last pushed 4 days ago -->
-<!-- psutil: 7934, last pushed 6 days ago -->
-<!-- vaex: 6788, last pushed 0 days ago -->
-<!-- Google Benchmark: 5979, last pushed 0 days ago -->
-<!-- asyncpg: 5208, last pushed 5 days ago -->
-<!-- Apache Beam: 5145, last pushed 0 days ago -->
-<!-- scikit-image: 4685, last pushed 0 days ago -->
-<!-- twisted-iocpsupport: 4437, last pushed 7 days ago -->
-<!-- cmake: 4345, last pushed 0 days ago -->
-<!-- duckdb: 3992, last pushed 0 days ago -->
-<!-- websockets: 3716, last pushed 23 days ago -->
-<!-- cvxpy: 3639, last pushed 0 days ago -->
-<!-- Triton: 3404, last pushed 0 days ago -->
-<!-- PyOxidizer: 3378, last pushed 0 days ago -->
-<!-- OpenSpiel: 2979, last pushed 1 days ago -->
-<!-- pyzmq: 2971, last pushed 6 days ago -->
-<!-- River: 2970, last pushed 2 days ago -->
-<!-- vispy: 2770, last pushed 3 days ago -->
-<!-- aiortc: 2698, last pushed 12 days ago -->
-<!-- Confluent client for Kafka: 2573, last pushed 0 days ago -->
-<!-- tinyobjloader: 2363, last pushed 9 days ago -->
-<!-- coverage.py: 1854, last pushed 0 days ago -->
-<!-- PyCryptodome: 1814, last pushed 10 days ago -->
-<!-- Dependency Injector: 1760, last pushed 15 days ago -->
-<!-- numexpr: 1697, last pushed 3 days ago -->
-<!-- PyYAML: 1678, last pushed 12 days ago -->
-<!-- h5py: 1642, last pushed 2 days ago -->
-<!-- PyAV: 1408, last pushed 4 days ago -->
-<!-- OpenColorIO: 1202, last pushed 0 days ago -->
-<!-- PyTables: 1087, last pushed 0 days ago -->
-<!-- Line Profiler: 986, last pushed 11 days ago -->
-<!-- OpenTimelineIO: 947, last pushed 0 days ago -->
-<!-- ruptures: 871, last pushed 0 days ago -->
-<!-- pikepdf: 831, last pushed 0 days ago -->
-<!-- aioquic: 792, last pushed 7 days ago -->
-<!-- google neuroglancer: 704, last pushed 10 days ago -->
-<!-- DeepForest: 689, last pushed 144 days ago -->
-<!-- Parselmouth: 597, last pushed 13 days ago -->
-<!-- AutoPy: 596, last pushed 180 days ago -->
-<!-- Psycopg 3: 547, last pushed 1 days ago -->
-<!-- H3-py: 457, last pushed 41 days ago -->
-<!-- markupsafe: 438, last pushed 7 days ago -->
-<!-- python-rapidjson: 433, last pushed 59 days ago -->
-<!-- Rtree: 432, last pushed 69 days ago -->
-<!-- python-snappy: 413, last pushed 5 days ago -->
-<!-- pybind11 cmake_example: 336, last pushed 15 days ago -->
-<!-- KDEpy: 315, last pushed 194 days ago -->
-<!-- tgcalls: 293, last pushed 85 days ago -->
-<!-- dd-trace-py: 287, last pushed 0 days ago -->
-<!-- pybind11 python_example: 285, last pushed 15 days ago -->
-<!-- cyvcf2: 279, last pushed 6 days ago -->
-<!-- sourmash: 267, last pushed 10 days ago -->
-<!-- time-machine: 237, last pushed 0 days ago -->
-<!-- matrixprofile: 217, last pushed 171 days ago -->
-<!-- CTranslate2: 213, last pushed 1 days ago -->
-<!-- jq.py: 189, last pushed 133 days ago -->
-<!-- iminuit: 189, last pushed 30 days ago -->
-<!-- Tokenizer: 161, last pushed 6 days ago -->
-<!-- PyGLM: 124, last pushed 8 days ago -->
-<!-- bx-python: 108, last pushed 0 days ago -->
-<!-- boost-histogram: 101, last pushed 7 days ago -->
-<!-- iDynTree: 92, last pushed 8 days ago -->
-<!-- TgCrypto: 81, last pushed 63 days ago -->
-<!-- pybase64: 76, last pushed 0 days ago -->
-<!-- etebase-py: 53, last pushed 344 days ago -->
-<!-- fathon: 41, last pushed 26 days ago -->
-<!-- pyjet: 32, last pushed 15 days ago -->
-<!-- numpythia: 32, last pushed 15 days ago -->
-<!-- polaroid: 27, last pushed 2 days ago -->
-<!-- Imagecodecs (fork): 26, last pushed 24 days ago -->
-<!-- clang-format: 22, last pushed 61 days ago -->
-<!-- ninja: 20, last pushed 1 days ago -->
-<!-- pybind11 scikit_build_example: 19, last pushed 15 days ago -->
-<!-- GSD: 18, last pushed 27 days ago -->
-<!-- pyinstrument_cext: 10, last pushed 66 days ago -->
-<!-- xmlstarlet: 7, last pushed 36 days ago -->
-<!-- CorrectionLib: 7, last pushed 0 days ago -->
-<!-- pillow-heif: 7, last pushed 2 days ago -->
-<!-- SiPM: 4, last pushed 36 days ago -->
+<!-- scikit-learn: 48423, last pushed 0 days ago -->
+<!-- Tornado: 20356, last pushed 0 days ago -->
+<!-- pytorch-fairseq: 14979, last pushed 2 days ago -->
+<!-- Matplotlib: 14798, last pushed 0 days ago -->
+<!-- MyPy: 12086, last pushed 0 days ago -->
+<!-- pydantic: 8621, last pushed 0 days ago -->
+<!-- uvloop: 8276, last pushed 0 days ago -->
+<!-- psutil: 7974, last pushed 0 days ago -->
+<!-- vaex: 6815, last pushed 2 days ago -->
+<!-- Google Benchmark: 6036, last pushed 4 days ago -->
+<!-- asyncpg: 5229, last pushed 25 days ago -->
+<!-- Apache Beam: 5187, last pushed 0 days ago -->
+<!-- scikit-image: 4702, last pushed 2 days ago -->
+<!-- twisted-iocpsupport: 4457, last pushed 0 days ago -->
+<!-- cmake: 4390, last pushed 0 days ago -->
+<!-- duckdb: 4114, last pushed 1 days ago -->
+<!-- websockets: 3741, last pushed 9 days ago -->
+<!-- cvxpy: 3662, last pushed 4 days ago -->
+<!-- Triton: 3425, last pushed 2 days ago -->
+<!-- PyOxidizer: 3404, last pushed 2 days ago -->
+<!-- OpenSpiel: 3014, last pushed 1 days ago -->
+<!-- River: 3001, last pushed 4 days ago -->
+<!-- pyzmq: 2977, last pushed 6 days ago -->
+<!-- vispy: 2781, last pushed 16 days ago -->
+<!-- aiortc: 2723, last pushed 2 days ago -->
+<!-- Confluent client for Kafka: 2592, last pushed 13 days ago -->
+<!-- tinyobjloader: 2374, last pushed 6 days ago -->
+<!-- coverage.py: 1865, last pushed 2 days ago -->
+<!-- PyCryptodome: 1835, last pushed 2 days ago -->
+<!-- Dependency Injector: 1799, last pushed 13 days ago -->
+<!-- numexpr: 1708, last pushed 17 days ago -->
+<!-- PyYAML: 1697, last pushed 13 days ago -->
+<!-- h5py: 1652, last pushed 10 days ago -->
+<!-- PyAV: 1428, last pushed 2 days ago -->
+<!-- OpenColorIO: 1215, last pushed 11 days ago -->
+<!-- PyTables: 1091, last pushed 4 days ago -->
+<!-- Line Profiler: 1004, last pushed 3 days ago -->
+<!-- OpenTimelineIO: 962, last pushed 0 days ago -->
+<!-- ruptures: 878, last pushed 18 days ago -->
+<!-- pikepdf: 859, last pushed 2 days ago -->
+<!-- aioquic: 806, last pushed 3 days ago -->
+<!-- google neuroglancer: 706, last pushed 11 days ago -->
+<!-- DeepForest: 697, last pushed 163 days ago -->
+<!-- Parselmouth: 602, last pushed 13 days ago -->
+<!-- AutoPy: 602, last pushed 8 days ago -->
+<!-- Psycopg 3: 562, last pushed 0 days ago -->
+<!-- H3-py: 465, last pushed 61 days ago -->
+<!-- markupsafe: 439, last pushed 1 days ago -->
+<!-- python-rapidjson: 436, last pushed 78 days ago -->
+<!-- Rtree: 435, last pushed 12 days ago -->
+<!-- python-snappy: 418, last pushed 25 days ago -->
+<!-- pybind11 cmake_example: 344, last pushed 19 days ago -->
+<!-- KDEpy: 323, last pushed 213 days ago -->
+<!-- tgcalls: 305, last pushed 104 days ago -->
+<!-- dd-trace-py: 289, last pushed 2 days ago -->
+<!-- pybind11 python_example: 288, last pushed 18 days ago -->
+<!-- cyvcf2: 281, last pushed 25 days ago -->
+<!-- sourmash: 268, last pushed 4 days ago -->
+<!-- time-machine: 249, last pushed 4 days ago -->
+<!-- CTranslate2: 220, last pushed 4 days ago -->
+<!-- matrixprofile: 218, last pushed 191 days ago -->
+<!-- jq.py: 192, last pushed 153 days ago -->
+<!-- iminuit: 189, last pushed 50 days ago -->
+<!-- Tokenizer: 160, last pushed 25 days ago -->
+<!-- PyGLM: 127, last pushed 6 days ago -->
+<!-- bx-python: 109, last pushed 17 days ago -->
+<!-- boost-histogram: 103, last pushed 6 days ago -->
+<!-- iDynTree: 92, last pushed 6 days ago -->
+<!-- TgCrypto: 83, last pushed 11 days ago -->
+<!-- pybase64: 75, last pushed 3 days ago -->
+<!-- etebase-py: 53, last pushed 6 days ago -->
+<!-- fathon: 41, last pushed 46 days ago -->
+<!-- numpythia: 33, last pushed 18 days ago -->
+<!-- pyjet: 32, last pushed 19 days ago -->
+<!-- Imagecodecs (fork): 27, last pushed 44 days ago -->
+<!-- polaroid: 27, last pushed 22 days ago -->
+<!-- clang-format: 23, last pushed 80 days ago -->
+<!-- ninja: 21, last pushed 3 days ago -->
+<!-- pybind11 scikit_build_example: 18, last pushed 18 days ago -->
+<!-- GSD: 18, last pushed 46 days ago -->
+<!-- pyinstrument_cext: 10, last pushed 86 days ago -->
+<!-- xmlstarlet: 7, last pushed 55 days ago -->
+<!-- pillow-heif: 7, last pushed 22 days ago -->
+<!-- CorrectionLib: 6, last pushed 16 days ago -->
+<!-- SiPM: 4, last pushed 56 days ago -->
 
 <!-- END bin/projects.py -->
 


Test patch:
diff --git a/unit_test/projectfiles_test.py b/unit_test/projectfiles_test.py
index c62df6a9..6c55d46a 100644
--- a/unit_test/projectfiles_test.py
+++ b/unit_test/projectfiles_test.py
@@ -25,7 +25,7 @@ def test_read_setup_py_simple(tmp_path):
 
 
 def test_read_setup_py_full(tmp_path):
-    with open(tmp_path / "setup.py", "w") as f:
+    with open(tmp_path / "setup.py", "w", encoding="utf8") as f:
         f.write(
             dedent(
                 """
@@ -35,6 +35,7 @@ def test_read_setup_py_full(tmp_path):
 
                 setuptools.setup(
                     name = "hello",
+                    description = "≥“”ü",
                     other = 23,
                     example = ["item", "other"],
                     python_requires = "1.24",
@@ -43,7 +44,9 @@ def test_read_setup_py_full(tmp_path):
             )
         )
 
-    assert setup_py_python_requires(tmp_path.joinpath("setup.py").read_text()) == "1.24"
+    assert (
+        setup_py_python_requires(tmp_path.joinpath("setup.py").read_text(encoding="utf8")) == "1.24"
+    )
     assert get_requires_python_str(tmp_path) == "1.24"
 
 
diff --git a/unit_test/wheel_print_test.py b/unit_test/wheel_print_test.py
index 35e1ce57..3fcadeb2 100644
--- a/unit_test/wheel_print_test.py
+++ b/unit_test/wheel_print_test.py
@@ -6,15 +6,15 @@
 def test_printout_wheels(tmp_path, capsys):
     tmp_path.joinpath("example.0").touch()
     with print_new_wheels("TEST_MSG: {n}", tmp_path):
-        tmp_path.joinpath("example.1").touch()
-        tmp_path.joinpath("example.2").touch()
+        tmp_path.joinpath("example.1").write_bytes(b"0" * 1023)
+        tmp_path.joinpath("example.2").write_bytes(b"0" * 1025)
 
     captured = capsys.readouterr()
     assert captured.err == ""
 
     assert "example.0" not in captured.out
-    assert "example.1\n" in captured.out
-    assert "example.2\n" in captured.out
+    assert "example.1   1 kB\n" in captured.out
+    assert "example.2   2 kB\n" in captured.out
     assert "TEST_MSG:" in captured.out
     assert "TEST_MSG: 2\n" in captured.out