diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d914c4c0..53371397 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,25 +1,27 @@ +ci: + autoupdate_schedule: quarterly repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.6.0 hooks: - - id: check-yaml - - id: end-of-file-fixer - - id: trailing-whitespace + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace - repo: https://github.com/python-jsonschema/check-jsonschema rev: 0.27.3 hooks: - - id: check-github-workflows - args: ["--verbose"] - - repo: https://github.com/psf/black - rev: 23.9.1 - hooks: - - id: black + - id: check-github-workflows + args: ["--verbose"] - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.291 + rev: v0.5.0 hooks: - - id: ruff + # Run the linter. + - id: ruff + args: [ --fix ] + # Run the formatter. + - id: ruff-format - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.5.1 hooks: - - id: mypy - exclude: 'docs/.' + - id: mypy + exclude: 'docs/.' diff --git a/pyproject.toml b/pyproject.toml index 4f018f54..9fb47992 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,10 +48,6 @@ version = {attr = "rtree.__version__"} [tool.setuptools.package-data] rtree = ["py.typed"] -[tool.black] -target-version = ["py38", "py39", "py310", "py311", "py312"] -color = true - [tool.cibuildwheel] build = "cp38-*" build-verbosity = 3 @@ -96,11 +92,14 @@ exclude_lines = [ "@overload", ] -[tool.isort] -profile = "black" -known_first_party = ["rtree"] -skip_gitignore = true -color_output = true +[tool.ruff.lint] +select = [ + "E", "W", # pycodestyle + "F", # Pyflakes + "UP", # pyupgrade + "I", # isort + "NPY", # NumPy-specific +] [tool.mypy] exclude = ["docs", "build"] diff --git a/rtree/__init__.py b/rtree/__init__.py index a9c0e088..c7bd14b4 100644 --- a/rtree/__init__.py +++ b/rtree/__init__.py @@ -4,6 +4,7 @@ Rtree provides Python bindings to libspatialindex for quick hyperrectangular intersection queries. """ + from __future__ import annotations __version__ = "1.2.0" diff --git a/rtree/exceptions.py b/rtree/exceptions.py index e835f2d5..5cc37007 100644 --- a/rtree/exceptions.py +++ b/rtree/exceptions.py @@ -3,4 +3,5 @@ class RTreeError(Exception): "RTree exception, indicates a RTree-related error." + pass diff --git a/rtree/finder.py b/rtree/finder.py index 59b74e4a..fd3450d0 100644 --- a/rtree/finder.py +++ b/rtree/finder.py @@ -1,6 +1,7 @@ """ Locate `libspatialindex` shared library and header files. """ + from __future__ import annotations import ctypes diff --git a/rtree/index.py b/rtree/index.py index f063eff4..59b5f4fa 100644 --- a/rtree/index.py +++ b/rtree/index.py @@ -246,7 +246,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: d = os.path.dirname(p) if not os.access(d, os.W_OK): - message = "Unable to open file '%s' for index storage" % f + message = f"Unable to open file '{f}' for index storage" raise OSError(message) elif storage: self.properties.storage = RT_Custom @@ -296,9 +296,7 @@ def __len__(self) -> int: return 0 def __repr__(self) -> str: - return "rtree.index.Index(bounds={}, size={})".format( - self.bounds, self.get_size() - ) + return f"rtree.index.Index(bounds={self.bounds}, size={self.get_size()})" def __getstate__(self) -> dict[str, Any]: state = self.__dict__.copy() @@ -575,18 +573,17 @@ def _countTP( return p_num_results.value @overload - def contains(self, coordinates: Any, objects: Literal[True]) -> Iterator[Item]: - ... + def contains(self, coordinates: Any, objects: Literal[True]) -> Iterator[Item]: ... @overload def contains( self, coordinates: Any, objects: Literal[False] = False - ) -> Iterator[int] | None: - ... + ) -> Iterator[int] | None: ... @overload - def contains(self, coordinates: Any, objects: Literal["raw"]) -> Iterator[object]: - ... + def contains( + self, coordinates: Any, objects: Literal["raw"] + ) -> Iterator[object]: ... def contains( self, coordinates: Any, objects: bool | Literal["raw"] = False @@ -724,20 +721,19 @@ def __or__(self, other: Index) -> Index: return new_idx @overload - def intersection(self, coordinates: Any, objects: Literal[True]) -> Iterator[Item]: - ... + def intersection( + self, coordinates: Any, objects: Literal[True] + ) -> Iterator[Item]: ... @overload def intersection( self, coordinates: Any, objects: Literal[False] = False - ) -> Iterator[int]: - ... + ) -> Iterator[int]: ... @overload def intersection( self, coordinates: Any, objects: Literal["raw"] - ) -> Iterator[object]: - ... + ) -> Iterator[object]: ... def intersection( self, coordinates: Any, objects: bool | Literal["raw"] = False @@ -952,20 +948,17 @@ def _nearest_obj(self, coordinates, num_results, objects): @overload def nearest( self, coordinates: Any, num_results: int, objects: Literal[True] - ) -> Iterator[Item]: - ... + ) -> Iterator[Item]: ... @overload def nearest( self, coordinates: Any, num_results: int, objects: Literal[False] = False - ) -> Iterator[int]: - ... + ) -> Iterator[int]: ... @overload def nearest( self, coordinates: Any, num_results: int, objects: Literal["raw"] - ) -> Iterator[object]: - ... + ) -> Iterator[object]: ... def nearest( self, @@ -2104,7 +2097,7 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: or isinstance(args[0], bytes) or isinstance(args[0], ICustomStorage) ): - raise ValueError("%s supports only in-memory indexes" % self.__class__) + raise ValueError(f"{self.__class__} supports only in-memory indexes") self._objects: dict[int, tuple[int, object]] = {} return super().__init__(*args, **kwargs) @@ -2171,14 +2164,12 @@ def insert(self, obj: object, coordinates: Any) -> None: # type: ignore[overrid add = insert # type: ignore[assignment] @overload # type: ignore[override] - def intersection(self, coordinates: Any, bbox: Literal[True]) -> Iterator[Item]: - ... + def intersection(self, coordinates: Any, bbox: Literal[True]) -> Iterator[Item]: ... @overload def intersection( self, coordinates: Any, bbox: Literal[False] = False - ) -> Iterator[object]: - ... + ) -> Iterator[object]: ... def intersection( self, coordinates: Any, bbox: bool = False @@ -2252,14 +2243,12 @@ def intersection( @overload # type: ignore[override] def nearest( self, coordinates: Any, num_results: int = 1, bbox: Literal[True] = True - ) -> Iterator[Item]: - ... + ) -> Iterator[Item]: ... @overload def nearest( self, coordinates: Any, num_results: int = 1, bbox: Literal[False] = False - ) -> Iterator[object]: - ... + ) -> Iterator[object]: ... def nearest( self, coordinates: Any, num_results: int = 1, bbox: bool = False @@ -2367,5 +2356,5 @@ def leaves(self): [self._objects[child_id][1] for child_id in child_ids], bounds, ) - for id, child_ids, bounds in super(RtreeContainer, self).leaves() + for id, child_ids, bounds in super().leaves() ] diff --git a/scripts/repair_wheel.py b/scripts/repair_wheel.py index 3f71bce6..d993ab7d 100755 --- a/scripts/repair_wheel.py +++ b/scripts/repair_wheel.py @@ -18,7 +18,7 @@ def main(): os_ = "windows" else: raise NotImplementedError( - "sys.platform '{}' is not supported yet.".format(sys.platform) + f"sys.platform '{sys.platform}' is not supported yet." ) p = argparse.ArgumentParser( diff --git a/tests/test_index.py b/tests/test_index.py index c95a422f..4a14fe8f 100644 --- a/tests/test_index.py +++ b/tests/test_index.py @@ -256,7 +256,7 @@ def test_objects(self) -> None: hit = [h for h in hits if h.id == 4321][0] self.assertEqual(hit.id, 4321) self.assertEqual(hit.object, 42) - box = ["%.10f" % t for t in hit.bbox] + box = [f"{t:.10f}" for t in hit.bbox] expected = ["34.3776829412", "26.7375853734", "49.3776829412", "41.7375853734"] self.assertEqual(box, expected) diff --git a/tests/test_tpr.py b/tests/test_tpr.py index b9c0bbc2..20ea8578 100644 --- a/tests/test_tpr.py +++ b/tests/test_tpr.py @@ -7,6 +7,7 @@ from typing import Any, Iterator import numpy as np +from numpy.random import default_rng from rtree.index import Index, Property, RT_TPRTree @@ -79,16 +80,18 @@ def data_generator( max_x: int = 1, max_y: int = 1, ) -> Iterator[tuple[str, int, Any]]: + rng = default_rng() + def create_object( id_: float, time: float, x: float | None = None, y: float | None = None ) -> Cartesian: # Create object with random or defined x, y and random velocity if x is None: - x = np.random.uniform(min_x, max_x) + x = rng.uniform(min_x, max_x) if y is None: - y = np.random.uniform(min_y, max_y) - speed = np.random.uniform(min_speed, max_speed) - angle = np.random.uniform(-np.pi, np.pi) + y = rng.uniform(min_y, max_y) + speed = rng.uniform(min_speed, max_speed) + angle = rng.uniform(-np.pi, np.pi) x_vel, y_vel = speed * np.cos(angle), speed * np.sin(angle) # Set update time for when out of bounds, or max interval @@ -123,9 +126,9 @@ def create_object( continue kill = object_.out_of_bounds else: - id_ = np.random.randint(0, dataset_size) + id_ = rng.integers(0, dataset_size) while id_ in updated_ids: - id_ = np.random.randint(0, dataset_size) + id_ = rng.integers(0, dataset_size) object_ = objects[id_] updated_ids.add(object_.id) @@ -144,12 +147,12 @@ def create_object( yield "INSERT", t_now, object_ for _ in range(queries_per_time_step): - x = np.random.uniform(min_x, max_x) - y = np.random.uniform(min_y, max_y) - dx = np.random.uniform(min_query_extent, max_query_extent) - dy = np.random.uniform(min_query_extent, max_query_extent) - dt = np.random.randint(min_query_interval, max_query_interval + 1) - t = np.random.randint(t_now, t_now + horizon - dt) + x = rng.uniform(min_x, max_x) + y = rng.uniform(min_y, max_y) + dx = rng.uniform(min_query_extent, max_query_extent) + dy = rng.uniform(min_query_extent, max_query_extent) + dt = rng.integers(min_query_interval, max_query_interval + 1) + t = rng.integers(t_now, t_now + horizon - dt) yield "QUERY", t_now, QueryCartesian(t, t + dt, x, y, dx, dy)