diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..69fc5b1e --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,13 @@ +name: Lint + +on: [push, pull_request, workflow_dispatch] + +permissions: {} + +jobs: + lint: + name: Check code with ruff + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: tox-dev/action-pre-commit-uv@v1 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..d468878b --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,21 @@ +repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.12.11 + hooks: + - id: ruff-check + name: Run Ruff (lint) + args: [--exit-non-zero-on-fix] + exclude: ^pyperformance/data-files/ + + - repo: https://github.com/tox-dev/pyproject-fmt + rev: v2.6.0 + hooks: + - id: pyproject-fmt + exclude: ^pyperformance/data-files/ + + - repo: https://github.com/abravalheri/validate-pyproject + rev: v0.24.1 + hooks: + - id: validate-pyproject + exclude: ^pyperformance/data-files/ + diff --git a/pyperformance/_benchmark.py b/pyperformance/_benchmark.py index bade89ea..7aa9619f 100644 --- a/pyperformance/_benchmark.py +++ b/pyperformance/_benchmark.py @@ -218,7 +218,7 @@ def _run_perf_script(python, runscript, runid, *, '--output', tmp, ] if pyperf_opts and '--copy-env' in pyperf_opts: - argv, env = _prep_cmd(python, runscript, opts, runid, NOOP) + argv, env = _prep_cmd(python, runscript, opts, runid, lambda name: None) else: opts, inherit_envvar = _resolve_restricted_opts(opts) argv, env = _prep_cmd(python, runscript, opts, runid, inherit_envvar) diff --git a/pyperformance/_benchmark_metadata.py b/pyperformance/_benchmark_metadata.py index cae97dc1..4a6b8944 100644 --- a/pyperformance/_benchmark_metadata.py +++ b/pyperformance/_benchmark_metadata.py @@ -194,7 +194,6 @@ def _resolve(project, tool, filename): if target is None: target = field if field == 'url': - repo = project.get('urls', {}).get('repository') raise NotImplementedError elif not resolved.get(target): value = project.get(field) diff --git a/pyperformance/_manifest.py b/pyperformance/_manifest.py index cd9479ef..582ed4e8 100644 --- a/pyperformance/_manifest.py +++ b/pyperformance/_manifest.py @@ -343,13 +343,12 @@ def _parse_metafile(metafile, name): def _parse_groups_section(lines): - for name in seclines: + for name in lines: _utils.check_name(name) yield name def _parse_group_section(lines): - yielded = False for line in lines: if line.startswith('-'): # Exclude a benchmark or group. @@ -363,7 +362,6 @@ def _parse_group_section(lines): name = line _benchmark.check_name(name) yield op, name - yielded = True def _get_tags(benchmarks): diff --git a/pyperformance/_pyproject_toml.py b/pyperformance/_pyproject_toml.py index 637c5888..06fc4096 100644 --- a/pyperformance/_pyproject_toml.py +++ b/pyperformance/_pyproject_toml.py @@ -100,7 +100,7 @@ def load_pyproject_toml(filename, *, name=None, tools=None, requirefiles=True): def _check_relfile(relname, rootdir, kind): if os.path.isabs(relname): - raise ValuError(f'{relname!r} is absolute, expected relative') + raise ValueError(f'{relname!r} is absolute, expected relative') actual = os.path.join(rootdir, relname) if kind == 'dir': if not os.path.isdir(actual): @@ -122,12 +122,9 @@ def _check_file_or_text(table, rootdir, requirefiles, extra=None): if 'file' in table: if 'text' in table: - raise ValueError(f'"file" and "text" are mutually exclusive') + raise ValueError('"file" and "text" are mutually exclusive') kind = 'file' if requirefiles else None _check_relfile(table['file'], rootdir, kind) - else: - text = table['text'] - # XXX Validate it? def _normalize_project(data, rootdir, name, requirefiles, **_ignored): diff --git a/pyperformance/_utils.py b/pyperformance/_utils.py index 908adc26..1e3349e6 100644 --- a/pyperformance/_utils.py +++ b/pyperformance/_utils.py @@ -6,7 +6,6 @@ 'check_dir', # platform 'MS_WINDOWS', - 'run_command', # misc 'check_name', 'parse_name_pattern', diff --git a/pyperformance/run.py b/pyperformance/run.py index b535f96a..98d80b16 100644 --- a/pyperformance/run.py +++ b/pyperformance/run.py @@ -1,6 +1,7 @@ from collections import namedtuple import hashlib import json +import os import sys import time import traceback diff --git a/pyperformance/tests/data/bm_local_wheel/pyproject.toml b/pyperformance/tests/data/bm_local_wheel/pyproject.toml index 2710ced7..453345d4 100644 --- a/pyperformance/tests/data/bm_local_wheel/pyproject.toml +++ b/pyperformance/tests/data/bm_local_wheel/pyproject.toml @@ -1,9 +1,20 @@ [project] -name = "pyperformance_bm_local_wheel" -requires-python = ">=3.7" -dependencies = ["pyperf"] -urls = {repository = "https://github.com/python/pyperformance"} +name = "pyperformance-bm-local-wheel" version = "1.0" +requires-python = ">=3.7" +classifiers = [ + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", +] +dependencies = [ "pyperf" ] +urls = { repository = "https://github.com/python/pyperformance" } + [tool.pyperformance] name = "local_wheel" diff --git a/pyperformance/tests/test_commands.py b/pyperformance/tests/test_commands.py index 42574f26..4eb0978c 100644 --- a/pyperformance/tests/test_commands.py +++ b/pyperformance/tests/test_commands.py @@ -102,13 +102,13 @@ def div(): print() def expect_success(*args): - text = self.run_pyperformance( + self.run_pyperformance( *args, capture=None, ) def expect_failure(*args): - text = self.run_pyperformance( + self.run_pyperformance( *args, capture=None, exitcode=1, @@ -148,7 +148,7 @@ def test_run_and_show(self): # --debug-single-value: benchmark results don't matter, we only # check that running benchmarks don't fail. # XXX Capture and check the output. - text = self.run_pyperformance( + self.run_pyperformance( 'run', '-b', 'all', '--debug-single-value', diff --git a/pyproject.toml b/pyproject.toml index da88a7b9..888f6f15 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,51 +38,80 @@ # - git push [build-system] -requires = ["setuptools >= 61"] build-backend = "setuptools.build_meta" +requires = [ "setuptools>=61" ] + [project] name = "pyperformance" -dynamic = ["version"] -license = {text = "MIT"} description = "Python benchmark suite" readme = "README.rst" -urls = {Homepage = "https://github.com/python/pyperformance"} -authors= [{name = "Collin Winter"}, {name= "Jeffrey Yasskin"}] +license = { text = "MIT" } +authors = [ { name = "Collin Winter" }, { name = "Jeffrey Yasskin" } ] +requires-python = ">=3.9" classifiers = [ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: MIT License', - 'Natural Language :: English', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python', + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", ] -requires-python = ">=3.9" +dynamic = [ "version" ] dependencies = [ - "pyperf", - "tomli; python_version < '3.11'", - "packaging", + "packaging", + "pyperf", + "tomli; python_version<'3.11'", ] -[project.optional-dependencies] -dev = [ - 'tox', - 'mypy==1.2.0', - 'tomli', # Needed even on 3.11+ for typechecking with mypy +optional-dependencies.dev = [ + "mypy==1.2", + "tomli", # Needed even on 3.11+ for typechecking with mypy + "tox", ] - -[project.scripts] -pyperformance = "pyperformance.cli:main" +urls = { Homepage = "https://github.com/python/pyperformance" } +scripts.pyperformance = "pyperformance.cli:main" [tool.setuptools] include-package-data = true [tool.setuptools.packages] -find = {} # Scanning implicit namespaces is active by default +find = {} # Scanning implicit namespaces is active by default [tool.setuptools.dynamic] -version = {attr = "pyperformance.__version__"} +version = { attr = "pyperformance.__version__" } + +[tool.ruff] +target-version = "py310" + +exclude = [ + "pyperformance/data-files/", +] + +fix = true + +lint.select = [ + "E", # pycodestyle errors + "F", # pyflakes errors +] +lint.ignore = [ + "E402", # module level import not at top of file + "E501", # line too long + "E701", # multiple statements on one line (colon) + "E722", # do not use bare 'except' + "E741", # ambiguous variable name + "F405", # name may be undefined, or defined from star imports +] + +[tool.pyproject-fmt] +max_supported_python = "3.14" [tool.mypy] python_version = "3.9" @@ -94,11 +123,11 @@ warn_redundant_casts = true warn_unused_ignores = true warn_unused_configs = true files = [ - 'pyperformance/', + 'pyperformance/', ] exclude = [ - 'pyperformance/data-files/', - 'pyperformance/tests/' + 'pyperformance/data-files/', + 'pyperformance/tests/', ] [[tool.mypy.overrides]]