From 577a8d333530aa368cc4ef682ae757b59f94618b Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sat, 25 Oct 2025 01:28:29 +0200 Subject: [PATCH 01/22] taking a stab... --- dev.py | 54 +++- pyperformance/_venv.py | 22 +- pyperformance/requirements/requirements.txt | 8 +- pyperformance/tests/__init__.py | 20 +- pyproject.toml | 12 +- requirements.in | 4 +- uv.lock | 300 ++++++++++++++++++++ 7 files changed, 380 insertions(+), 40 deletions(-) create mode 100644 uv.lock diff --git a/dev.py b/dev.py index 3a6752ac..fffe568f 100644 --- a/dev.py +++ b/dev.py @@ -1,6 +1,8 @@ # A script for running pyperformance out of the repo in dev-mode. import os.path +import shutil +import subprocess import sys REPO_ROOT = os.path.dirname(os.path.abspath(__file__)) @@ -29,8 +31,6 @@ def ensure_venv_ready(venvroot=None, kind="dev", venvsdir=VENVS): readyfile = os.path.join(sys.prefix, "READY") isready = os.path.exists(readyfile) else: - import venv - if not venvroot: venvroot = resolve_venv_root(kind, venvsdir) # Make sure the venv exists. @@ -38,11 +38,28 @@ def ensure_venv_ready(venvroot=None, kind="dev", venvsdir=VENVS): isready = os.path.exists(readyfile) if not isready: relroot = os.path.relpath(venvroot) - if not os.path.exists(venvroot): - print(f"creating venv at {relroot}...") + uv = shutil.which("uv") + if not uv: + sys.exit( + "ERROR: uv executable not found. " + "Install uv from https://astral.sh/uv and retry." + ) + if os.path.exists(venvroot): + print(f"uv env {relroot} not ready, re-creating...") + shutil.rmtree(venvroot) else: - print(f"venv {relroot} not ready, re-creating...") - venv.create(venvroot, with_pip=True, clear=True) + print(f"creating uv env at {relroot}...") + result = subprocess.run( + [ + uv, + "venv", + "--python", + sys.executable, + venvroot, + ] + ) + if result.returncode != 0: + sys.exit("ERROR: uv venv creation failed") else: assert os.path.exists(os.path.join(venvroot, "pyvenv.cfg")) # Return the venv's Python executable. @@ -52,18 +69,31 @@ def ensure_venv_ready(venvroot=None, kind="dev", venvsdir=VENVS): # Now make sure the venv has pyperformance installed. if not isready: - import subprocess - relroot = os.path.relpath(venvroot) - print(f"venv {relroot} not ready, installing dependencies...") + print(f"uv env {relroot} not ready, installing dependencies...") + uv = shutil.which("uv") + if not uv: + sys.exit( + "ERROR: uv executable not found. " + "Install uv from https://astral.sh/uv and retry." + ) proc = subprocess.run( - [python, "-m", "pip", "install", "--upgrade", "--editable", REPO_ROOT], + [ + uv, + "pip", + "install", + "--python", + python, + "--upgrade", + "--editable", + f"{REPO_ROOT}[dev]", + ], ) if proc.returncode != 0: - sys.exit("ERROR: install failed") + sys.exit("ERROR: uv pip install failed") with open(readyfile, "w"): pass - print("...venv {relroot} ready!") + print(f"...uv env {relroot} ready!") return venvroot, python diff --git a/pyperformance/_venv.py b/pyperformance/_venv.py index 73c0b989..b6bf4c28 100644 --- a/pyperformance/_venv.py +++ b/pyperformance/_venv.py @@ -2,6 +2,7 @@ import os import os.path +import shutil import sys import types @@ -111,11 +112,24 @@ def create_venv( ): """Create a new venv at the given root, optionally installing pip.""" already_existed = os.path.exists(root) - if withpip: - args = ["-m", "venv", root] + uv = shutil.which("uv") + if not uv: + print("ERROR: uv executable not found. Install uv from https://astral.sh/uv.") + raise VenvCreationFailedError(root, 127, already_existed) + + if isinstance(python, str) or python is None: + target_python = python or sys.executable else: - args = ["-m", "venv", "--without-pip", root] - ec, _, _ = _utils.run_python(*args, python=python, env=env) + try: + target_python = python.sys.executable + except AttributeError as exc: + raise TypeError(f"expected python str, got {python!r}") from exc + + argv = [uv, "venv"] + if target_python: + argv.extend(["--python", target_python]) + argv.append(root) + ec, _, _ = _utils.run_cmd(argv, env=env) if ec != 0: if cleanonfail and not already_existed: _utils.safe_rmtree(root) diff --git a/pyperformance/requirements/requirements.txt b/pyperformance/requirements/requirements.txt index 644a4965..0770802c 100644 --- a/pyperformance/requirements/requirements.txt +++ b/pyperformance/requirements/requirements.txt @@ -1,9 +1,5 @@ -# -# This file is autogenerated by pip-compile with Python 3.11 -# by the following command: -# -# pip-compile --output-file=pyperformance/requirements/requirements.txt requirements.in -# +# This file was autogenerated by uv via the following command: +# uv pip compile -o requirements.txt requirements.in packaging==23.1 # via -r requirements.in psutil==5.9.5 diff --git a/pyperformance/tests/__init__.py b/pyperformance/tests/__init__.py index 2aa40f21..9f0a5858 100644 --- a/pyperformance/tests/__init__.py +++ b/pyperformance/tests/__init__.py @@ -75,16 +75,16 @@ def cleanup(): def cleanup(): return None - run_cmd( - python or sys.executable, - "-m", - "venv", - root, - capture=not verbose, - onfail="raise", - verbose=verbose, - ) - return root, _resolve_venv_python(root), cleanup + uv = shutil.which("uv") + if not uv: + raise RuntimeError("uv executable is required to provision test environments") + argv = [uv, "venv"] + if python: + argv.extend(["--python", python]) + argv.append(root) + run_cmd(*argv, capture=not verbose, onfail="raise", verbose=verbose) + venv_root = os.path.realpath(root) + return venv_root, _resolve_venv_python(venv_root), cleanup class CleanupFile: diff --git a/pyproject.toml b/pyproject.toml index 94e9ce07..aa8966b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,8 +1,7 @@ # Update dependencies: # -# - python3 -m pip install --user --upgrade pip-tools build # - git clean -fdx # remove all untracked files! -# - (pip-compile --upgrade -o requirements.txt requirements.in) +# - uv pip compile --upgrade -o requirements.txt requirements.in # # Prepare a release: # @@ -28,8 +27,8 @@ # - git tag VERSION # - git push --tags # - Remove untracked files/dirs: git clean -fdx -# - python -m build -# - twine upload dist/* +# - uv run python -m build +# - uvx twine upload dist/* # # After the release: # @@ -65,8 +64,9 @@ classifiers = [ ] dynamic = [ "version" ] dependencies = [ - "packaging", - "pyperf", + "packaging==23.1", + "psutil==5.9.5", + "pyperf==2.9", "tomli; python_version<'3.11'", ] diff --git a/requirements.in b/requirements.in index 55f432ef..d682f417 100644 --- a/requirements.in +++ b/requirements.in @@ -12,7 +12,7 @@ pyperf # for benchmark metadata: -packaging +packaging==23.1 tomli; python_version < '3.11' @@ -22,4 +22,4 @@ tomli; python_version < '3.11' # The list of optional dependencies is hardcoded in pyperformance/venv.py # XXX Do we still need this? -psutil +psutil==5.9.5 diff --git a/uv.lock b/uv.lock new file mode 100644 index 00000000..f98dddff --- /dev/null +++ b/uv.lock @@ -0,0 +1,300 @@ +version = 1 +revision = 3 +requires-python = ">=3.10" + +[[package]] +name = "cachetools" +version = "6.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/7e/b975b5814bd36faf009faebe22c1072a1fa1168db34d285ef0ba071ad78c/cachetools-6.2.1.tar.gz", hash = "sha256:3f391e4bd8f8bf0931169baf7456cc822705f4e2a31f840d218f445b9a854201", size = 31325, upload-time = "2025-10-12T14:55:30.139Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/c5/1e741d26306c42e2bf6ab740b2202872727e0f606033c9dd713f8b93f5a8/cachetools-6.2.1-py3-none-any.whl", hash = "sha256:09868944b6dde876dfd44e1d47e18484541eaf12f26f29b7af91b26cc892d701", size = 11280, upload-time = "2025-10-12T14:55:28.382Z" }, +] + +[[package]] +name = "chardet" +version = "5.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/f7b6ab21ec75897ed80c17d79b15951a719226b9fababf1e40ea74d69079/chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7", size = 2069618, upload-time = "2023-08-01T19:23:02.662Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/6f/f5fbc992a329ee4e0f288c1fe0e2ad9485ed064cac731ed2fe47dcc38cbf/chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970", size = 199385, upload-time = "2023-08-01T19:23:00.661Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "distlib" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, +] + +[[package]] +name = "filelock" +version = "3.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/58/46/0028a82567109b5ef6e4d2a1f04a583fb513e6cf9527fcdd09afd817deeb/filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4", size = 18922, upload-time = "2025-10-08T18:03:50.056Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054, upload-time = "2025-10-08T18:03:48.35Z" }, +] + +[[package]] +name = "mypy" +version = "1.18.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mypy-extensions" }, + { name = "pathspec" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/77/8f0d0001ffad290cef2f7f216f96c814866248a0b92a722365ed54648e7e/mypy-1.18.2.tar.gz", hash = "sha256:06a398102a5f203d7477b2923dda3634c36727fa5c237d8f859ef90c42a9924b", size = 3448846, upload-time = "2025-09-19T00:11:10.519Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/03/6f/657961a0743cff32e6c0611b63ff1c1970a0b482ace35b069203bf705187/mypy-1.18.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c1eab0cf6294dafe397c261a75f96dc2c31bffe3b944faa24db5def4e2b0f77c", size = 12807973, upload-time = "2025-09-19T00:10:35.282Z" }, + { url = "https://files.pythonhosted.org/packages/10/e9/420822d4f661f13ca8900f5fa239b40ee3be8b62b32f3357df9a3045a08b/mypy-1.18.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a780ca61fc239e4865968ebc5240bb3bf610ef59ac398de9a7421b54e4a207e", size = 11896527, upload-time = "2025-09-19T00:10:55.791Z" }, + { url = "https://files.pythonhosted.org/packages/aa/73/a05b2bbaa7005f4642fcfe40fb73f2b4fb6bb44229bd585b5878e9a87ef8/mypy-1.18.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:448acd386266989ef11662ce3c8011fd2a7b632e0ec7d61a98edd8e27472225b", size = 12507004, upload-time = "2025-09-19T00:11:05.411Z" }, + { url = "https://files.pythonhosted.org/packages/4f/01/f6e4b9f0d031c11ccbd6f17da26564f3a0f3c4155af344006434b0a05a9d/mypy-1.18.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f9e171c465ad3901dc652643ee4bffa8e9fef4d7d0eece23b428908c77a76a66", size = 13245947, upload-time = "2025-09-19T00:10:46.923Z" }, + { url = "https://files.pythonhosted.org/packages/d7/97/19727e7499bfa1ae0773d06afd30ac66a58ed7437d940c70548634b24185/mypy-1.18.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:592ec214750bc00741af1f80cbf96b5013d81486b7bb24cb052382c19e40b428", size = 13499217, upload-time = "2025-09-19T00:09:39.472Z" }, + { url = "https://files.pythonhosted.org/packages/9f/4f/90dc8c15c1441bf31cf0f9918bb077e452618708199e530f4cbd5cede6ff/mypy-1.18.2-cp310-cp310-win_amd64.whl", hash = "sha256:7fb95f97199ea11769ebe3638c29b550b5221e997c63b14ef93d2e971606ebed", size = 9766753, upload-time = "2025-09-19T00:10:49.161Z" }, + { url = "https://files.pythonhosted.org/packages/88/87/cafd3ae563f88f94eec33f35ff722d043e09832ea8530ef149ec1efbaf08/mypy-1.18.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:807d9315ab9d464125aa9fcf6d84fde6e1dc67da0b6f80e7405506b8ac72bc7f", size = 12731198, upload-time = "2025-09-19T00:09:44.857Z" }, + { url = "https://files.pythonhosted.org/packages/0f/e0/1e96c3d4266a06d4b0197ace5356d67d937d8358e2ee3ffac71faa843724/mypy-1.18.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:776bb00de1778caf4db739c6e83919c1d85a448f71979b6a0edd774ea8399341", size = 11817879, upload-time = "2025-09-19T00:09:47.131Z" }, + { url = "https://files.pythonhosted.org/packages/72/ef/0c9ba89eb03453e76bdac5a78b08260a848c7bfc5d6603634774d9cd9525/mypy-1.18.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1379451880512ffce14505493bd9fe469e0697543717298242574882cf8cdb8d", size = 12427292, upload-time = "2025-09-19T00:10:22.472Z" }, + { url = "https://files.pythonhosted.org/packages/1a/52/ec4a061dd599eb8179d5411d99775bec2a20542505988f40fc2fee781068/mypy-1.18.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1331eb7fd110d60c24999893320967594ff84c38ac6d19e0a76c5fd809a84c86", size = 13163750, upload-time = "2025-09-19T00:09:51.472Z" }, + { url = "https://files.pythonhosted.org/packages/c4/5f/2cf2ceb3b36372d51568f2208c021870fe7834cf3186b653ac6446511839/mypy-1.18.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ca30b50a51e7ba93b00422e486cbb124f1c56a535e20eff7b2d6ab72b3b2e37", size = 13351827, upload-time = "2025-09-19T00:09:58.311Z" }, + { url = "https://files.pythonhosted.org/packages/c8/7d/2697b930179e7277529eaaec1513f8de622818696857f689e4a5432e5e27/mypy-1.18.2-cp311-cp311-win_amd64.whl", hash = "sha256:664dc726e67fa54e14536f6e1224bcfce1d9e5ac02426d2326e2bb4e081d1ce8", size = 9757983, upload-time = "2025-09-19T00:10:09.071Z" }, + { url = "https://files.pythonhosted.org/packages/07/06/dfdd2bc60c66611dd8335f463818514733bc763e4760dee289dcc33df709/mypy-1.18.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:33eca32dd124b29400c31d7cf784e795b050ace0e1f91b8dc035672725617e34", size = 12908273, upload-time = "2025-09-19T00:10:58.321Z" }, + { url = "https://files.pythonhosted.org/packages/81/14/6a9de6d13a122d5608e1a04130724caf9170333ac5a924e10f670687d3eb/mypy-1.18.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a3c47adf30d65e89b2dcd2fa32f3aeb5e94ca970d2c15fcb25e297871c8e4764", size = 11920910, upload-time = "2025-09-19T00:10:20.043Z" }, + { url = "https://files.pythonhosted.org/packages/5f/a9/b29de53e42f18e8cc547e38daa9dfa132ffdc64f7250e353f5c8cdd44bee/mypy-1.18.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d6c838e831a062f5f29d11c9057c6009f60cb294fea33a98422688181fe2893", size = 12465585, upload-time = "2025-09-19T00:10:33.005Z" }, + { url = "https://files.pythonhosted.org/packages/77/ae/6c3d2c7c61ff21f2bee938c917616c92ebf852f015fb55917fd6e2811db2/mypy-1.18.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01199871b6110a2ce984bde85acd481232d17413868c9807e95c1b0739a58914", size = 13348562, upload-time = "2025-09-19T00:10:11.51Z" }, + { url = "https://files.pythonhosted.org/packages/4d/31/aec68ab3b4aebdf8f36d191b0685d99faa899ab990753ca0fee60fb99511/mypy-1.18.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a2afc0fa0b0e91b4599ddfe0f91e2c26c2b5a5ab263737e998d6817874c5f7c8", size = 13533296, upload-time = "2025-09-19T00:10:06.568Z" }, + { url = "https://files.pythonhosted.org/packages/9f/83/abcb3ad9478fca3ebeb6a5358bb0b22c95ea42b43b7789c7fb1297ca44f4/mypy-1.18.2-cp312-cp312-win_amd64.whl", hash = "sha256:d8068d0afe682c7c4897c0f7ce84ea77f6de953262b12d07038f4d296d547074", size = 9828828, upload-time = "2025-09-19T00:10:28.203Z" }, + { url = "https://files.pythonhosted.org/packages/5f/04/7f462e6fbba87a72bc8097b93f6842499c428a6ff0c81dd46948d175afe8/mypy-1.18.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:07b8b0f580ca6d289e69209ec9d3911b4a26e5abfde32228a288eb79df129fcc", size = 12898728, upload-time = "2025-09-19T00:10:01.33Z" }, + { url = "https://files.pythonhosted.org/packages/99/5b/61ed4efb64f1871b41fd0b82d29a64640f3516078f6c7905b68ab1ad8b13/mypy-1.18.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ed4482847168439651d3feee5833ccedbf6657e964572706a2adb1f7fa4dfe2e", size = 11910758, upload-time = "2025-09-19T00:10:42.607Z" }, + { url = "https://files.pythonhosted.org/packages/3c/46/d297d4b683cc89a6e4108c4250a6a6b717f5fa96e1a30a7944a6da44da35/mypy-1.18.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3ad2afadd1e9fea5cf99a45a822346971ede8685cc581ed9cd4d42eaf940986", size = 12475342, upload-time = "2025-09-19T00:11:00.371Z" }, + { url = "https://files.pythonhosted.org/packages/83/45/4798f4d00df13eae3bfdf726c9244bcb495ab5bd588c0eed93a2f2dd67f3/mypy-1.18.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a431a6f1ef14cf8c144c6b14793a23ec4eae3db28277c358136e79d7d062f62d", size = 13338709, upload-time = "2025-09-19T00:11:03.358Z" }, + { url = "https://files.pythonhosted.org/packages/d7/09/479f7358d9625172521a87a9271ddd2441e1dab16a09708f056e97007207/mypy-1.18.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7ab28cc197f1dd77a67e1c6f35cd1f8e8b73ed2217e4fc005f9e6a504e46e7ba", size = 13529806, upload-time = "2025-09-19T00:10:26.073Z" }, + { url = "https://files.pythonhosted.org/packages/71/cf/ac0f2c7e9d0ea3c75cd99dff7aec1c9df4a1376537cb90e4c882267ee7e9/mypy-1.18.2-cp313-cp313-win_amd64.whl", hash = "sha256:0e2785a84b34a72ba55fb5daf079a1003a34c05b22238da94fcae2bbe46f3544", size = 9833262, upload-time = "2025-09-19T00:10:40.035Z" }, + { url = "https://files.pythonhosted.org/packages/5a/0c/7d5300883da16f0063ae53996358758b2a2df2a09c72a5061fa79a1f5006/mypy-1.18.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:62f0e1e988ad41c2a110edde6c398383a889d95b36b3e60bcf155f5164c4fdce", size = 12893775, upload-time = "2025-09-19T00:10:03.814Z" }, + { url = "https://files.pythonhosted.org/packages/50/df/2cffbf25737bdb236f60c973edf62e3e7b4ee1c25b6878629e88e2cde967/mypy-1.18.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8795a039bab805ff0c1dfdb8cd3344642c2b99b8e439d057aba30850b8d3423d", size = 11936852, upload-time = "2025-09-19T00:10:51.631Z" }, + { url = "https://files.pythonhosted.org/packages/be/50/34059de13dd269227fb4a03be1faee6e2a4b04a2051c82ac0a0b5a773c9a/mypy-1.18.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6ca1e64b24a700ab5ce10133f7ccd956a04715463d30498e64ea8715236f9c9c", size = 12480242, upload-time = "2025-09-19T00:11:07.955Z" }, + { url = "https://files.pythonhosted.org/packages/5b/11/040983fad5132d85914c874a2836252bbc57832065548885b5bb5b0d4359/mypy-1.18.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d924eef3795cc89fecf6bedc6ed32b33ac13e8321344f6ddbf8ee89f706c05cb", size = 13326683, upload-time = "2025-09-19T00:09:55.572Z" }, + { url = "https://files.pythonhosted.org/packages/e9/ba/89b2901dd77414dd7a8c8729985832a5735053be15b744c18e4586e506ef/mypy-1.18.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20c02215a080e3a2be3aa50506c67242df1c151eaba0dcbc1e4e557922a26075", size = 13514749, upload-time = "2025-09-19T00:10:44.827Z" }, + { url = "https://files.pythonhosted.org/packages/25/bc/cc98767cffd6b2928ba680f3e5bc969c4152bf7c2d83f92f5a504b92b0eb/mypy-1.18.2-cp314-cp314-win_amd64.whl", hash = "sha256:749b5f83198f1ca64345603118a6f01a4e99ad4bf9d103ddc5a3200cc4614adf", size = 9982959, upload-time = "2025-09-19T00:10:37.344Z" }, + { url = "https://files.pythonhosted.org/packages/87/e3/be76d87158ebafa0309946c4a73831974d4d6ab4f4ef40c3b53a385a66fd/mypy-1.18.2-py3-none-any.whl", hash = "sha256:22a1748707dd62b58d2ae53562ffc4d7f8bcc727e8ac7cbc69c053ddc874d47e", size = 2352367, upload-time = "2025-09-19T00:10:15.489Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "packaging" +version = "23.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/6c/7c6658d258d7971c5eb0d9b69fa9265879ec9a9158031206d47800ae2213/packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f", size = 134240, upload-time = "2023-04-12T16:11:11.76Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/c3/57f0601a2d4fe15de7a553c00adbc901425661bf048f2a22dfc500caf121/packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61", size = 48905, upload-time = "2023-04-12T16:11:09.29Z" }, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/61/33/9611380c2bdb1225fdef633e2a9610622310fed35ab11dac9620972ee088/platformdirs-4.5.0.tar.gz", hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312", size = 21632, upload-time = "2025-10-08T17:44:48.791Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/cb/ac7874b3e5d58441674fb70742e6c374b28b0c7cb988d37d991cde47166c/platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3", size = 18651, upload-time = "2025-10-08T17:44:47.223Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "psutil" +version = "5.9.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/0f/96b7309212a926c1448366e9ce69b081ea79d63265bde33f11cc9cfc2c07/psutil-5.9.5.tar.gz", hash = "sha256:5410638e4df39c54d957fc51ce03048acd8e6d60abc0f5107af51e5fb566eb3c", size = 493489, upload-time = "2023-04-17T18:25:18.787Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/76/c0195c3443a725c24b3a479f57636dec89efe53d19d435d1752c5188f7de/psutil-5.9.5-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3c6f686f4225553615612f6d9bc21f1c0e305f75d7d8454f9b46e901778e7217", size = 245316, upload-time = "2023-04-17T18:24:52.864Z" }, + { url = "https://files.pythonhosted.org/packages/e5/2e/56db2b45508ad484b3f22888b3e1adaaf09b8766eaa058ed0e4486c1abae/psutil-5.9.5-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7a7dd9997128a0d928ed4fb2c2d57e5102bb6089027939f3b722f3a210f9a8da", size = 279398, upload-time = "2023-04-17T18:24:56.977Z" }, + { url = "https://files.pythonhosted.org/packages/af/4d/389441079ecef400e2551a3933224885a7bde6b8a4810091d628cdd75afe/psutil-5.9.5-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89518112647f1276b03ca97b65cc7f64ca587b1eb0278383017c2a0dcc26cbe4", size = 282082, upload-time = "2023-04-17T18:25:00.863Z" }, + { url = "https://files.pythonhosted.org/packages/fa/e0/e91277b1cabf5c3f2995c22314553f1be68b17444260101f365c5a5b6ba1/psutil-5.9.5-cp36-abi3-win32.whl", hash = "sha256:104a5cc0e31baa2bcf67900be36acde157756b9c44017b86b2c049f11957887d", size = 249834, upload-time = "2023-04-17T18:25:05.571Z" }, + { url = "https://files.pythonhosted.org/packages/86/f3/23e4e4e7ec7855d506ed928756b04735c246b14d9f778ed7ffaae18d8043/psutil-5.9.5-cp36-abi3-win_amd64.whl", hash = "sha256:b258c0c1c9d145a1d5ceffab1134441c4c5113b2417fafff7315a917a026c3c9", size = 255148, upload-time = "2023-04-17T18:25:09.779Z" }, + { url = "https://files.pythonhosted.org/packages/ed/98/2624954f83489ab13fde2b544baa337d5578c07eee304d320d9ba56e1b1f/psutil-5.9.5-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:c607bb3b57dc779d55e1554846352b4e358c10fff3abf3514a7a6601beebdb30", size = 246094, upload-time = "2023-04-17T18:25:14.584Z" }, +] + +[[package]] +name = "pyperf" +version = "2.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "psutil" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/89/f9/27bd50fb5475147ad04e288a076f29a893c1e94f10866fc3623a84ad75d2/pyperf-2.9.0.tar.gz", hash = "sha256:dbe0feef8ec1a465df191bba2576149762d15a8c9985c9fea93ab625d875c362", size = 227189, upload-time = "2025-03-04T13:45:44.335Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/19/29/ce5d2dcb095debb3bed0654950c45207a7d138e194abef0ded4b86de1dc1/pyperf-2.9.0-py3-none-any.whl", hash = "sha256:215673fb60f3fbbc6c7a90b609b0806d8466fded8dd502bf5731e2b92506076e", size = 144204, upload-time = "2025-03-04T13:45:40.491Z" }, +] + +[[package]] +name = "pyperformance" +source = { editable = "." } +dependencies = [ + { name = "packaging" }, + { name = "psutil" }, + { name = "pyperf" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] + +[package.optional-dependencies] +dev = [ + { name = "mypy" }, + { name = "tomli" }, + { name = "tox" }, +] + +[package.metadata] +requires-dist = [ + { name = "mypy", marker = "extra == 'dev'", specifier = "==1.18.2" }, + { name = "packaging", specifier = "==23.1" }, + { name = "psutil", specifier = "==5.9.5" }, + { name = "pyperf", specifier = "==2.9.0" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "tomli", marker = "extra == 'dev'" }, + { name = "tox", marker = "extra == 'dev'" }, +] +provides-extras = ["dev"] + +[[package]] +name = "pyproject-api" +version = "1.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1b/c6/b39e42e1d721dcb35be6a7d35157795398193ec0b78a034c110bd0ab3e48/pyproject_api-1.6.1.tar.gz", hash = "sha256:1817dc018adc0d1ff9ca1ed8c60e1623d5aaca40814b953af14a9cf9a5cae538", size = 22507, upload-time = "2023-08-29T18:05:50.47Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/b4/39eea50542e50e93876ebc09c4349a9c9eee9f6b9c9d30f88c7dc5433db8/pyproject_api-1.6.1-py3-none-any.whl", hash = "sha256:4c0116d60476b0786c88692cf4e325a9814965e2469c5998b830bba16b183675", size = 12904, upload-time = "2023-08-29T18:05:48.804Z" }, +] + +[[package]] +name = "tomli" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/52/ed/3f73f72945444548f33eba9a87fc7a6e969915e7b1acc8260b30e1f76a2f/tomli-2.3.0.tar.gz", hash = "sha256:64be704a875d2a59753d80ee8a533c3fe183e3f06807ff7dc2232938ccb01549", size = 17392, upload-time = "2025-10-08T22:01:47.119Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/2e/299f62b401438d5fe1624119c723f5d877acc86a4c2492da405626665f12/tomli-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:88bd15eb972f3664f5ed4b57c1634a97153b4bac4479dcb6a495f41921eb7f45", size = 153236, upload-time = "2025-10-08T22:01:00.137Z" }, + { url = "https://files.pythonhosted.org/packages/86/7f/d8fffe6a7aefdb61bced88fcb5e280cfd71e08939da5894161bd71bea022/tomli-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:883b1c0d6398a6a9d29b508c331fa56adbcdff647f6ace4dfca0f50e90dfd0ba", size = 148084, upload-time = "2025-10-08T22:01:01.63Z" }, + { url = "https://files.pythonhosted.org/packages/47/5c/24935fb6a2ee63e86d80e4d3b58b222dafaf438c416752c8b58537c8b89a/tomli-2.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d1381caf13ab9f300e30dd8feadb3de072aeb86f1d34a8569453ff32a7dea4bf", size = 234832, upload-time = "2025-10-08T22:01:02.543Z" }, + { url = "https://files.pythonhosted.org/packages/89/da/75dfd804fc11e6612846758a23f13271b76d577e299592b4371a4ca4cd09/tomli-2.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0e285d2649b78c0d9027570d4da3425bdb49830a6156121360b3f8511ea3441", size = 242052, upload-time = "2025-10-08T22:01:03.836Z" }, + { url = "https://files.pythonhosted.org/packages/70/8c/f48ac899f7b3ca7eb13af73bacbc93aec37f9c954df3c08ad96991c8c373/tomli-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a154a9ae14bfcf5d8917a59b51ffd5a3ac1fd149b71b47a3a104ca4edcfa845", size = 239555, upload-time = "2025-10-08T22:01:04.834Z" }, + { url = "https://files.pythonhosted.org/packages/ba/28/72f8afd73f1d0e7829bfc093f4cb98ce0a40ffc0cc997009ee1ed94ba705/tomli-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:74bf8464ff93e413514fefd2be591c3b0b23231a77f901db1eb30d6f712fc42c", size = 245128, upload-time = "2025-10-08T22:01:05.84Z" }, + { url = "https://files.pythonhosted.org/packages/b6/eb/a7679c8ac85208706d27436e8d421dfa39d4c914dcf5fa8083a9305f58d9/tomli-2.3.0-cp311-cp311-win32.whl", hash = "sha256:00b5f5d95bbfc7d12f91ad8c593a1659b6387b43f054104cda404be6bda62456", size = 96445, upload-time = "2025-10-08T22:01:06.896Z" }, + { url = "https://files.pythonhosted.org/packages/0a/fe/3d3420c4cb1ad9cb462fb52967080575f15898da97e21cb6f1361d505383/tomli-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:4dc4ce8483a5d429ab602f111a93a6ab1ed425eae3122032db7e9acf449451be", size = 107165, upload-time = "2025-10-08T22:01:08.107Z" }, + { url = "https://files.pythonhosted.org/packages/ff/b7/40f36368fcabc518bb11c8f06379a0fd631985046c038aca08c6d6a43c6e/tomli-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7d86942e56ded512a594786a5ba0a5e521d02529b3826e7761a05138341a2ac", size = 154891, upload-time = "2025-10-08T22:01:09.082Z" }, + { url = "https://files.pythonhosted.org/packages/f9/3f/d9dd692199e3b3aab2e4e4dd948abd0f790d9ded8cd10cbaae276a898434/tomli-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:73ee0b47d4dad1c5e996e3cd33b8a76a50167ae5f96a2607cbe8cc773506ab22", size = 148796, upload-time = "2025-10-08T22:01:10.266Z" }, + { url = "https://files.pythonhosted.org/packages/60/83/59bff4996c2cf9f9387a0f5a3394629c7efa5ef16142076a23a90f1955fa/tomli-2.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:792262b94d5d0a466afb5bc63c7daa9d75520110971ee269152083270998316f", size = 242121, upload-time = "2025-10-08T22:01:11.332Z" }, + { url = "https://files.pythonhosted.org/packages/45/e5/7c5119ff39de8693d6baab6c0b6dcb556d192c165596e9fc231ea1052041/tomli-2.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f195fe57ecceac95a66a75ac24d9d5fbc98ef0962e09b2eddec5d39375aae52", size = 250070, upload-time = "2025-10-08T22:01:12.498Z" }, + { url = "https://files.pythonhosted.org/packages/45/12/ad5126d3a278f27e6701abde51d342aa78d06e27ce2bb596a01f7709a5a2/tomli-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31d432427dcbf4d86958c184b9bfd1e96b5b71f8eb17e6d02531f434fd335b8", size = 245859, upload-time = "2025-10-08T22:01:13.551Z" }, + { url = "https://files.pythonhosted.org/packages/fb/a1/4d6865da6a71c603cfe6ad0e6556c73c76548557a8d658f9e3b142df245f/tomli-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b0882799624980785240ab732537fcfc372601015c00f7fc367c55308c186f6", size = 250296, upload-time = "2025-10-08T22:01:14.614Z" }, + { url = "https://files.pythonhosted.org/packages/a0/b7/a7a7042715d55c9ba6e8b196d65d2cb662578b4d8cd17d882d45322b0d78/tomli-2.3.0-cp312-cp312-win32.whl", hash = "sha256:ff72b71b5d10d22ecb084d345fc26f42b5143c5533db5e2eaba7d2d335358876", size = 97124, upload-time = "2025-10-08T22:01:15.629Z" }, + { url = "https://files.pythonhosted.org/packages/06/1e/f22f100db15a68b520664eb3328fb0ae4e90530887928558112c8d1f4515/tomli-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:1cb4ed918939151a03f33d4242ccd0aa5f11b3547d0cf30f7c74a408a5b99878", size = 107698, upload-time = "2025-10-08T22:01:16.51Z" }, + { url = "https://files.pythonhosted.org/packages/89/48/06ee6eabe4fdd9ecd48bf488f4ac783844fd777f547b8d1b61c11939974e/tomli-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5192f562738228945d7b13d4930baffda67b69425a7f0da96d360b0a3888136b", size = 154819, upload-time = "2025-10-08T22:01:17.964Z" }, + { url = "https://files.pythonhosted.org/packages/f1/01/88793757d54d8937015c75dcdfb673c65471945f6be98e6a0410fba167ed/tomli-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:be71c93a63d738597996be9528f4abe628d1adf5e6eb11607bc8fe1a510b5dae", size = 148766, upload-time = "2025-10-08T22:01:18.959Z" }, + { url = "https://files.pythonhosted.org/packages/42/17/5e2c956f0144b812e7e107f94f1cc54af734eb17b5191c0bbfb72de5e93e/tomli-2.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4665508bcbac83a31ff8ab08f424b665200c0e1e645d2bd9ab3d3e557b6185b", size = 240771, upload-time = "2025-10-08T22:01:20.106Z" }, + { url = "https://files.pythonhosted.org/packages/d5/f4/0fbd014909748706c01d16824eadb0307115f9562a15cbb012cd9b3512c5/tomli-2.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4021923f97266babc6ccab9f5068642a0095faa0a51a246a6a02fccbb3514eaf", size = 248586, upload-time = "2025-10-08T22:01:21.164Z" }, + { url = "https://files.pythonhosted.org/packages/30/77/fed85e114bde5e81ecf9bc5da0cc69f2914b38f4708c80ae67d0c10180c5/tomli-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4ea38c40145a357d513bffad0ed869f13c1773716cf71ccaa83b0fa0cc4e42f", size = 244792, upload-time = "2025-10-08T22:01:22.417Z" }, + { url = "https://files.pythonhosted.org/packages/55/92/afed3d497f7c186dc71e6ee6d4fcb0acfa5f7d0a1a2878f8beae379ae0cc/tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05", size = 248909, upload-time = "2025-10-08T22:01:23.859Z" }, + { url = "https://files.pythonhosted.org/packages/f8/84/ef50c51b5a9472e7265ce1ffc7f24cd4023d289e109f669bdb1553f6a7c2/tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606", size = 96946, upload-time = "2025-10-08T22:01:24.893Z" }, + { url = "https://files.pythonhosted.org/packages/b2/b7/718cd1da0884f281f95ccfa3a6cc572d30053cba64603f79d431d3c9b61b/tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999", size = 107705, upload-time = "2025-10-08T22:01:26.153Z" }, + { url = "https://files.pythonhosted.org/packages/19/94/aeafa14a52e16163008060506fcb6aa1949d13548d13752171a755c65611/tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e", size = 154244, upload-time = "2025-10-08T22:01:27.06Z" }, + { url = "https://files.pythonhosted.org/packages/db/e4/1e58409aa78eefa47ccd19779fc6f36787edbe7d4cd330eeeedb33a4515b/tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3", size = 148637, upload-time = "2025-10-08T22:01:28.059Z" }, + { url = "https://files.pythonhosted.org/packages/26/b6/d1eccb62f665e44359226811064596dd6a366ea1f985839c566cd61525ae/tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc", size = 241925, upload-time = "2025-10-08T22:01:29.066Z" }, + { url = "https://files.pythonhosted.org/packages/70/91/7cdab9a03e6d3d2bb11beae108da5bdc1c34bdeb06e21163482544ddcc90/tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0", size = 249045, upload-time = "2025-10-08T22:01:31.98Z" }, + { url = "https://files.pythonhosted.org/packages/15/1b/8c26874ed1f6e4f1fcfeb868db8a794cbe9f227299402db58cfcc858766c/tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879", size = 245835, upload-time = "2025-10-08T22:01:32.989Z" }, + { url = "https://files.pythonhosted.org/packages/fd/42/8e3c6a9a4b1a1360c1a2a39f0b972cef2cc9ebd56025168c4137192a9321/tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005", size = 253109, upload-time = "2025-10-08T22:01:34.052Z" }, + { url = "https://files.pythonhosted.org/packages/22/0c/b4da635000a71b5f80130937eeac12e686eefb376b8dee113b4a582bba42/tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463", size = 97930, upload-time = "2025-10-08T22:01:35.082Z" }, + { url = "https://files.pythonhosted.org/packages/b9/74/cb1abc870a418ae99cd5c9547d6bce30701a954e0e721821df483ef7223c/tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8", size = 107964, upload-time = "2025-10-08T22:01:36.057Z" }, + { url = "https://files.pythonhosted.org/packages/54/78/5c46fff6432a712af9f792944f4fcd7067d8823157949f4e40c56b8b3c83/tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77", size = 163065, upload-time = "2025-10-08T22:01:37.27Z" }, + { url = "https://files.pythonhosted.org/packages/39/67/f85d9bd23182f45eca8939cd2bc7050e1f90c41f4a2ecbbd5963a1d1c486/tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf", size = 159088, upload-time = "2025-10-08T22:01:38.235Z" }, + { url = "https://files.pythonhosted.org/packages/26/5a/4b546a0405b9cc0659b399f12b6adb750757baf04250b148d3c5059fc4eb/tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530", size = 268193, upload-time = "2025-10-08T22:01:39.712Z" }, + { url = "https://files.pythonhosted.org/packages/42/4f/2c12a72ae22cf7b59a7fe75b3465b7aba40ea9145d026ba41cb382075b0e/tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b", size = 275488, upload-time = "2025-10-08T22:01:40.773Z" }, + { url = "https://files.pythonhosted.org/packages/92/04/a038d65dbe160c3aa5a624e93ad98111090f6804027d474ba9c37c8ae186/tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67", size = 272669, upload-time = "2025-10-08T22:01:41.824Z" }, + { url = "https://files.pythonhosted.org/packages/be/2f/8b7c60a9d1612a7cbc39ffcca4f21a73bf368a80fc25bccf8253e2563267/tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f", size = 279709, upload-time = "2025-10-08T22:01:43.177Z" }, + { url = "https://files.pythonhosted.org/packages/7e/46/cc36c679f09f27ded940281c38607716c86cf8ba4a518d524e349c8b4874/tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0", size = 107563, upload-time = "2025-10-08T22:01:44.233Z" }, + { url = "https://files.pythonhosted.org/packages/84/ff/426ca8683cf7b753614480484f6437f568fd2fda2edbdf57a2d3d8b27a0b/tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba", size = 119756, upload-time = "2025-10-08T22:01:45.234Z" }, + { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" }, +] + +[[package]] +name = "tox" +version = "4.11.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cachetools" }, + { name = "chardet" }, + { name = "colorama" }, + { name = "filelock" }, + { name = "packaging" }, + { name = "platformdirs" }, + { name = "pluggy" }, + { name = "pyproject-api" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "virtualenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1a/78/e9672163a341a557b6fb9f6723c771835c763c0de2bccc9f4abc29e0a4a2/tox-4.11.4.tar.gz", hash = "sha256:73a7240778fabf305aeb05ab8ea26e575e042ab5a18d71d0ed13e343a51d6ce1", size = 176168, upload-time = "2023-11-28T04:14:16.034Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cd/88/de28a027acdb3a1b070a8acdff62bd23fdc23ce32acc7ac4b92b088979a4/tox-4.11.4-py3-none-any.whl", hash = "sha256:2adb83d68f27116812b69aa36676a8d6a52249cb0d173649de0e7d0c2e3e7229", size = 153989, upload-time = "2023-11-28T04:14:11.956Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "virtualenv" +version = "20.35.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "distlib" }, + { name = "filelock" }, + { name = "platformdirs" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a4/d5/b0ccd381d55c8f45d46f77df6ae59fbc23d19e901e2d523395598e5f4c93/virtualenv-20.35.3.tar.gz", hash = "sha256:4f1a845d131133bdff10590489610c98c168ff99dc75d6c96853801f7f67af44", size = 6002907, upload-time = "2025-10-10T21:23:33.178Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/73/d9a94da0e9d470a543c1b9d3ccbceb0f59455983088e727b8a1824ed90fb/virtualenv-20.35.3-py3-none-any.whl", hash = "sha256:63d106565078d8c8d0b206d48080f938a8b25361e19432d2c9db40d2899c810a", size = 5981061, upload-time = "2025-10-10T21:23:30.433Z" }, +] From 3c6f1da29efe0dcb7a926551229857dd57079245 Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sat, 25 Oct 2025 17:26:26 +0200 Subject: [PATCH 02/22] run_uv --- dev.py | 49 ++++++++++++++--------------------------- pyperformance/_utils.py | 13 ++++++++++- pyperformance/_venv.py | 15 +++++-------- 3 files changed, 34 insertions(+), 43 deletions(-) diff --git a/dev.py b/dev.py index fffe568f..9d87ae18 100644 --- a/dev.py +++ b/dev.py @@ -4,6 +4,7 @@ import shutil import subprocess import sys +from pyperformance import _utils REPO_ROOT = os.path.dirname(os.path.abspath(__file__)) VENVS = os.path.join(REPO_ROOT, ".venvs") @@ -38,27 +39,18 @@ def ensure_venv_ready(venvroot=None, kind="dev", venvsdir=VENVS): isready = os.path.exists(readyfile) if not isready: relroot = os.path.relpath(venvroot) - uv = shutil.which("uv") - if not uv: - sys.exit( - "ERROR: uv executable not found. " - "Install uv from https://astral.sh/uv and retry." - ) if os.path.exists(venvroot): print(f"uv env {relroot} not ready, re-creating...") shutil.rmtree(venvroot) else: print(f"creating uv env at {relroot}...") - result = subprocess.run( - [ - uv, - "venv", - "--python", - sys.executable, - venvroot, - ] + ec, _, _ = _utils.run_uv( + "venv", + "--python", + sys.executable, + venvroot, ) - if result.returncode != 0: + if ec != 0: sys.exit("ERROR: uv venv creation failed") else: assert os.path.exists(os.path.join(venvroot, "pyvenv.cfg")) @@ -71,25 +63,16 @@ def ensure_venv_ready(venvroot=None, kind="dev", venvsdir=VENVS): if not isready: relroot = os.path.relpath(venvroot) print(f"uv env {relroot} not ready, installing dependencies...") - uv = shutil.which("uv") - if not uv: - sys.exit( - "ERROR: uv executable not found. " - "Install uv from https://astral.sh/uv and retry." - ) - proc = subprocess.run( - [ - uv, - "pip", - "install", - "--python", - python, - "--upgrade", - "--editable", - f"{REPO_ROOT}[dev]", - ], + ec, _, _ = _utils.run_uv( + "pip", + "install", + "--python", + python, + "--upgrade", + "--editable", + f"{REPO_ROOT}[dev]", ) - if proc.returncode != 0: + if ec != 0: sys.exit("ERROR: uv pip install failed") with open(readyfile, "w"): pass diff --git a/pyperformance/_utils.py b/pyperformance/_utils.py index e0eaa5b2..50c90ee8 100644 --- a/pyperformance/_utils.py +++ b/pyperformance/_utils.py @@ -11,6 +11,8 @@ "parse_name_pattern", "parse_selections", "parse_tag_pattern", + # tooling + "run_uv", ] @@ -21,8 +23,8 @@ import errno import os import os.path -import shlex import shutil +import shlex import subprocess import sys import tempfile @@ -165,6 +167,15 @@ def run_python(*args, python=sys.executable, **kwargs): return run_cmd([python, *args], **kwargs) +def run_uv(*args, env=None, capture=None, verbose=True): + uv = shutil.which("uv") + if not uv: + if verbose: + print("ERROR: uv executable not found. Install uv from https://astral.sh/uv.") + return 127, None, None + return run_cmd([uv, *args], env=env, capture=capture, verbose=verbose) + + ####################################### # network utils diff --git a/pyperformance/_venv.py b/pyperformance/_venv.py index b6bf4c28..a4c675db 100644 --- a/pyperformance/_venv.py +++ b/pyperformance/_venv.py @@ -112,10 +112,6 @@ def create_venv( ): """Create a new venv at the given root, optionally installing pip.""" already_existed = os.path.exists(root) - uv = shutil.which("uv") - if not uv: - print("ERROR: uv executable not found. Install uv from https://astral.sh/uv.") - raise VenvCreationFailedError(root, 127, already_existed) if isinstance(python, str) or python is None: target_python = python or sys.executable @@ -125,11 +121,12 @@ def create_venv( except AttributeError as exc: raise TypeError(f"expected python str, got {python!r}") from exc - argv = [uv, "venv"] - if target_python: - argv.extend(["--python", target_python]) - argv.append(root) - ec, _, _ = _utils.run_cmd(argv, env=env) + args = [ + "venv", + *( ["--python", target_python] if target_python else [] ), + root, + ] + ec, _, _ = _utils.run_uv(*args, env=env) if ec != 0: if cleanonfail and not already_existed: _utils.safe_rmtree(root) From 1414f8753e9a24bded244f5cdd7dd6ed75bf7b36 Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sat, 25 Oct 2025 17:34:14 +0200 Subject: [PATCH 03/22] lint --- dev.py | 2 +- pyperformance/_utils.py | 6 ++++-- pyperformance/_venv.py | 3 +-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/dev.py b/dev.py index 9d87ae18..669d6712 100644 --- a/dev.py +++ b/dev.py @@ -2,8 +2,8 @@ import os.path import shutil -import subprocess import sys + from pyperformance import _utils REPO_ROOT = os.path.dirname(os.path.abspath(__file__)) diff --git a/pyperformance/_utils.py b/pyperformance/_utils.py index 50c90ee8..83fc7f63 100644 --- a/pyperformance/_utils.py +++ b/pyperformance/_utils.py @@ -23,8 +23,8 @@ import errno import os import os.path -import shutil import shlex +import shutil import subprocess import sys import tempfile @@ -171,7 +171,9 @@ def run_uv(*args, env=None, capture=None, verbose=True): uv = shutil.which("uv") if not uv: if verbose: - print("ERROR: uv executable not found. Install uv from https://astral.sh/uv.") + print( + "ERROR: uv executable not found. Install uv from https://astral.sh/uv." + ) return 127, None, None return run_cmd([uv, *args], env=env, capture=capture, verbose=verbose) diff --git a/pyperformance/_venv.py b/pyperformance/_venv.py index a4c675db..bd1a56ac 100644 --- a/pyperformance/_venv.py +++ b/pyperformance/_venv.py @@ -2,7 +2,6 @@ import os import os.path -import shutil import sys import types @@ -123,7 +122,7 @@ def create_venv( args = [ "venv", - *( ["--python", target_python] if target_python else [] ), + *(["--python", target_python] if target_python else []), root, ] ec, _, _ = _utils.run_uv(*args, env=env) From 76ad094e22c77fa9546549f050dce1542bf3b4ae Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sat, 25 Oct 2025 19:05:20 +0200 Subject: [PATCH 04/22] unbreak ga? --- .github/workflows/main.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8535da4f..d7260341 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -64,6 +64,10 @@ jobs: with: python-version: ${{ matrix.python }} nogil: ${{ matrix.build == 'free-threading' }} + - name: Install uv + uses: astral-sh/setup-uv@v6 + with: + version: "0.9.5" - name: Install # pyperformance must be installed: # pyperformance/tests/test_compare.py imports it From bb1101f5864123a8d55d998673bcbd1fbafeefba Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sat, 25 Oct 2025 19:12:30 +0200 Subject: [PATCH 05/22] seed venv --- pyperformance/tests/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyperformance/tests/__init__.py b/pyperformance/tests/__init__.py index 9f0a5858..1ffdc49e 100644 --- a/pyperformance/tests/__init__.py +++ b/pyperformance/tests/__init__.py @@ -78,7 +78,7 @@ def cleanup(): uv = shutil.which("uv") if not uv: raise RuntimeError("uv executable is required to provision test environments") - argv = [uv, "venv"] + argv = [uv, "venv", "--seed"] if python: argv.extend(["--python", python]) argv.append(root) From 2b7cfa3739744ac6c97341a0a539cce31c8d125b Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sat, 25 Oct 2025 20:01:45 +0200 Subject: [PATCH 06/22] ensure once --- pyperformance/commands.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pyperformance/commands.py b/pyperformance/commands.py index 7d67010f..7fc6603b 100644 --- a/pyperformance/commands.py +++ b/pyperformance/commands.py @@ -89,7 +89,6 @@ def cmd_venv_recreate(options, root, python, benchmarks): venv.ensure_pip() try: venv.ensure_reqs(requirements) - venv.ensure_reqs(requirements) except _venv.RequirementsInstallationFailedError: sys.exit(1) else: From c776958804535f36595980d02d7075748e6a93bc Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sat, 25 Oct 2025 20:30:59 +0200 Subject: [PATCH 07/22] use uv pip --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d7260341..d33b2259 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -72,8 +72,8 @@ jobs: # pyperformance must be installed: # pyperformance/tests/test_compare.py imports it run: | - python -m pip install --upgrade pip setuptools - python -m pip install -e . + uv pip install --upgrade pip setuptools + uv pip install -e . - name: Display Python version run: | python -VV From 40031e2d705cfb331d0b7715c7d8f2d52ba61eb4 Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sat, 25 Oct 2025 20:31:37 +0200 Subject: [PATCH 08/22] gh action caching --- .github/workflows/main.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d33b2259..b9f0eac2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -65,9 +65,11 @@ jobs: python-version: ${{ matrix.python }} nogil: ${{ matrix.build == 'free-threading' }} - name: Install uv - uses: astral-sh/setup-uv@v6 + uses: astral-sh/setup-uv@v7 with: version: "0.9.5" + enable-cache: true + cache-suffix: ${{ runner.os }}-${{ matrix.python }}-${{ matrix.build != '' && matrix.build || 'default' }} - name: Install # pyperformance must be installed: # pyperformance/tests/test_compare.py imports it From 976e978c5f81f65f314fdfbdbd47710b7c7b752a Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sat, 25 Oct 2025 20:34:00 +0200 Subject: [PATCH 09/22] system in ga should be ok --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b9f0eac2..dfd9d5a4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -74,8 +74,8 @@ jobs: # pyperformance must be installed: # pyperformance/tests/test_compare.py imports it run: | - uv pip install --upgrade pip setuptools - uv pip install -e . + uv pip install --system --upgrade pip setuptools + uv pip install --system -e . - name: Display Python version run: | python -VV From 5f1af6398b950067a8db6f2793246155fa9d46f0 Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sat, 25 Oct 2025 20:50:58 +0200 Subject: [PATCH 10/22] --python --- .github/workflows/main.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index dfd9d5a4..447b39e2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -71,11 +71,13 @@ jobs: enable-cache: true cache-suffix: ${{ runner.os }}-${{ matrix.python }}-${{ matrix.build != '' && matrix.build || 'default' }} - name: Install + shell: bash # pyperformance must be installed: # pyperformance/tests/test_compare.py imports it run: | - uv pip install --system --upgrade pip setuptools - uv pip install --system -e . + PYTHON_BIN="$(python -c 'import sys; print(sys.executable)')" + uv pip install --python "$PYTHON_BIN" --upgrade pip setuptools + uv pip install --python "$PYTHON_BIN" -e . - name: Display Python version run: | python -VV From 164f0c87caf5decf0a953c9fb122469b6457235b Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sat, 25 Oct 2025 21:04:54 +0200 Subject: [PATCH 11/22] psutil is the source of all evil --- pyproject.toml | 1 - uv.lock | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index aa8966b5..359f5cd5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -65,7 +65,6 @@ classifiers = [ dynamic = [ "version" ] dependencies = [ "packaging==23.1", - "psutil==5.9.5", "pyperf==2.9", "tomli; python_version<'3.11'", ] diff --git a/uv.lock b/uv.lock index f98dddff..c508bf1d 100644 --- a/uv.lock +++ b/uv.lock @@ -168,7 +168,6 @@ name = "pyperformance" source = { editable = "." } dependencies = [ { name = "packaging" }, - { name = "psutil" }, { name = "pyperf" }, { name = "tomli", marker = "python_full_version < '3.11'" }, ] @@ -184,8 +183,7 @@ dev = [ requires-dist = [ { name = "mypy", marker = "extra == 'dev'", specifier = "==1.18.2" }, { name = "packaging", specifier = "==23.1" }, - { name = "psutil", specifier = "==5.9.5" }, - { name = "pyperf", specifier = "==2.9.0" }, + { name = "pyperf", specifier = "==2.9" }, { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "tomli", marker = "extra == 'dev'" }, { name = "tox", marker = "extra == 'dev'" }, From 87e58b7e8896cbe9c7bfd9309d15fb91608f7599 Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sat, 25 Oct 2025 22:05:32 +0200 Subject: [PATCH 12/22] use run_uv everywhere --- pyperformance/tests/__init__.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/pyperformance/tests/__init__.py b/pyperformance/tests/__init__.py index 1ffdc49e..54e37555 100644 --- a/pyperformance/tests/__init__.py +++ b/pyperformance/tests/__init__.py @@ -9,6 +9,8 @@ import tempfile import unittest +from pyperformance._utils import run_uv + TESTS_ROOT = os.path.realpath(os.path.dirname(__file__)) DATA_DIR = os.path.join(TESTS_ROOT, "data") REPO_ROOT = os.path.dirname(os.path.dirname(TESTS_ROOT)) @@ -75,14 +77,17 @@ def cleanup(): def cleanup(): return None - uv = shutil.which("uv") - if not uv: - raise RuntimeError("uv executable is required to provision test environments") - argv = [uv, "venv", "--seed"] + argv = ["venv", "--seed"] if python: argv.extend(["--python", python]) argv.append(root) - run_cmd(*argv, capture=not verbose, onfail="raise", verbose=verbose) + exitcode, _, _ = run_uv(*argv, capture=not verbose, verbose=verbose) + if exitcode: + if exitcode == 127: + raise RuntimeError( + "uv executable is required to provision test environments" + ) + raise RuntimeError(f'"uv {" ".join(argv)}" failed with exit code {exitcode}') venv_root = os.path.realpath(root) return venv_root, _resolve_venv_python(venv_root), cleanup From 05b1da4eadfe7c21404a663f50701d91b6288bb6 Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sat, 25 Oct 2025 22:17:48 +0200 Subject: [PATCH 13/22] import less --- dev.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dev.py b/dev.py index 669d6712..b6e4127d 100644 --- a/dev.py +++ b/dev.py @@ -4,7 +4,7 @@ import shutil import sys -from pyperformance import _utils +from pyperformance._utils import run_uv REPO_ROOT = os.path.dirname(os.path.abspath(__file__)) VENVS = os.path.join(REPO_ROOT, ".venvs") @@ -44,7 +44,7 @@ def ensure_venv_ready(venvroot=None, kind="dev", venvsdir=VENVS): shutil.rmtree(venvroot) else: print(f"creating uv env at {relroot}...") - ec, _, _ = _utils.run_uv( + ec, _, _ = run_uv( "venv", "--python", sys.executable, @@ -63,7 +63,7 @@ def ensure_venv_ready(venvroot=None, kind="dev", venvsdir=VENVS): if not isready: relroot = os.path.relpath(venvroot) print(f"uv env {relroot} not ready, installing dependencies...") - ec, _, _ = _utils.run_uv( + ec, _, _ = run_uv( "pip", "install", "--python", From d458ec1258c23bb038e0ce46a0d7110589576047 Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sat, 25 Oct 2025 23:12:42 +0200 Subject: [PATCH 14/22] separate module, uv pip --- dev.py | 2 +- pyperformance/_utils.py | 13 ----- pyperformance/_uv.py | 85 +++++++++++++++++++++++++++++++++ pyperformance/_venv.py | 10 ++-- pyperformance/tests/__init__.py | 2 +- 5 files changed, 93 insertions(+), 19 deletions(-) create mode 100644 pyperformance/_uv.py diff --git a/dev.py b/dev.py index b6e4127d..82e7a0da 100644 --- a/dev.py +++ b/dev.py @@ -4,7 +4,7 @@ import shutil import sys -from pyperformance._utils import run_uv +from pyperformance._uv import run_uv REPO_ROOT = os.path.dirname(os.path.abspath(__file__)) VENVS = os.path.join(REPO_ROOT, ".venvs") diff --git a/pyperformance/_utils.py b/pyperformance/_utils.py index 83fc7f63..e0eaa5b2 100644 --- a/pyperformance/_utils.py +++ b/pyperformance/_utils.py @@ -11,8 +11,6 @@ "parse_name_pattern", "parse_selections", "parse_tag_pattern", - # tooling - "run_uv", ] @@ -167,17 +165,6 @@ def run_python(*args, python=sys.executable, **kwargs): return run_cmd([python, *args], **kwargs) -def run_uv(*args, env=None, capture=None, verbose=True): - uv = shutil.which("uv") - if not uv: - if verbose: - print( - "ERROR: uv executable not found. Install uv from https://astral.sh/uv." - ) - return 127, None, None - return run_cmd([uv, *args], env=env, capture=capture, verbose=verbose) - - ####################################### # network utils diff --git a/pyperformance/_uv.py b/pyperformance/_uv.py new file mode 100644 index 00000000..4a427bd7 --- /dev/null +++ b/pyperformance/_uv.py @@ -0,0 +1,85 @@ +"""Utilities for invoking the ``uv`` CLI.""" + +import os +import os.path +import shutil + +from . import _pip, _utils + + +def run_uv(*args, env=None, capture=None, verbose=True): + uv = shutil.which("uv") + if not uv: + if verbose: + print( + "ERROR: uv executable not found. Install uv from https://astral.sh/uv." + ) + return 127, None, None + return _utils.run_cmd([uv, *args], env=env, capture=capture, verbose=verbose) + + +def _iter_requirements(*reqs): + for item in reqs: + if item is None: + continue + if isinstance(item, os.PathLike): + yield os.fspath(item) + continue + if isinstance(item, bytes): + yield item.decode() + continue + if isinstance(item, str): + yield item + continue + specs = getattr(item, "specs", None) + if specs is not None: + yield from _iter_requirements(*specs) + continue + if isinstance(item, (list, tuple, set)): + yield from _iter_requirements(*item) + continue + try: + iterator = iter(item) + except TypeError: + yield str(item) + else: + yield from _iter_requirements(*iterator) + + +def install_requirements( + *reqs, + python, + upgrade=True, + env=None, + verbose=True, +): + requirements = list(_iter_requirements(*reqs)) + if not requirements: + return 0, None, None + + args = [ + "pip", + "install", + "--python", + python, + ] + if upgrade: + args.append("--upgrade") + + for spec in requirements: + if os.path.isfile(spec) and spec.endswith(".txt"): + args.extend(["-r", spec]) + else: + args.append(spec) + + ec, stdout, stderr = run_uv(*args, env=env, verbose=verbose) + if ec == 127: + first, *rest = requirements + return _pip.install_requirements( + first, + *rest, + python=python, + env=env, + upgrade=upgrade, + ) + return ec, stdout, stderr diff --git a/pyperformance/_venv.py b/pyperformance/_venv.py index bd1a56ac..4f9d30a0 100644 --- a/pyperformance/_venv.py +++ b/pyperformance/_venv.py @@ -5,7 +5,7 @@ import sys import types -from . import _pip, _pythoninfo, _utils +from . import _pip, _pythoninfo, _utils, _uv class VenvCreationFailedError(Exception): @@ -125,7 +125,7 @@ def create_venv( *(["--python", target_python] if target_python else []), root, ] - ec, _, _ = _utils.run_uv(*args, env=env) + ec, _, _ = _uv.run_uv(*args, env=env) if ec != 0: if cleanonfail and not already_existed: _utils.safe_rmtree(root) @@ -214,7 +214,9 @@ def base(self): return self._base def ensure_pip(self, downloaddir=None, *, installer=True, upgrade=True): - if not upgrade and _pip.is_pip_installed(self.python, env=self._env): + if _pip.is_pip_installed(self.python, env=self._env): + if upgrade: + self.upgrade_pip(installer=installer) return ec, _, _ = _pip.install_pip( self.python, @@ -250,7 +252,7 @@ def upgrade_pip(self, *, installer=True): def ensure_reqs(self, *reqs, upgrade=True): print("Installing requirements into the virtual environment %s" % self.root) - ec, _, _ = _pip.install_requirements( + ec, _, _ = _uv.install_requirements( *reqs, python=self.python, env=self._env, diff --git a/pyperformance/tests/__init__.py b/pyperformance/tests/__init__.py index 54e37555..26c0c920 100644 --- a/pyperformance/tests/__init__.py +++ b/pyperformance/tests/__init__.py @@ -9,7 +9,7 @@ import tempfile import unittest -from pyperformance._utils import run_uv +from pyperformance._uv import run_uv TESTS_ROOT = os.path.realpath(os.path.dirname(__file__)) DATA_DIR = os.path.join(TESTS_ROOT, "data") From a3ef49c0c2f9f24fc6dcb44daa8010b1569a83c9 Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sat, 25 Oct 2025 23:23:08 +0200 Subject: [PATCH 15/22] copy from _pip --- pyperformance/_uv.py | 39 ++++++++++----------------------------- 1 file changed, 10 insertions(+), 29 deletions(-) diff --git a/pyperformance/_uv.py b/pyperformance/_uv.py index 4a427bd7..6f62110f 100644 --- a/pyperformance/_uv.py +++ b/pyperformance/_uv.py @@ -18,34 +18,6 @@ def run_uv(*args, env=None, capture=None, verbose=True): return _utils.run_cmd([uv, *args], env=env, capture=capture, verbose=verbose) -def _iter_requirements(*reqs): - for item in reqs: - if item is None: - continue - if isinstance(item, os.PathLike): - yield os.fspath(item) - continue - if isinstance(item, bytes): - yield item.decode() - continue - if isinstance(item, str): - yield item - continue - specs = getattr(item, "specs", None) - if specs is not None: - yield from _iter_requirements(*specs) - continue - if isinstance(item, (list, tuple, set)): - yield from _iter_requirements(*item) - continue - try: - iterator = iter(item) - except TypeError: - yield str(item) - else: - yield from _iter_requirements(*iterator) - - def install_requirements( *reqs, python, @@ -53,7 +25,16 @@ def install_requirements( env=None, verbose=True, ): - requirements = list(_iter_requirements(*reqs)) + requirements = [] + for item in reqs: + if item is None: + continue + if isinstance(item, os.PathLike): + requirements.append(os.fspath(item)) + elif isinstance(item, bytes): + requirements.append(item.decode()) + else: + requirements.append(str(item)) if not requirements: return 0, None, None From 3a402551d306ba7b4e80c1b308c426f5f2a08e30 Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sat, 25 Oct 2025 23:26:30 +0200 Subject: [PATCH 16/22] reduce the diff --- dev.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dev.py b/dev.py index 82e7a0da..b09b8375 100644 --- a/dev.py +++ b/dev.py @@ -39,11 +39,11 @@ def ensure_venv_ready(venvroot=None, kind="dev", venvsdir=VENVS): isready = os.path.exists(readyfile) if not isready: relroot = os.path.relpath(venvroot) - if os.path.exists(venvroot): + if not os.path.exists(venvroot): + print(f"creating uv env at {relroot}...") + else: print(f"uv env {relroot} not ready, re-creating...") shutil.rmtree(venvroot) - else: - print(f"creating uv env at {relroot}...") ec, _, _ = run_uv( "venv", "--python", From 4b5f47a1292e94802af93873709b688cc0c43de1 Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sat, 25 Oct 2025 23:44:24 +0200 Subject: [PATCH 17/22] how fast are we without caching? --- .github/workflows/main.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 447b39e2..232d503d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -68,7 +68,6 @@ jobs: uses: astral-sh/setup-uv@v7 with: version: "0.9.5" - enable-cache: true cache-suffix: ${{ runner.os }}-${{ matrix.python }}-${{ matrix.build != '' && matrix.build || 'default' }} - name: Install shell: bash From 60b981242c846917f0dce6c54a393a3987b9eb25 Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sat, 25 Oct 2025 23:57:52 +0200 Subject: [PATCH 18/22] Revert "how fast are we without caching?" This reverts commit 4b5f47a1292e94802af93873709b688cc0c43de1. --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 232d503d..447b39e2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -68,6 +68,7 @@ jobs: uses: astral-sh/setup-uv@v7 with: version: "0.9.5" + enable-cache: true cache-suffix: ${{ runner.os }}-${{ matrix.python }}-${{ matrix.build != '' && matrix.build || 'default' }} - name: Install shell: bash From 2ddd03ce987ddce372d2008902fb4bcbac2964db Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sun, 26 Oct 2025 01:38:11 +0200 Subject: [PATCH 19/22] smaller diff --- dev.py | 4 ++-- pyperformance/tests/__init__.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dev.py b/dev.py index b09b8375..b5659917 100644 --- a/dev.py +++ b/dev.py @@ -4,8 +4,6 @@ import shutil import sys -from pyperformance._uv import run_uv - REPO_ROOT = os.path.dirname(os.path.abspath(__file__)) VENVS = os.path.join(REPO_ROOT, ".venvs") @@ -32,6 +30,8 @@ def ensure_venv_ready(venvroot=None, kind="dev", venvsdir=VENVS): readyfile = os.path.join(sys.prefix, "READY") isready = os.path.exists(readyfile) else: + from pyperformance._uv import run_uv + if not venvroot: venvroot = resolve_venv_root(kind, venvsdir) # Make sure the venv exists. diff --git a/pyperformance/tests/__init__.py b/pyperformance/tests/__init__.py index 26c0c920..0853a819 100644 --- a/pyperformance/tests/__init__.py +++ b/pyperformance/tests/__init__.py @@ -9,8 +9,6 @@ import tempfile import unittest -from pyperformance._uv import run_uv - TESTS_ROOT = os.path.realpath(os.path.dirname(__file__)) DATA_DIR = os.path.join(TESTS_ROOT, "data") REPO_ROOT = os.path.dirname(os.path.dirname(TESTS_ROOT)) @@ -66,6 +64,8 @@ def _resolve_venv_python(venv): def create_venv(root=None, python=sys.executable, *, verbose=False): + from pyperformance._uv import run_uv + if not root: tmpdir = tempfile.mkdtemp() root = os.path.join(tmpdir, "venv") From 4dfad0850f8871b8d1db95fe862d68964268ec79 Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sun, 26 Oct 2025 01:42:25 +0200 Subject: [PATCH 20/22] reduce diff even more! --- dev.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dev.py b/dev.py index b5659917..2a366b8b 100644 --- a/dev.py +++ b/dev.py @@ -40,9 +40,9 @@ def ensure_venv_ready(venvroot=None, kind="dev", venvsdir=VENVS): if not isready: relroot = os.path.relpath(venvroot) if not os.path.exists(venvroot): - print(f"creating uv env at {relroot}...") + print(f"creating env at {relroot}...") else: - print(f"uv env {relroot} not ready, re-creating...") + print(f"venv {relroot} not ready, re-creating...") shutil.rmtree(venvroot) ec, _, _ = run_uv( "venv", @@ -62,7 +62,7 @@ def ensure_venv_ready(venvroot=None, kind="dev", venvsdir=VENVS): # Now make sure the venv has pyperformance installed. if not isready: relroot = os.path.relpath(venvroot) - print(f"uv env {relroot} not ready, installing dependencies...") + print(f"venv {relroot} not ready, installing dependencies...") ec, _, _ = run_uv( "pip", "install", @@ -76,7 +76,7 @@ def ensure_venv_ready(venvroot=None, kind="dev", venvsdir=VENVS): sys.exit("ERROR: uv pip install failed") with open(readyfile, "w"): pass - print(f"...uv env {relroot} ready!") + print(f"...venv {relroot} ready!") return venvroot, python From 4d1f1e3f2340b51867d32048f090bcaaa012f9fd Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sun, 26 Oct 2025 01:49:00 +0200 Subject: [PATCH 21/22] diff... --- dev.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev.py b/dev.py index 2a366b8b..7d402df0 100644 --- a/dev.py +++ b/dev.py @@ -40,7 +40,7 @@ def ensure_venv_ready(venvroot=None, kind="dev", venvsdir=VENVS): if not isready: relroot = os.path.relpath(venvroot) if not os.path.exists(venvroot): - print(f"creating env at {relroot}...") + print(f"creating venv at {relroot}...") else: print(f"venv {relroot} not ready, re-creating...") shutil.rmtree(venvroot) @@ -51,7 +51,7 @@ def ensure_venv_ready(venvroot=None, kind="dev", venvsdir=VENVS): venvroot, ) if ec != 0: - sys.exit("ERROR: uv venv creation failed") + sys.exit("ERROR: venv creation failed") else: assert os.path.exists(os.path.join(venvroot, "pyvenv.cfg")) # Return the venv's Python executable. From ba9fc09458ee140a7a3f65dd61e5e49c07dd20b9 Mon Sep 17 00:00:00 2001 From: maurycy <5383+maurycy@users.noreply.github.com> Date: Sun, 26 Oct 2025 01:49:09 +0200 Subject: [PATCH 22/22] simplify the requirements --- pyperformance/_uv.py | 34 +++++++--------------------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/pyperformance/_uv.py b/pyperformance/_uv.py index 6f62110f..f9bd51af 100644 --- a/pyperformance/_uv.py +++ b/pyperformance/_uv.py @@ -18,26 +18,7 @@ def run_uv(*args, env=None, capture=None, verbose=True): return _utils.run_cmd([uv, *args], env=env, capture=capture, verbose=verbose) -def install_requirements( - *reqs, - python, - upgrade=True, - env=None, - verbose=True, -): - requirements = [] - for item in reqs: - if item is None: - continue - if isinstance(item, os.PathLike): - requirements.append(os.fspath(item)) - elif isinstance(item, bytes): - requirements.append(item.decode()) - else: - requirements.append(str(item)) - if not requirements: - return 0, None, None - +def install_requirements(reqs, *extra, python, upgrade=True, env=None, verbose=True): args = [ "pip", "install", @@ -47,7 +28,11 @@ def install_requirements( if upgrade: args.append("--upgrade") - for spec in requirements: + for spec in (reqs, *extra): + if spec is None: + continue + if isinstance(spec, os.PathLike): + spec = os.fspath(spec) if os.path.isfile(spec) and spec.endswith(".txt"): args.extend(["-r", spec]) else: @@ -55,12 +40,7 @@ def install_requirements( ec, stdout, stderr = run_uv(*args, env=env, verbose=verbose) if ec == 127: - first, *rest = requirements return _pip.install_requirements( - first, - *rest, - python=python, - env=env, - upgrade=upgrade, + reqs, *extra, python=python, env=env, upgrade=upgrade ) return ec, stdout, stderr