From fdca541068652f560a1fd5d7f6af932ab0d3d412 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Fri, 17 Feb 2023 09:49:27 -0500 Subject: [PATCH 1/2] chore: move to using Ruff Signed-off-by: Henry Schreiner --- .flake8 | 6 ---- .pre-commit-config.yaml | 44 +++++----------------------- pyproject.toml | 39 ++++++++++++++++++++++-- src/vector/_methods.py | 4 +-- src/vector/backends/_numba.py | 4 +-- src/vector/backends/_numba_object.py | 3 +- tests/test_compute_features.py | 6 ++-- 7 files changed, 51 insertions(+), 55 deletions(-) delete mode 100644 .flake8 diff --git a/.flake8 b/.flake8 deleted file mode 100644 index f27941a2..00000000 --- a/.flake8 +++ /dev/null @@ -1,6 +0,0 @@ -[flake8] -extend-select = B902, B903, B904 -extend-ignore = E501, E203, D103, D102, D101, D100, D107, D105, D205, D400, D401, D104, D412, B009 -per-file-ignores = - tests/*: T - noxfile.py: T diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 84cae3ac..65b7e666 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,13 +3,6 @@ ci: autofix_commit_msg: "style: pre-commit fixes" repos: - - repo: https://github.com/asottile/pyupgrade - rev: v3.3.1 - hooks: - - id: pyupgrade - args: &pyupver - - --py37-plus - - repo: https://github.com/psf/black rev: 23.1.0 hooks: @@ -30,11 +23,11 @@ repos: - id: requirements-txt-fixer - id: trailing-whitespace - - repo: https://github.com/PyCQA/isort - rev: 5.12.0 + - repo: https://github.com/charliermarsh/ruff-pre-commit + rev: "v0.0.247" hooks: - - id: isort - args: ["-a", "from __future__ import annotations"] + - id: ruff + args: ["--fix", "--show-fixes"] - repo: https://github.com/tox-dev/pyproject-fmt rev: "0.8.0" @@ -48,18 +41,9 @@ repos: files: src args: [] additional_dependencies: - - numpy~=1.24.0 + - numpy - packaging - - repo: https://github.com/PyCQA/flake8 - rev: 6.0.0 - hooks: - - id: flake8 - additional_dependencies: - - flake8-bugbear - - flake8-docstrings - - flake8-print - - repo: https://github.com/codespell-project/codespell rev: v2.2.2 hooks: @@ -77,35 +61,21 @@ repos: hooks: - id: blacken-docs args: ["-E"] - additional_dependencies: [black==22.8.0] + additional_dependencies: [black==23.1.0] - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.10.0 hooks: - - id: python-check-blanket-noqa - id: python-check-blanket-type-ignore exclude: ^src/vector/backends/_numba_object.py$ - - id: python-no-log-warn - - id: python-no-eval - - id: python-use-type-annotations - id: rst-backticks - id: rst-directive-colons - id: rst-inline-touching-normal - - repo: https://github.com/asottile/yesqa - rev: "v1.4.0" - hooks: - - id: yesqa - - repo: https://github.com/nbQA-dev/nbQA rev: 1.6.1 hooks: - id: nbqa-pyupgrade - args: *pyupver + args: ["--py37-plus"] - id: nbqa-isort args: ["--float-to-top"] - - - repo: https://github.com/hadialqattan/pycln - rev: "v2.1.3" - hooks: - - id: pycln diff --git a/pyproject.toml b/pyproject.toml index 99afc754..e2f31171 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -119,9 +119,6 @@ filterwarnings = [ "ignore::UserWarning", ] -[tool.isort] -profile = "black" - [tool.pylint] master.py-version = "3.7" reports.output-format = "colorized" @@ -204,3 +201,39 @@ module = [ ignore_missing_imports = true disallow_untyped_defs = false disallow_untyped_calls = false + +[tool.ruff] +select = [ + "E", "F", "W", # flake8 + "B", "B904", # flake8-bugbear + "I", # isort + "C4", # flake8-comprehensions + "ISC", # flake8-implicit-str-concat + "PGH", # pygrep-hooks + "PIE", # flake8-pie + "PL", # pylint + "PT", # flake8-pytest-style + "RUF", # Ruff-specific + "SIM", # flake8-simplify + "T20", # flake8-print + "UP", # pyupgrade + "YTT", # flake8-2020 +] +extend-ignore = [ + "PLR", # Design related pylint codes + "E501", # Line too long +] +target-version = "py37" +typing-modules = ["vector._typeutils"] +src = ["src"] +unfixable = [ + "T20", # Removes print statements + "F841", # Removes unused variables +] +exclude = [] +isort.required-imports = ["from __future__ import annotations"] + +[tool.ruff.per-file-ignores] +"noxfile.py" = ["T20"] +"tests/*" = ["T20"] +"src/vector/backends/_numba_object.py" = ["PGH003"] diff --git a/src/vector/_methods.py b/src/vector/_methods.py index 20dcf4c3..28c179fc 100644 --- a/src/vector/_methods.py +++ b/src/vector/_methods.py @@ -4026,9 +4026,7 @@ def _handler_of(*objects: VectorProtocol) -> VectorProtocol: for obj in objects: if not isinstance(obj, Vector): continue - if handler is None: - handler = obj - elif _get_handler_index(obj) > _get_handler_index(handler): + if handler is None or _get_handler_index(obj) > _get_handler_index(handler): handler = obj assert handler is not None diff --git a/src/vector/backends/_numba.py b/src/vector/backends/_numba.py index d576d59c..69751f0e 100644 --- a/src/vector/backends/_numba.py +++ b/src/vector/backends/_numba.py @@ -44,8 +44,8 @@ numba.extending.register_jitable(obj) registered.add(obj) - # MyPy doesn't know that submodule contains dispatch_map, so getattr - for key, value in getattr(submodule, "dispatch_map").items(): + # MyPy doesn't know that submodule contains dispatch_map + for key, value in submodule.dispatch_map.items(): function, *returns = value if function not in registered: numba.extending.register_jitable(function) diff --git a/src/vector/backends/_numba_object.py b/src/vector/backends/_numba_object.py index dfa0403e..3352552d 100644 --- a/src/vector/backends/_numba_object.py +++ b/src/vector/backends/_numba_object.py @@ -4,13 +4,14 @@ # or https://github.com/scikit-hep/vector for details. # type: ignore -from __future__ import annotations """ Implements VectorObjects in Numba. Every function should be made usable in Numba. """ +from __future__ import annotations + import operator import numba diff --git a/tests/test_compute_features.py b/tests/test_compute_features.py index ccdddf57..efa16c4f 100644 --- a/tests/test_compute_features.py +++ b/tests/test_compute_features.py @@ -32,6 +32,7 @@ from __future__ import annotations import collections +import contextlib import inspect import sys @@ -117,10 +118,9 @@ def analyze_function(function): closure = dict(function.__globals__) if function.__closure__ is not None: for var, cell in zip(function.__code__.co_freevars, function.__closure__): - try: + # the cell has not been filled yet, so ignore it + with contextlib.suppress(ValueError): closure[var] = cell.cell_contents - except ValueError: - pass # the cell has not been filled yet, so ignore it analyze_code(function.__code__, Context(function.__name__, closure)) analyze_function.done.add(function) From 44f5cf259cef799364ecab4fb24c0bfc8f415a73 Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Fri, 17 Feb 2023 10:07:15 -0500 Subject: [PATCH 2/2] style: make Ruff happy Signed-off-by: Henry Schreiner --- pyproject.toml | 1 + src/vector/__init__.py | 5 +- src/vector/_methods.py | 4 +- src/vector/backends/_numba.py | 3 +- src/vector/backends/awkward.py | 12 ----- src/vector/backends/numpy.py | 75 ++--------------------------- src/vector/backends/object.py | 45 ++--------------- tests/backends/test_numba_object.py | 6 +-- tests/test_compute_features.py | 7 +-- tests/test_issues.py | 5 +- 10 files changed, 19 insertions(+), 144 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e2f31171..e533f046 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -237,3 +237,4 @@ isort.required-imports = ["from __future__ import annotations"] "noxfile.py" = ["T20"] "tests/*" = ["T20"] "src/vector/backends/_numba_object.py" = ["PGH003"] +"tests/backends/test_operators.py" = ["SIM201", "SIM202"] diff --git a/src/vector/__init__.py b/src/vector/__init__.py index 7c537d1a..201e634e 100644 --- a/src/vector/__init__.py +++ b/src/vector/__init__.py @@ -32,9 +32,8 @@ Vector4D, dim, ) -from vector.backends.awkward_constructors import Array +from vector.backends.awkward_constructors import Array, zip from vector.backends.awkward_constructors import Array as awk -from vector.backends.awkward_constructors import zip from vector.backends.numpy import ( MomentumNumpy2D, MomentumNumpy3D, @@ -43,8 +42,8 @@ VectorNumpy2D, VectorNumpy3D, VectorNumpy4D, + array, ) -from vector.backends.numpy import array from vector.backends.numpy import array as arr from vector.backends.object import ( MomentumObject2D, diff --git a/src/vector/_methods.py b/src/vector/_methods.py index 28c179fc..505704c1 100644 --- a/src/vector/_methods.py +++ b/src/vector/_methods.py @@ -2962,7 +2962,7 @@ def to_Vector2D(self) -> VectorProtocolPlanar: def to_Vector3D(self) -> VectorProtocolSpatial: return self._wrap_result( type(self), - self.azimuthal.elements + (0,), + (*self.azimuthal.elements, 0), [_aztype(self), LongitudinalZ, None], 1, ) @@ -2970,7 +2970,7 @@ def to_Vector3D(self) -> VectorProtocolSpatial: def to_Vector4D(self) -> VectorProtocolLorentz: return self._wrap_result( type(self), - self.azimuthal.elements + (0, 0), + (*self.azimuthal.elements, 0, 0), [_aztype(self), LongitudinalZ, TemporalT], 1, ) diff --git a/src/vector/backends/_numba.py b/src/vector/backends/_numba.py index 69751f0e..fc65479a 100644 --- a/src/vector/backends/_numba.py +++ b/src/vector/backends/_numba.py @@ -44,11 +44,10 @@ numba.extending.register_jitable(obj) registered.add(obj) - # MyPy doesn't know that submodule contains dispatch_map for key, value in submodule.dispatch_map.items(): function, *returns = value if function not in registered: numba.extending.register_jitable(function) registered.add(function) - numba_modules[groupname][modname][key] = tuple([function] + returns) + numba_modules[groupname][modname][key] = (function, *returns) diff --git a/src/vector/backends/awkward.py b/src/vector/backends/awkward.py index cc40dfb4..39e46290 100644 --- a/src/vector/backends/awkward.py +++ b/src/vector/backends/awkward.py @@ -1252,8 +1252,6 @@ class VectorRecord2D(VectorAwkward2D, ak.Record): # type: ignore[misc] for the corresponding ``Vector`` and ``Momentum`` classes. """ - pass - behavior["Vector2D"] = VectorRecord2D @@ -1288,8 +1286,6 @@ class VectorRecord3D(VectorAwkward3D, ak.Record): # type: ignore[misc] for the corresponding ``Vector`` and ``Momentum`` classes. """ - pass - behavior["Vector3D"] = VectorRecord3D @@ -1324,8 +1320,6 @@ class VectorRecord4D(VectorAwkward4D, ak.Record): # type: ignore[misc] for the corresponding ``Vector`` and ``Momentum`` classes. """ - pass - behavior["Vector4D"] = VectorRecord4D @@ -1360,8 +1354,6 @@ class MomentumRecord2D(MomentumAwkward2D, ak.Record): # type: ignore[misc] for the corresponding ``Momentum`` and ``Vector`` classes. """ - pass - behavior["Momentum2D"] = MomentumRecord2D @@ -1395,8 +1387,6 @@ class MomentumRecord3D(MomentumAwkward3D, ak.Record): # type: ignore[misc] for the corresponding ``Momentum`` and ``Vector`` classes. """ - pass - behavior["Momentum3D"] = MomentumRecord3D @@ -1430,8 +1420,6 @@ class MomentumRecord4D(MomentumAwkward4D, ak.Record): # type: ignore[misc] for the corresponding ``Momentum`` and ``Vector`` classes. """ - pass - behavior["Momentum4D"] = MomentumRecord4D diff --git a/src/vector/backends/numpy.py b/src/vector/backends/numpy.py index 20cfd284..107ec1f4 100644 --- a/src/vector/backends/numpy.py +++ b/src/vector/backends/numpy.py @@ -1029,6 +1029,7 @@ def _wrap_result( elif ( len(returns) == 1 + or (len(returns) == 2 and returns[1] is None) and isinstance(returns[0], type) and issubclass(returns[0], Azimuthal) ): @@ -1044,55 +1045,11 @@ def _wrap_result( return out.view(cls.ProjectionClass2D) elif ( - len(returns) == 2 - and isinstance(returns[0], type) - and issubclass(returns[0], Azimuthal) - and returns[1] is None - ): - result = _toarrays(result) - dtype = [ - (name, result[i].dtype) - for i, name in enumerate(_coordinate_class_to_names[returns[0]]) - ] - - out = numpy.empty(_shape_of(result), dtype=dtype) - for i, name in enumerate(_coordinate_class_to_names[returns[0]]): - out[name] = result[i] - return out.view(cls.ProjectionClass2D) - - elif ( - len(returns) == 2 - and isinstance(returns[0], type) - and issubclass(returns[0], Azimuthal) - and isinstance(returns[1], type) - and issubclass(returns[1], Longitudinal) - ): - result = _toarrays(result) - dtype = [] - i = 0 - for name in _coordinate_class_to_names[returns[0]]: - dtype.append((name, result[i].dtype)) - i += 1 - for name in _coordinate_class_to_names[returns[1]]: - dtype.append((name, result[i].dtype)) - i += 1 - out = numpy.empty(_shape_of(result), dtype=dtype) - i = 0 - for name in _coordinate_class_to_names[returns[0]]: - out[name] = result[i] - i += 1 - for name in _coordinate_class_to_names[returns[1]]: - out[name] = result[i] - i += 1 - return out.view(cls.ProjectionClass3D) - - elif ( - len(returns) == 3 + (len(returns) == 2 or (len(returns) == 3 and returns[2] is None)) and isinstance(returns[0], type) and issubclass(returns[0], Azimuthal) and isinstance(returns[1], type) and issubclass(returns[1], Longitudinal) - and returns[2] is None ): result = _toarrays(result) dtype = [] @@ -1343,6 +1300,7 @@ def _wrap_result( elif ( len(returns) == 2 + or (len(returns) == 3 and returns[2] is None) and isinstance(returns[0], type) and issubclass(returns[0], Azimuthal) and isinstance(returns[1], type) @@ -1367,33 +1325,6 @@ def _wrap_result( i += 1 return out.view(cls.ProjectionClass3D) - elif ( - len(returns) == 3 - and isinstance(returns[0], type) - and issubclass(returns[0], Azimuthal) - and isinstance(returns[1], type) - and issubclass(returns[1], Longitudinal) - and returns[2] is None - ): - result = _toarrays(result) - dtype = [] - i = 0 - for name in _coordinate_class_to_names[returns[0]]: - dtype.append((name, result[i].dtype)) - i += 1 - for name in _coordinate_class_to_names[returns[1]]: - dtype.append((name, result[i].dtype)) - i += 1 - out = numpy.empty(_shape_of(result), dtype=dtype) - i = 0 - for name in _coordinate_class_to_names[returns[0]]: - out[name] = result[i] - i += 1 - for name in _coordinate_class_to_names[returns[1]]: - out[name] = result[i] - i += 1 - return out.view(cls.ProjectionClass3D) - elif ( len(returns) == 3 and isinstance(returns[0], type) diff --git a/src/vector/backends/object.py b/src/vector/backends/object.py index 79648cd5..8ae6b729 100644 --- a/src/vector/backends/object.py +++ b/src/vector/backends/object.py @@ -70,26 +70,18 @@ class CoordinatesObject: """Coordinates class for the Object backend.""" - pass - class AzimuthalObject(CoordinatesObject, Azimuthal): """Azimuthal class for the Object backend.""" - pass - class LongitudinalObject(CoordinatesObject, Longitudinal): """Longitudinal class for the Object backend.""" - pass - class TemporalObject(CoordinatesObject, Temporal): """Temporal class for the Object backend.""" - pass - class TupleXY(typing.NamedTuple): """``x`` and ``y`` coordinates as a ``NamedTuple``.""" @@ -733,7 +725,7 @@ def _wrap_result( return result elif ( - len(returns) == 1 + (len(returns) == 1 or (len(returns) == 2 and returns[1] is None)) and isinstance(returns[0], type) and issubclass(returns[0], Azimuthal) ): @@ -742,31 +734,11 @@ def _wrap_result( elif ( len(returns) == 2 - and isinstance(returns[0], type) - and issubclass(returns[0], Azimuthal) - and returns[1] is None - ): - azcoords = _coord_object_type[returns[0]](result[0], result[1]) - return cls.ProjectionClass2D(azimuthal=azcoords) - - elif ( - len(returns) == 2 - and isinstance(returns[0], type) - and issubclass(returns[0], Azimuthal) - and isinstance(returns[1], type) - and issubclass(returns[1], Longitudinal) - ): - azcoords = _coord_object_type[returns[0]](result[0], result[1]) - lcoords = _coord_object_type[returns[1]](result[2]) - return cls.ProjectionClass3D(azimuthal=azcoords, longitudinal=lcoords) - - elif ( - len(returns) == 3 + or (len(returns) == 3 and returns[2] is None) and isinstance(returns[0], type) and issubclass(returns[0], Azimuthal) and isinstance(returns[1], type) and issubclass(returns[1], Longitudinal) - and returns[2] is None ): azcoords = _coord_object_type[returns[0]](result[0], result[1]) lcoords = _coord_object_type[returns[1]](result[2]) @@ -1165,6 +1137,7 @@ def _wrap_result( elif ( len(returns) == 2 + or (len(returns) == 3 and returns[2] is None) and isinstance(returns[0], type) and issubclass(returns[0], Azimuthal) and isinstance(returns[1], type) @@ -1174,18 +1147,6 @@ def _wrap_result( lcoords = _coord_object_type[returns[1]](result[2]) return cls.ProjectionClass3D(azimuthal=azcoords, longitudinal=lcoords) - elif ( - len(returns) == 3 - and isinstance(returns[0], type) - and issubclass(returns[0], Azimuthal) - and isinstance(returns[1], type) - and issubclass(returns[1], Longitudinal) - and returns[2] is None - ): - azcoords = _coord_object_type[returns[0]](result[0], result[1]) - lcoords = _coord_object_type[returns[1]](result[2]) - return cls.ProjectionClass3D(azimuthal=azcoords, longitudinal=lcoords) - elif ( len(returns) == 3 and isinstance(returns[0], type) diff --git a/tests/backends/test_numba_object.py b/tests/backends/test_numba_object.py index 68dd1985..cfb75053 100644 --- a/tests/backends/test_numba_object.py +++ b/tests/backends/test_numba_object.py @@ -694,7 +694,7 @@ def momentum_rhophizt(): assert out.pt == pytest.approx(2) assert out.phi == pytest.approx(3.3) assert out.z == pytest.approx(5) - assert out.E == pytest.approx(10) + assert out.E == pytest.approx(10) # noqa: SIM300 def test_property_float(): @@ -1368,11 +1368,11 @@ def get_true(v): def test_operator_truth(): @numba.njit def get_true(v): - return True if v else False + return True if v else False # noqa: SIM210 @numba.njit def get_false(v): - return False if v else True + return False if v else True # noqa: SIM211 assert not get_true(vector.obj(x=0, y=0)) assert get_true(vector.obj(x=0, y=0.1)) diff --git a/tests/test_compute_features.py b/tests/test_compute_features.py index efa16c4f..f8f96edc 100644 --- a/tests/test_compute_features.py +++ b/tests/test_compute_features.py @@ -99,7 +99,7 @@ @pytest.mark.skipif(is_unsupported, reason=unsupported_message) -@pytest.mark.slow +@pytest.mark.slow() @pytest.mark.parametrize("signature", functions.keys()) def test(signature): analyze_function(functions[signature]) @@ -277,10 +277,7 @@ def analyze_expression(node, context): analyze_callable(expr(node[0]), context) for argument in node[1:-1]: - if argument.kind == "pos_arg": - expr_arg = argument[0] - else: - expr_arg = argument + expr_arg = argument[0] if argument.kind == "pos_arg" else argument assert expr_arg.kind == "expr", "only positional arguments" analyze_expression(expr(expr_arg), context) diff --git a/tests/test_issues.py b/tests/test_issues.py index 2f5a292f..a69e5daf 100644 --- a/tests/test_issues.py +++ b/tests/test_issues.py @@ -44,7 +44,6 @@ def repro(generator_like_jet_constituents): else os.path.join("tests", "samples", "issue-161-v2.pkl") ) - f = open(file_path, "rb") - a = ak.from_buffers(*pickle.load(f)) - f.close() + with open(file_path, "rb") as f: + a = ak.from_buffers(*pickle.load(f)) repro(generator_like_jet_constituents=a.constituents)