From e21db500a0a19a9ba4ce5ddc6a28c25d1857922f Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Mon, 28 Aug 2023 08:40:00 -0600 Subject: [PATCH 1/6] Switch from flake8 (and isort) to ruff Ruff is fast, and this commit only aims to take advange of that, not to add new rules. --- .flake8 | 5 --- .pre-commit-config.yaml | 19 +++++------- environments/dev-environment.yaml | 6 ---- pyproject.toml | 51 +++++++++++++++++++++++++------ requirements-dev.txt | 7 ----- 5 files changed, 48 insertions(+), 40 deletions(-) delete mode 100644 .flake8 diff --git a/.flake8 b/.flake8 deleted file mode 100644 index b61e46d48..000000000 --- a/.flake8 +++ /dev/null @@ -1,5 +0,0 @@ -[flake8] -ignore = E203, E266, E501, W503, F403, F401 -max-line-length = 89 -max-complexity = 18 -select = B,C,E,F,W,T4,B9 \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8f6c693af..7d51e8ebe 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,9 +8,9 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks: - - id: trailing-whitespace - exclude: "^.*\\.patch$" - - id: check-ast + - id: trailing-whitespace + exclude: "^.*\\.patch$" + - id: check-ast - repo: https://github.com/psf/black rev: 23.7.0 @@ -18,16 +18,11 @@ repos: - id: black language_version: python3 -- repo: https://github.com/pycqa/flake8 - rev: 6.1.0 +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.0.286 hooks: - - id: flake8 - -- repo: https://github.com/pycqa/isort - rev: 5.12.0 - hooks: - - id: isort - args: ["--profile", "black", "--filter-files"] + - id: ruff + args: ["--fix"] - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.5.1 diff --git a/environments/dev-environment.yaml b/environments/dev-environment.yaml index 14a05a878..bee5b5a24 100644 --- a/environments/dev-environment.yaml +++ b/environments/dev-environment.yaml @@ -8,19 +8,13 @@ dependencies: - check-manifest - doctr - filelock -- flake8 -- flake8-builtins -- flake8-comprehensions -- flake8-mutable - python-build - freezegun - isort - mypy - pre-commit -- pylint - pytest - pytest-cov -- pytest-flake8 - pytest-xdist - pytest-timeout - tomli diff --git a/pyproject.toml b/pyproject.toml index 390b7dd7c..7de4748f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -108,16 +108,6 @@ exclude = [ "tests", ] -[tool.isort] -atomic = true -force_grid_wrap = 0 -include_trailing_comma = true -lines_after_imports = 2 -lines_between_types = 1 -multi_line_output = 3 -use_parentheses = true -known_first_party = "attr" - [tool.pytest.ini_options] addopts = "-vrsx -n auto" flake8-max-line-length = 105 @@ -157,3 +147,44 @@ platforms = ["linux-64", "osx-64", "osx-arm64", "win-64", "osx-arm64", "linux-aa # This is necessary to pull in the lockfile/filelock dependency # since we don't handle the optional dependency. cachecontrol-with-filecache = ">=0.12.9" + + +[tool.ruff] +ignore = [ + "E501", + "F401", + "F403", + # Disabled during migration to Ruff: + "A001", + "A002", + "A003", + "C401", + "C405", + "C408", + "C409", + "C413", + "C414", + "C416", + "RUF012", + "RUF015", +] +line-length = 89 +select = [ + "A", # flake8-builtins + # "B", # flake8-bugbear + "C4", # flake8-comprehensions + "C9", # mccabe + "E", # pycodestyle errors + "F", # pyflakes + "I", # isort + "RUF", # ruff rules + "W", # pycodestyle warnings +] + +[tool.ruff.mccabe] +max-complexity = 18 + +[tool.ruff.isort] +lines-after-imports = 2 +lines-between-types = 1 +known-first-party = ["attr"] diff --git a/requirements-dev.txt b/requirements-dev.txt index 62bcf44c4..b4feceec3 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,19 +1,12 @@ black check-manifest doctr -flake8 -flake8-builtins -flake8-comprehensions -flake8-mutable build freezegun -isort mypy pre_commit -pylint pytest pytest-cov -pytest-flake8 pytest-xdist pytest-timeout tomli; python_version<"3.11" From f015fdd3f82c821364d4eca27c94ff47ba5eb08a Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Sun, 27 Aug 2023 18:25:53 -0600 Subject: [PATCH 2/6] Autofix a manageable set of new rules These all felt likely to be uncontroversial choices for letting Ruff autofix some new rules. --- conda_lock/conda_lock.py | 6 +++--- conda_lock/conda_solver.py | 2 +- conda_lock/lockfile/v2prelim/models.py | 4 +++- conda_lock/models/channel.py | 2 +- conda_lock/src_parser/meta_yaml.py | 4 ++-- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/conda_lock/conda_lock.py b/conda_lock/conda_lock.py index ff398783d..d8c4e44b7 100644 --- a/conda_lock/conda_lock.py +++ b/conda_lock/conda_lock.py @@ -241,7 +241,7 @@ def fn_to_dist_name(fn: str) -> str: return fn -def make_lock_files( # noqa: C901 +def make_lock_files( *, conda: PathLike, src_files: List[pathlib.Path], @@ -943,10 +943,10 @@ def _render_lockfile_for_install( if platform not in lock_content.metadata.platforms: suggested_platforms_section = "platforms:\n- " suggested_platforms_section += "\n- ".join( - [platform] + lock_content.metadata.platforms + [platform, *lock_content.metadata.platforms] ) suggested_platform_args = "--platform=" + " --platform=".join( - [platform] + lock_content.metadata.platforms + [platform, *lock_content.metadata.platforms] ) raise PlatformValidationError( f"The lockfile {filename} does not contain a solution for the current " diff --git a/conda_lock/conda_solver.py b/conda_lock/conda_solver.py index eb9fb6ef6..9c414e640 100644 --- a/conda_lock/conda_solver.py +++ b/conda_lock/conda_solver.py @@ -476,7 +476,7 @@ def update_specs_for_arch( proc = subprocess.run( [ str(arg) - for arg in args + ["-p", prefix, "--json", "--dry-run", *to_update] + for arg in [*args, "-p", prefix, "--json", "--dry-run", *to_update] ], env=conda_env_override(platform), stdout=subprocess.PIPE, diff --git a/conda_lock/lockfile/v2prelim/models.py b/conda_lock/lockfile/v2prelim/models.py index 038a7e14e..2cc35c26a 100644 --- a/conda_lock/lockfile/v2prelim/models.py +++ b/conda_lock/lockfile/v2prelim/models.py @@ -7,10 +7,12 @@ GitMeta, HashModel, InputMeta, + LockMeta, + MetadataOption, + TimeMeta, ) from conda_lock.lockfile.v1.models import LockedDependency as LockedDependencyV1 from conda_lock.lockfile.v1.models import Lockfile as LockfileV1 -from conda_lock.lockfile.v1.models import LockMeta, MetadataOption, TimeMeta from conda_lock.models import StrictModel diff --git a/conda_lock/models/channel.py b/conda_lock/models/channel.py index 5cb3b6143..60874f3f4 100644 --- a/conda_lock/models/channel.py +++ b/conda_lock/models/channel.py @@ -143,7 +143,7 @@ def _detect_used_env_var( if value.startswith("$"): return value.lstrip("$").strip("{}") - for suffix in preferred_env_var_suffix + [""]: + for suffix in [*preferred_env_var_suffix, ""]: candidates = {v: k for k, v in os.environ.items() if k.upper().endswith(suffix)} # try first with a simple match key = candidates.get(value) diff --git a/conda_lock/src_parser/meta_yaml.py b/conda_lock/src_parser/meta_yaml.py index 79470027a..5ebd8768f 100644 --- a/conda_lock/src_parser/meta_yaml.py +++ b/conda_lock/src_parser/meta_yaml.py @@ -47,7 +47,7 @@ def __init__( # type: ignore __mod__ = __rmod__ = __pos__ = __neg__ = __call__ = \ __getitem__ = __lt__ = __le__ = __gt__ = __ge__ = \ __complex__ = __pow__ = __rpow__ = \ - lambda self, *args, **kwargs: self._return_undefined(self._undefined_name) # noqa: E122 + lambda self, *args, **kwargs: self._return_undefined(self._undefined_name) # fmt: on # Accessing an attribute of an Undefined variable @@ -60,7 +60,7 @@ def __getattr__(self, k: str) -> "UndefinedNeverFail": # Unlike the methods above, Python requires that these # few methods must always return the correct type - __str__ = __repr__ = lambda self: self._return_value(str()) # type: ignore # noqa: E731 + __str__ = __repr__ = lambda self: self._return_value(str()) # type: ignore __unicode__ = lambda self: self._return_value("") # noqa: E731 __int__ = lambda self: self._return_value(0) # type: ignore # noqa: E731 __float__ = lambda self: self._return_value(0.0) # type: ignore # noqa: E731 From 3aca73e99e92e0cbd5d9784a69ca8e504821b378 Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Sun, 27 Aug 2023 18:43:31 -0600 Subject: [PATCH 3/6] Specify target version --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 7de4748f4..cce0cc543 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -150,6 +150,7 @@ cachecontrol-with-filecache = ">=0.12.9" [tool.ruff] +target-version = "py38" ignore = [ "E501", "F401", From dbd131ebae4dad837cef787c63da030f31e6b21a Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Sun, 27 Aug 2023 18:52:30 -0600 Subject: [PATCH 4/6] Enable and autofix B006 --- conda_lock/conda_lock.py | 4 +++- pyproject.toml | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/conda_lock/conda_lock.py b/conda_lock/conda_lock.py index d8c4e44b7..0c96d4c84 100644 --- a/conda_lock/conda_lock.py +++ b/conda_lock/conda_lock.py @@ -768,7 +768,7 @@ def create_lockfile_from_spec( *, conda: PathLike, spec: LockSpecification, - platforms: List[str] = [], + platforms: Optional[List[str]] = None, lockfile_path: pathlib.Path, update_spec: Optional[UpdateSpecification] = None, metadata_choices: AbstractSet[MetadataOption] = frozenset(), @@ -778,6 +778,8 @@ def create_lockfile_from_spec( """ Solve or update specification """ + if platforms is None: + platforms = [] assert spec.virtual_package_repo is not None virtual_package_channel = spec.virtual_package_repo.channel diff --git a/pyproject.toml b/pyproject.toml index cce0cc543..b6523232f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -173,6 +173,7 @@ line-length = 89 select = [ "A", # flake8-builtins # "B", # flake8-bugbear + "B006", # Do not use mutable data structures for argument defaults "C4", # flake8-comprehensions "C9", # mccabe "E", # pycodestyle errors From 95f64eafa6724ad5745ac1a7ab0e4f148b8d01ad Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Mon, 28 Aug 2023 19:17:02 -0600 Subject: [PATCH 5/6] Restore flake8 and isort in a way that's compatible with ruff I'm sure there are better ways to do this than `# isort: skip_file` and flake8 `per-file-ignores`, but this is what I had time for today :) --- .flake8 | 10 ++++++++++ .pre-commit-config.yaml | 12 ++++++++++++ conda_lock/lockfile/v2prelim/models.py | 5 ++--- pyproject.toml | 14 ++++++++++++++ 4 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 .flake8 diff --git a/.flake8 b/.flake8 new file mode 100644 index 000000000..684ff8df7 --- /dev/null +++ b/.flake8 @@ -0,0 +1,10 @@ +[flake8] +# NOTE: Can ruff replace flake8? See `tool.isort`, these two configs should be +# kept in sync until we pick one. +ignore = E203, E266, E501, W503, F403, F401 +per-file-ignores = + conda_lock/src_parser/meta_yaml.py:E122 + +max-line-length = 89 +max-complexity = 19 +select = B,C,E,F,W,T4,B9 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7d51e8ebe..8c45525aa 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,6 +24,18 @@ repos: - id: ruff args: ["--fix"] +# Ruff should catch (and mostly fix) everything that flake8 and isort do; if +# either of these checks fails, can Ruff's config be updated to catch the same? +- repo: https://github.com/pycqa/flake8 + rev: 6.1.0 + hooks: + - id: flake8 +- repo: https://github.com/pycqa/isort + rev: 5.12.0 + hooks: + - id: isort + args: ["--profile", "black", "--filter-files"] + - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.5.1 hooks: diff --git a/conda_lock/lockfile/v2prelim/models.py b/conda_lock/lockfile/v2prelim/models.py index 2cc35c26a..fd36dbade 100644 --- a/conda_lock/lockfile/v2prelim/models.py +++ b/conda_lock/lockfile/v2prelim/models.py @@ -1,3 +1,4 @@ +# isort: skip_file from collections import defaultdict from typing import ClassVar, Dict, List, Optional @@ -7,12 +8,10 @@ GitMeta, HashModel, InputMeta, - LockMeta, - MetadataOption, - TimeMeta, ) from conda_lock.lockfile.v1.models import LockedDependency as LockedDependencyV1 from conda_lock.lockfile.v1.models import Lockfile as LockfileV1 +from conda_lock.lockfile.v1.models import LockMeta, MetadataOption, TimeMeta from conda_lock.models import StrictModel diff --git a/pyproject.toml b/pyproject.toml index b6523232f..9fb2c0b64 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -108,6 +108,18 @@ exclude = [ "tests", ] +[tool.isort] +# NOTE: Can ruff replace isort? See `tool.ruff.isort`, these two configs should +# be kept in sync until we pick one. +atomic = true +force_grid_wrap = 0 +include_trailing_comma = true +lines_after_imports = 2 +lines_between_types = 1 +multi_line_output = 3 +use_parentheses = true +known_first_party = "attr" + [tool.pytest.ini_options] addopts = "-vrsx -n auto" flake8-max-line-length = 105 @@ -187,6 +199,8 @@ select = [ max-complexity = 18 [tool.ruff.isort] +# NOTE: Can ruff replace isort? See `tool.isort`, these two configs should be +# kept in sync until we pick one. lines-after-imports = 2 lines-between-types = 1 known-first-party = ["attr"] From 52cb31b69a87ca879c5c3e8228e2125c16932bef Mon Sep 17 00:00:00 2001 From: Matt Fisher Date: Tue, 29 Aug 2023 18:22:48 -0600 Subject: [PATCH 6/6] Add context to isort skip comment --- conda_lock/lockfile/v2prelim/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/conda_lock/lockfile/v2prelim/models.py b/conda_lock/lockfile/v2prelim/models.py index fd36dbade..71ad4352f 100644 --- a/conda_lock/lockfile/v2prelim/models.py +++ b/conda_lock/lockfile/v2prelim/models.py @@ -1,4 +1,6 @@ # isort: skip_file +# TODO: Remove the isort skip comment above if/when isort is no longer used. This skip +# exists because isort and ruff disagree about how to sort the imports in this file. from collections import defaultdict from typing import ClassVar, Dict, List, Optional