From 700b15f05fcf487daad777a17d927e860def89e5 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 25 Mar 2025 08:49:58 +0800 Subject: [PATCH 1/6] Add a test of .pth handling. Co-authored-by: Tim Rid <6593626+timrid@users.noreply.github.com> --- .gitignore | 1 + pth-tester/pth_tester.py | 8 +++++++ pth-tester/pyproject.toml | 8 +++++++ pth-tester/setup.py | 44 +++++++++++++++++++++++++++++++++++++++ pyproject.toml | 6 ++++++ tests/test_common.py | 12 +++++++++++ 6 files changed, 79 insertions(+) create mode 100644 pth-tester/pth_tester.py create mode 100644 pth-tester/pyproject.toml create mode 100644 pth-tester/setup.py diff --git a/.gitignore b/.gitignore index 2becc04..d882066 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ __pycache__/ .idea *.dist-info/ +*.egg-info/ build/ logs/ .DS_Store diff --git a/pth-tester/pth_tester.py b/pth-tester/pth_tester.py new file mode 100644 index 0000000..9cbb6f9 --- /dev/null +++ b/pth-tester/pth_tester.py @@ -0,0 +1,8 @@ +initialized = False + + +# The pth_tester module should be initalized by processing the `.pth` file +# created on installation. +def init(): + global initialized + initialized = True diff --git a/pth-tester/pyproject.toml b/pth-tester/pyproject.toml new file mode 100644 index 0000000..efc4401 --- /dev/null +++ b/pth-tester/pyproject.toml @@ -0,0 +1,8 @@ +[build-system] +requires = ["setuptools==78.0.2", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "x-pth-tester" +version = "1.0.0" +classifiers = ["Private :: Do Not Upload"] diff --git a/pth-tester/setup.py b/pth-tester/setup.py new file mode 100644 index 0000000..b1ace4a --- /dev/null +++ b/pth-tester/setup.py @@ -0,0 +1,44 @@ +import os + +import setuptools +from setuptools.command.install import install + + +# Copied from setuptools: +# (https://github.com/pypa/setuptools/blob/7c859e017368360ba66c8cc591279d8964c031bc/setup.py#L40C6-L82) +class install_with_pth(install): + """ + Custom install command to install a .pth file. + + This hack is necessary because there's no standard way to install behavior + on startup (and it's debatable if there should be one). This hack (ab)uses + the `extra_path` behavior in Setuptools to install a `.pth` file with + implicit behavior on startup. + + The original source strongly recommends against using this behavior. + """ + + _pth_name = "_pth_tester" + _pth_contents = "import pth_tester; pth_tester.init()" + + def initialize_options(self): + install.initialize_options(self) + self.extra_path = self._pth_name, self._pth_contents + + def finalize_options(self): + install.finalize_options(self) + self._restore_install_lib() + + def _restore_install_lib(self): + """ + Undo secondary effect of `extra_path` adding to `install_lib` + """ + suffix = os.path.relpath(self.install_lib, self.install_libbase) + + if suffix.strip() == self._pth_contents.strip(): + self.install_lib = self.install_libbase + + +setuptools.setup( + cmdclass={"install": install_with_pth}, +) diff --git a/pyproject.toml b/pyproject.toml index 8fda42a..b0d862c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,6 +19,12 @@ sources = ["src/testbed"] test_sources = ["tests"] requires = [ + # Android can't process .pth files (see chaquo/chaquopy#1338). This also + # means we can only test Linux on Python3.13+, because Android reports as + # "Linux" on Python3.12 and earlier. + """./pth-tester; \ + (platform_system != 'Linux' and platform_system != 'Android') \ + or (platform_system == 'Linux' and python_version >= '3.13')""", # Cryptography provides an ABI3 wheel for all desktop platforms, but requires cffi which doesn't. """cryptography; \ (platform_system != 'iOS' and platform_system != 'Android' and python_version < '3.14') \ diff --git a/tests/test_common.py b/tests/test_common.py index a987c7a..1ff0aa1 100644 --- a/tests/test_common.py +++ b/tests/test_common.py @@ -7,6 +7,8 @@ import pytest +from .test_thirdparty import xfail_if_not_installed + def test_bootstrap_modules(): "All the bootstrap modules are importable" @@ -444,3 +446,13 @@ def test_zoneinfo(): dt = datetime(2022, 5, 4, 13, 40, 42, tzinfo=ZoneInfo("Australia/Perth")) assert str(dt) == "2022-05-04 13:40:42+08:00" + + +@xfail_if_not_installed("x-pth-tester") +def test_pth_handling(): + ".pth files installed by a package are processed" + import pth_tester + + # The pth_tester module should be "initialized" as a result of + # processing the .pth file created when the package is installed. + assert pth_tester.initialized From 221fe76e93a9f047eb733a5b33b886436a15dd46 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Tue, 25 Mar 2025 10:18:03 +0800 Subject: [PATCH 2/6] Pre-compile the wheel so that it can be found on platforms that use requirements.txt --- pth-tester/README.md | 18 ++++++++++++++++++ pyproject.toml | 2 +- wheels/x_pth_tester-1.0.0-py3-none-any.whl | Bin 0 -> 1472 bytes 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 pth-tester/README.md create mode 100644 wheels/x_pth_tester-1.0.0-py3-none-any.whl diff --git a/pth-tester/README.md b/pth-tester/README.md new file mode 100644 index 0000000..6bf1270 --- /dev/null +++ b/pth-tester/README.md @@ -0,0 +1,18 @@ +# pth-tester + +This is a package that tests whether `.pth` files are correctly process on +import. It is not designed to be published; it is only useful in the context of +the testbed app. + +When installed, it includes `.pth` file that invokes the `pth_tester.init()` method. +This sets the `initialized` attribute of the module to `True`. In this way, it is +possible to tell if `.pth` handling has occurred on app startup. + +This project has been compiled into a wheel, stored in the `wheels` directory +of the top-level directory. The wheel can be rebuilt using: + + $ pip install build + $ python -m build --wheel --outdir ../wheels + +If you make any modifications to the code for this project, you will need to +rebuild the wheel. diff --git a/pyproject.toml b/pyproject.toml index b0d862c..0c82281 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ requires = [ # Android can't process .pth files (see chaquo/chaquopy#1338). This also # means we can only test Linux on Python3.13+, because Android reports as # "Linux" on Python3.12 and earlier. - """./pth-tester; \ + """x-pth-tester; \ (platform_system != 'Linux' and platform_system != 'Android') \ or (platform_system == 'Linux' and python_version >= '3.13')""", # Cryptography provides an ABI3 wheel for all desktop platforms, but requires cffi which doesn't. diff --git a/wheels/x_pth_tester-1.0.0-py3-none-any.whl b/wheels/x_pth_tester-1.0.0-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..5aaaa2a5dfaf5831e691500335c309ae4c9d3255 GIT binary patch literal 1472 zcmWIWW@Zs#U|`^2uoSF}>Oc8wog9#-3dHgFG6cYll4Yxmx_VdVL=(`6Z9vQm#K3LM@JjDdEqA-1G}l;#rKo(yHK(QqtHfu{SGM;_Uw=yMn@HdYAYq zJ~pdbx4oKS|6Em9xr>&WN4}UB=E)xBM>rPb?<~uC&7nX?#sV?O?-fWM*EQ5L0K$~a z;u77=ytI6MU)K;v7sn7s+mnV|4F)_62W;1$V2@g5k$N?>@S^R7j@B$M=_Ai~SH3=* zBy;n|+o0%6vp0Na)~Vrq__}NExg|40e(e8zkX^!f`?NllsRe6aCJL(@@+=HIyf)=2 zhdipgY6bh4Vu0?624W>b?h5yCb@gFAXvlRK6tc0)FWlfU^0eCWgK25BrsB-lA6poe zStI3&e(&4C)^Upk# z-7+Qr+2cK3vz)r-xzwY&t>40X1t?;+e-2a;&W2VQgif5DoV8B>E{s>QmO$( z#Ik2wVpJFz7>)z6G9g2QT%G-cTw2fimODEzv_7n@JMB^H8d0L?#wm2zQ!4FohL501 zbLPbO`#E#Urq0{FbMg|Bv0HF~aj z#Lat$i(}OeiOKP8!qfgFN8U7Ex2Nu2T-h_n_Fsf{srsP z_Btk8yA(KIPF?1g>EXy=@^WYGCg01m6JMN~^}O0Grmi$nT9E!K0l8*G)5 zPz=nwE5Y%f#p&_~XO07p*aN&7nM9az=W}34gTazU5QUWZLAf4TGq#)!(aONEq|qD5 zM9JsKTCwFbgw`KGCQ-Q#-AME-f-rIdFb&}~5}aYs%|lPt2=jE92$+YG$kEM3PuVbY cw=^~qG8dB01H4(;K$_Tq@CeXr&zM0x0MT{#ApigX literal 0 HcmV?d00001 From c789a0e39189be2631d33b61ba0f1d7fcffee0b8 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Tue, 25 Mar 2025 15:20:30 +0000 Subject: [PATCH 3/6] Fix typos --- pth-tester/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pth-tester/README.md b/pth-tester/README.md index 6bf1270..15beb0a 100644 --- a/pth-tester/README.md +++ b/pth-tester/README.md @@ -1,10 +1,10 @@ # pth-tester -This is a package that tests whether `.pth` files are correctly process on +This is a package that tests whether `.pth` files are correctly processed on import. It is not designed to be published; it is only useful in the context of the testbed app. -When installed, it includes `.pth` file that invokes the `pth_tester.init()` method. +When installed, it includes a `.pth` file that invokes the `pth_tester.init()` method. This sets the `initialized` attribute of the module to `True`. In this way, it is possible to tell if `.pth` handling has occurred on app startup. From d18d4c0a2c4086c1c5714a9cdae33fd41ecd3fc8 Mon Sep 17 00:00:00 2001 From: Malcolm Smith Date: Tue, 25 Mar 2025 15:21:31 +0000 Subject: [PATCH 4/6] Install x-pth-tester unconditionally --- pyproject.toml | 8 ++------ tests/test_common.py | 3 --- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0c82281..47dcfcf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,12 +19,8 @@ sources = ["src/testbed"] test_sources = ["tests"] requires = [ - # Android can't process .pth files (see chaquo/chaquopy#1338). This also - # means we can only test Linux on Python3.13+, because Android reports as - # "Linux" on Python3.12 and earlier. - """x-pth-tester; \ - (platform_system != 'Linux' and platform_system != 'Android') \ - or (platform_system == 'Linux' and python_version >= '3.13')""", + "x-pth-tester", + # Cryptography provides an ABI3 wheel for all desktop platforms, but requires cffi which doesn't. """cryptography; \ (platform_system != 'iOS' and platform_system != 'Android' and python_version < '3.14') \ diff --git a/tests/test_common.py b/tests/test_common.py index 1ff0aa1..b8e7873 100644 --- a/tests/test_common.py +++ b/tests/test_common.py @@ -7,8 +7,6 @@ import pytest -from .test_thirdparty import xfail_if_not_installed - def test_bootstrap_modules(): "All the bootstrap modules are importable" @@ -448,7 +446,6 @@ def test_zoneinfo(): assert str(dt) == "2022-05-04 13:40:42+08:00" -@xfail_if_not_installed("x-pth-tester") def test_pth_handling(): ".pth files installed by a package are processed" import pth_tester From a3d96cfe1651d00c76b303473b30ece1b0edd0bd Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 26 Mar 2025 07:31:01 +0800 Subject: [PATCH 5/6] Add a test of stdlib availability during .pth handling. --- pth-tester/pth_tester.py | 13 +++++++++++++ pth-tester/pyproject.toml | 2 +- tests/test_common.py | 17 +++++++++++++++++ wheels/x_pth_tester-1.0.0-py3-none-any.whl | Bin 1472 -> 0 bytes wheels/x_pth_tester-2025.3.26-py3-none-any.whl | Bin 0 -> 1679 bytes 5 files changed, 31 insertions(+), 1 deletion(-) delete mode 100644 wheels/x_pth_tester-1.0.0-py3-none-any.whl create mode 100644 wheels/x_pth_tester-2025.3.26-py3-none-any.whl diff --git a/pth-tester/pth_tester.py b/pth-tester/pth_tester.py index 9cbb6f9..a052e9a 100644 --- a/pth-tester/pth_tester.py +++ b/pth-tester/pth_tester.py @@ -1,8 +1,21 @@ initialized = False +has_socket = False # The pth_tester module should be initalized by processing the `.pth` file # created on installation. def init(): global initialized + global has_socket + initialized = True + + # At the time that the module is initialized, it *should* have access + # to all of the standard library. This might not be true, depending on + # the initialization order of the site module and sys.path. + try: + import socket # NOQA: F401 + + has_socket = True + except ImportError: + pass diff --git a/pth-tester/pyproject.toml b/pth-tester/pyproject.toml index efc4401..66559c2 100644 --- a/pth-tester/pyproject.toml +++ b/pth-tester/pyproject.toml @@ -4,5 +4,5 @@ build-backend = "setuptools.build_meta" [project] name = "x-pth-tester" -version = "1.0.0" +version = "2025.3.26" classifiers = ["Private :: Do Not Upload"] diff --git a/tests/test_common.py b/tests/test_common.py index b8e7873..ffe431c 100644 --- a/tests/test_common.py +++ b/tests/test_common.py @@ -453,3 +453,20 @@ def test_pth_handling(): # The pth_tester module should be "initialized" as a result of # processing the .pth file created when the package is installed. assert pth_tester.initialized + + # When the .pth file is processed, the full standard library should be + # available. Check if the initialization process could import socket. + if sys.platform == "android" or hasattr(sys, "getandroidapilevel"): + # Android is known to have an issue with .pth/sys.path ordering that + # causes this test to fail. For now, accept this as an XFAIL; if it + # passes, fail as an indicator that the bug has been resolved, and we + # can simplify the test. + try: + assert pth_tester.has_socket + pytest.fail("Android .pth handling bug has been resolved.") + except AssertionError: + pytest.xfail( + "On Android, .pth files are processed before sys.path is finalized." + ) + else: + assert pth_tester.has_socket diff --git a/wheels/x_pth_tester-1.0.0-py3-none-any.whl b/wheels/x_pth_tester-1.0.0-py3-none-any.whl deleted file mode 100644 index 5aaaa2a5dfaf5831e691500335c309ae4c9d3255..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1472 zcmWIWW@Zs#U|`^2uoSF}>Oc8wog9#-3dHgFG6cYll4Yxmx_VdVL=(`6Z9vQm#K3LM@JjDdEqA-1G}l;#rKo(yHK(QqtHfu{SGM;_Uw=yMn@HdYAYq zJ~pdbx4oKS|6Em9xr>&WN4}UB=E)xBM>rPb?<~uC&7nX?#sV?O?-fWM*EQ5L0K$~a z;u77=ytI6MU)K;v7sn7s+mnV|4F)_62W;1$V2@g5k$N?>@S^R7j@B$M=_Ai~SH3=* zBy;n|+o0%6vp0Na)~Vrq__}NExg|40e(e8zkX^!f`?NllsRe6aCJL(@@+=HIyf)=2 zhdipgY6bh4Vu0?624W>b?h5yCb@gFAXvlRK6tc0)FWlfU^0eCWgK25BrsB-lA6poe zStI3&e(&4C)^Upk# z-7+Qr+2cK3vz)r-xzwY&t>40X1t?;+e-2a;&W2VQgif5DoV8B>E{s>QmO$( z#Ik2wVpJFz7>)z6G9g2QT%G-cTw2fimODEzv_7n@JMB^H8d0L?#wm2zQ!4FohL501 zbLPbO`#E#Urq0{FbMg|Bv0HF~aj z#Lat$i(}OeiOKP8!qfgFN8U7Ex2Nu2T-h_n_Fsf{srsP z_Btk8yA(KIPF?1g>EXy=@^WYGCg01m6JMN~^}O0Grmi$nT9E!K0l8*G)5 zPz=nwE5Y%f#p&_~XO07p*aN&7nM9az=W}34gTazU5QUWZLAf4TGq#)!(aONEq|qD5 zM9JsKTCwFbgw`KGCQ-Q#-AME-f-rIdFb&}~5}aYs%|lPt2=jE92$+YG$kEM3PuVbY cw=^~qG8dB01H4(;K$_Tq@CeXr&zM0x0MT{#ApigX diff --git a/wheels/x_pth_tester-2025.3.26-py3-none-any.whl b/wheels/x_pth_tester-2025.3.26-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..6f95bdc1f9f2856d09c31b9ea35bce72c3095869 GIT binary patch literal 1679 zcmWIWW@Zs#U|`^2h}l^g)qnEWIyoRu6^QwPIKH4HBfccHxFoem56C=yM)!oiR-lH) z$qs!_T|H0TO>RlAQ=g?y%g88VWC(y8WdJnFxx+Y1n~{M*nTdgc7ibi+NtK}!&t@Gq z5NLT{)zunvOTdTQmbL1{!p68LuASB?1*zBBTi)#C=gVRVy5h5n{e_VE4CAB1 z9ei?HvbF~%GHI>y%y4~QcxC09;DcO+YkCeRKeVw~$NQq3E9Q2`IIM? ztvv1-f55Gadtn%V{__2nj8AVL6rFIO$ZP8rUVDyhl9SJJtb%c*OgOFgRl z`YpUyfKnqj5bF`+zLNZc_?*@ zI{OE?^q$>a)?~oY@bGRrr-o%{u7553N$vIQuTW9@^zounkCf;YYx!-tbi^idt-02{ z|Ad9~WI40thg9}eUl*+94~l-p^U+}ChMLv8+l!9nY*ww#mhx3N__gWiejatMMMu_G z1U}omX?aYg?fp2Jnt+K_0nZPno5;;A<8Qu_I+yL@y1gBWX1Q||esw{@^HtZA z+Z#W%$V*7vHts76*ccmaJ@vaDx3jrS$!jHf#Z^68?WL#ob(MV(cKoamFzx^2FV~Kk zR!*3;yyu*7){T1+xu2$)1X`Bn&YS7HW9o!0DlrpgX Date: Wed, 26 Mar 2025 09:08:07 +0800 Subject: [PATCH 6/6] Simplify logic for android special case test. --- tests/test_common.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_common.py b/tests/test_common.py index ffe431c..ee6040a 100644 --- a/tests/test_common.py +++ b/tests/test_common.py @@ -461,10 +461,9 @@ def test_pth_handling(): # causes this test to fail. For now, accept this as an XFAIL; if it # passes, fail as an indicator that the bug has been resolved, and we # can simplify the test. - try: - assert pth_tester.has_socket + if pth_tester.has_socket: pytest.fail("Android .pth handling bug has been resolved.") - except AssertionError: + else: pytest.xfail( "On Android, .pth files are processed before sys.path is finalized." )