diff --git a/.prospector.yaml b/.prospector.yaml index 64ece6197..f1dfda7b6 100644 --- a/.prospector.yaml +++ b/.prospector.yaml @@ -1,45 +1,18 @@ inherits: - duplicated + - utils:base + - utils:no-design-checks + strictness: veryhigh doc-warnings: false -max-line-length: 110 ignore-patterns: - ^tilecloud/scripts/tc_.*.py pylint: - options: - max-line-length: 110 disable: - - too-many-instance-attributes - - too-many-ancestors - - too-many-return-statements - - too-many-branches - - too-many-arguments - - too-many-locals - - too-many-nested-blocks - - too-few-public-methods - - no-else-return - - abstract-method - - invalid-name - - redefined-builtin - - broad-except - cyclic-import # see: https://github.com/PyCQA/pylint/issues/850 -pycodestyle: - disable: - - E501 - -mypy: - run: true - bandit: - run: true options: config: .bandit.yaml - -pyroma: - run: true - -mccabe: - run: false diff --git a/poetry.lock b/poetry.lock index 951a91b95..4edea1508 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1159,13 +1159,24 @@ with-vulture = ["vulture (>=1.5)"] [[package]] name = "prospector-profile-duplicated" -version = "0.1.0" +version = "0.3.0" description = "Profile that can be used to disable the duplicated or conflict rules between Prospector and other tools" optional = false python-versions = "*" files = [ - {file = "prospector_profile_duplicated-0.1.0-py2.py3-none-any.whl", hash = "sha256:d9c8679736bb2f2310b0faec3f6955d63388abc594e293a3e82e43ae0315991c"}, - {file = "prospector_profile_duplicated-0.1.0.tar.gz", hash = "sha256:144baaa10101a28834d68552ff0b1714c08f22c8a7e9506e02ccc8584a28943c"}, + {file = "prospector_profile_duplicated-0.3.0-py2.py3-none-any.whl", hash = "sha256:5c9bc909c0bc024421bbd38e75015b56425769233aad31437c41835c8d3a34e7"}, + {file = "prospector_profile_duplicated-0.3.0.tar.gz", hash = "sha256:142dd353a2e65f3c39c36eba7d1a5f4ffc3869381425bed43a47cc158a122a8a"}, +] + +[[package]] +name = "prospector-profile-utils" +version = "1.1.0" +description = "Some utility Prospector profiles." +optional = false +python-versions = "*" +files = [ + {file = "prospector_profile_utils-1.1.0-py2.py3-none-any.whl", hash = "sha256:ff762e66c9b252191e8c7b944ee73a4b67957e9b9eb1db0af088510dc1791e26"}, + {file = "prospector_profile_utils-1.1.0.tar.gz", hash = "sha256:4e589b4842cee70c04200a0d77e509909deedff9df76f2a0dfee145eb9327a66"}, ] [[package]] @@ -2161,4 +2172,4 @@ wsgi = ["pyramid"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.12" -content-hash = "16d4324f0b55ff64c43c219894748831d5d70bf421c2ad8e75b8b386b442220b" +content-hash = "5612d4347311605ba680a18faa0c847bda450e95a12ad1aabb1d45c056f1b3ee" diff --git a/pyproject.toml b/pyproject.toml index e5c7954b0..eee7471a9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,8 +69,10 @@ redis = ["redis"] prometheus = ["prometheus_client"] all = ["azure-storage-blob", "azure-identity", "boto3", "pyramid", "redis", "prometheus_client"] -[tool.poetry.dev-dependencies] +[tool.poetry.group.dev.dependencies] prospector = { version = "1.10.2", extras = ["with_bandit", "with_mypy", "with_pyroma"] } +prospector-profile-duplicated = "0.3.0" +prospector-profile-utils = "1.1.0" pytest = "7.4.0" pytest-cov = "4.1.0" types-boto = "2.49.18.8" @@ -79,9 +81,6 @@ types-requests = "2.31.0.1" pycairo = "1.24.0" Shapely = "2.0.1" -[tool.poetry.group.dev.dependencies] -prospector-profile-duplicated = "0.1.0" - [build-system] requires = [ "poetry-core>=1.3.0", diff --git a/tilecloud/__init__.py b/tilecloud/__init__.py index 69462957f..f4c22a863 100644 --- a/tilecloud/__init__.py +++ b/tilecloud/__init__.py @@ -13,15 +13,20 @@ from operator import attrgetter from typing import Any, Optional, Union, cast +logger = logging.getLogger(__name__) -def cmp(a: Any, b: Any) -> int: - return cast(bool, a > b) - cast(bool, a < b) +class NotSupportedOperation(Exception): + pass -logger = logging.getLogger(__name__) + +def cmp(a: Any, b: Any) -> int: # pylint: disable=invalid-name + return cast(bool, a > b) - cast(bool, a < b) -def consume(iterator: Iterator[Optional["Tile"]], n: Optional[int] = None) -> None: # pragma: no cover +def consume( + iterator: Iterator[Optional["Tile"]], n: Optional[int] = None # pylint: disable=invalid-name +) -> None: # pragma: no cover """ Advance the iterator n-steps ahead. @@ -77,9 +82,8 @@ def __contains__(self, key: int) -> bool: """ if self.start is None: return False - else: - assert self.stop is not None - return self.start <= key < self.stop + assert self.stop is not None + return self.start <= key < self.stop def __len__(self) -> int: """ @@ -87,9 +91,8 @@ def __len__(self) -> int: """ if self.start is None: return 0 - else: - assert self.stop is not None - return self.stop - self.start + assert self.stop is not None + return self.stop - self.start def __iter__(self) -> Iterator[int]: if self.start is not None: @@ -99,8 +102,7 @@ def __iter__(self) -> Iterator[int]: def __repr__(self) -> str: # pragma: no cover if self.start is None: return f"{self.__class__.__name__}(None)" - else: - return f"{self.__class__.__name__}({self.start}, {self.stop})" + return f"{self.__class__.__name__}({self.start}, {self.stop})" def add(self, value: int) -> "Bounds": """ @@ -141,12 +143,11 @@ def union(self, other: "Bounds") -> "Bounds": assert other.start is not None assert other.stop is not None return Bounds(min(self.start, other.start), max(self.stop, other.stop)) - elif self: + if self: return Bounds(self.start, self.stop) - elif other: + if other: return Bounds(other.start, other.stop) - else: - return Bounds() + return Bounds() class BoundingPyramid: @@ -198,7 +199,7 @@ def add(self, tilecoord: "TileCoord") -> "BoundingPyramid": self.bounds[tilecoord.z] = (Bounds(tilecoord.x), Bounds(tilecoord.y)) return self - def add_bounds(self, z: int, bounds: tuple[Bounds, Bounds]) -> None: + def add_bounds(self, z: int, bounds: tuple[Bounds, Bounds]) -> None: # pylint: disable=invalid-name if z in self.bounds: self.bounds[z][0].update(bounds[0]) self.bounds[z][1].update(bounds[1]) @@ -206,7 +207,9 @@ def add_bounds(self, z: int, bounds: tuple[Bounds, Bounds]) -> None: self.bounds[z] = bounds def fill( - self, zs: Optional[Iterable[int]] = None, extent: Optional[tuple[float, float, float, float]] = None + self, + zs: Optional[Iterable[int]] = None, # pylint: disable=invalid-name + extent: Optional[tuple[float, float, float, float]] = None, ) -> None: if zs is None: assert self.tilegrid is not None @@ -215,7 +218,7 @@ def fill( if extent is None: extent = self.tilegrid.max_extent minx, miny, maxx, maxy = extent - for z in zs: + for z in zs: # pylint: disable=invalid-name self.add(self.tilegrid.tilecoord(z, minx, miny)) self.add(self.tilegrid.tilecoord(z, maxx, maxy)) @@ -223,59 +226,59 @@ def fill_down(self, bottom: int, start: Optional[Any] = None) -> None: if start is None: start = max(self.bounds) assert self.tilegrid is not None - for z in range(start, bottom): + for z in range(start, bottom): # pylint: disable=invalid-name self.add_bounds(z + 1, self.tilegrid.fill_down(z, self.bounds[z])) def fill_up(self, top: int = 0) -> None: assert self.tilegrid is not None - for z in range(max(self.bounds), top, -1): + for z in range(max(self.bounds), top, -1): # pylint: disable=invalid-name self.add_bounds(z - 1, self.tilegrid.fill_up(z, self.bounds[z])) def iterbottomup(self) -> Iterator["TileCoord"]: - for z in reversed(sorted(self.bounds.keys())): + for z in reversed(sorted(self.bounds.keys())): # pylint: disable=invalid-name yield from self.ziter(z) def itertopdown(self) -> Iterator["TileCoord"]: - for z in sorted(self.bounds.keys()): + for z in sorted(self.bounds.keys()): # pylint: disable=invalid-name yield from self.ziter(z) - def metatilecoords(self, n: int = 8) -> Iterator["TileCoord"]: - for z in sorted(self.bounds.keys()): + def metatilecoords(self, n: int = 8) -> Iterator["TileCoord"]: # pylint: disable=invalid-name + for z in sorted(self.bounds.keys()): # pylint: disable=invalid-name xbounds, ybounds = self.bounds[z] assert xbounds.start is not None assert xbounds.stop is not None assert ybounds.start is not None assert ybounds.stop is not None metatilecoord = TileCoord(z, xbounds.start, ybounds.start).metatilecoord(n) - x = metatilecoord.x + x = metatilecoord.x # pylint: disable=invalid-name while x < xbounds.stop: - y = metatilecoord.y + y = metatilecoord.y # pylint: disable=invalid-name while y < ybounds.stop: yield TileCoord(z, x, y, n) - y += n - x += n + y += n # pylint: disable=invalid-name + x += n # pylint: disable=invalid-name - def zget(self, z: int) -> tuple[Bounds, Bounds]: + def zget(self, z: int) -> tuple[Bounds, Bounds]: # pylint: disable=invalid-name """ Return the tuple (xbounds, ybounds) at level z. """ return self.bounds[z] - def ziter(self, z: int) -> Iterator["TileCoord"]: + def ziter(self, z: int) -> Iterator["TileCoord"]: # pylint: disable=invalid-name """ Generates every TileCoord in self at level z. """ if z in self.bounds: xbounds, ybounds = self.bounds[z] - for x in xbounds: - for y in ybounds: + for x in xbounds: # pylint: disable=invalid-name + for y in ybounds: # pylint: disable=invalid-name yield TileCoord(z, x, y) - def zs(self) -> Iterable[int]: + def zs(self) -> Iterable[int]: # pylint: disable=invalid-name return self.bounds.keys() @classmethod - def from_string(cls, s: str) -> "BoundingPyramid": + def from_string(cls, s: str) -> "BoundingPyramid": # pylint: disable=invalid-name match = re.match( r"(?P\d+)/(?P\d+)/(?P\d+):" + r"(?:(?P\+)?(?P\d+)/)?" @@ -285,26 +288,26 @@ def from_string(cls, s: str) -> "BoundingPyramid": ) if not match: raise ValueError(f"invalid literal for {cls.__name__}.from_string(): {s}") - z1 = int(match.group("z1")) - x1 = int(match.group("x1")) + z1 = int(match.group("z1")) # pylint: disable=invalid-name + x1 = int(match.group("x1")) # pylint: disable=invalid-name if match.group("starx"): - x2 = 1 << z1 + x2 = 1 << z1 # pylint: disable=invalid-name elif match.group("plusx"): - x2 = x1 + int(match.group("x2")) + x2 = x1 + int(match.group("x2")) # pylint: disable=invalid-name else: - x2 = int(match.group("x2")) - y1 = int(match.group("y1")) + x2 = int(match.group("x2")) # pylint: disable=invalid-name + y1 = int(match.group("y1")) # pylint: disable=invalid-name if match.group("stary"): - y2 = 1 << z1 + y2 = 1 << z1 # pylint: disable=invalid-name elif match.group("plusy"): - y2 = y1 + int(match.group("y2")) + y2 = y1 + int(match.group("y2")) # pylint: disable=invalid-name else: - y2 = int(match.group("y2")) + y2 = int(match.group("y2")) # pylint: disable=invalid-name result = cls({z1: (Bounds(x1, x2), Bounds(y1, y2))}) if match.group("z2"): - z2 = int(match.group("z2")) + z2 = int(match.group("z2")) # pylint: disable=invalid-name if match.group("plusz"): - z2 += z1 + z2 += z1 # pylint: disable=invalid-name if z1 < z2: result.fill_down(z2) elif z1 > z2: @@ -314,7 +317,7 @@ def from_string(cls, s: str) -> "BoundingPyramid": @classmethod def full(cls, zmin: Optional[int] = None, zmax: Optional[int] = None) -> "BoundingPyramid": assert zmax is not None - zs = (zmax,) if zmin is None else range(zmin, zmax + 1) + zs = (zmax,) if zmin is None else range(zmin, zmax + 1) # pylint: disable=invalid-name return cls({z: (Bounds(0, 1 << z), Bounds(0, 1 << z)) for z in zs}) @@ -393,7 +396,7 @@ class TileCoord: A tile coordinate. """ - def __init__(self, z: int, x: int, y: int, n: int = 1) -> None: + def __init__(self, z: int, x: int, y: int, n: int = 1) -> None: # pylint: disable=invalid-name """ Construct a TileCoord. @@ -404,10 +407,10 @@ def __init__(self, z: int, x: int, y: int, n: int = 1) -> None: y: Y coordinate n: Tile size """ - self.z = z - self.x = x - self.y = y - self.n = n + self.z = z # pylint: disable=invalid-name + self.x = x # pylint: disable=invalid-name + self.y = y # pylint: disable=invalid-name + self.n = n # pylint: disable=invalid-name def __cmp__(self, other: "TileCoord") -> int: """ @@ -449,8 +452,7 @@ def __repr__(self) -> str: # pragma: no cover """ if self.n == 1: return f"{self.__class__.__name__}({self.z}, {self.x}, {self.y})" - else: - return f"{self.__class__.__name__}({self.z}, {self.x}, {self.x}, {self.n})" + return f"{self.__class__.__name__}({self.z}, {self.x}, {self.x}, {self.n})" def __str__(self) -> str: """ @@ -458,21 +460,20 @@ def __str__(self) -> str: """ if self.n == 1: return f"{self.z}/{self.x}/{self.y}" - else: - return f"{self.z}/{self.x}/{self.y}:+{self.n}/+{self.n}" + return f"{self.z}/{self.x}/{self.y}:+{self.n}/+{self.n}" - def metatilecoord(self, n: int = 8) -> "TileCoord": + def metatilecoord(self, n: int = 8) -> "TileCoord": # pylint: disable=invalid-name return TileCoord(self.z, n * (self.x // n), n * (self.y // n), n) def tuple(self) -> tuple[int, int, int, int]: return (self.z, self.x, self.y, self.n) @classmethod - def from_string(cls, s: str) -> "TileCoord": - m = re.match(r"(\d+)/(\d+)/(\d+)(?::\+(\d+)/\+\4)?\Z", s) - if not m: + def from_string(cls, s: str) -> "TileCoord": # pylint: disable=invalid-name + match = re.match(r"(\d+)/(\d+)/(\d+)(?::\+(\d+)/\+\4)?\Z", s) + if not match: raise ValueError(f"invalid literal for {cls.__name__}.from_string: {s}") - x, y, z, n = m.groups() + x, y, z, n = match.groups() # pylint: disable=invalid-name return cls(int(x), int(y), int(z), int(n) if n else 1) @classmethod @@ -507,10 +508,14 @@ def extent(self, tilecoord: TileCoord, border: float = 0) -> tuple[float, float, """ raise NotImplementedError - def fill_down(self, z: int, bounds: tuple[Bounds, Bounds]) -> tuple[Bounds, Bounds]: + def fill_down( + self, z: int, bounds: tuple[Bounds, Bounds] # pylint: disable=invalid-name + ) -> tuple[Bounds, Bounds]: raise NotImplementedError - def fill_up(self, z: int, bounds: tuple[Bounds, Bounds]) -> tuple[Bounds, Bounds]: + def fill_up( + self, z: int, bounds: tuple[Bounds, Bounds] # pylint: disable=invalid-name + ) -> tuple[Bounds, Bounds]: raise NotImplementedError def parent(self, tilecoord: TileCoord) -> Optional[TileCoord]: @@ -525,13 +530,13 @@ def roots(self) -> Iterator[TileCoord]: """ raise NotImplementedError - def tilecoord(self, z: int, x: float, y: float) -> TileCoord: + def tilecoord(self, z: int, x: float, y: float) -> TileCoord: # pylint: disable=invalid-name """ Returns the TileCoord for location (x, y) at level z. """ raise NotImplementedError - def zs(self) -> Iterable[int]: + def zs(self) -> Iterable[int]: # pylint: disable=invalid-name """ Generates all zs. """ @@ -599,8 +604,7 @@ def __contains__(self, tile: Tile) -> bool: """ if tile and self.bounding_pyramid: return tile.tilecoord in self.bounding_pyramid - else: - return False + return False def __len__(self) -> int: """ diff --git a/tilecloud/filter/benchmark.py b/tilecloud/filter/benchmark.py index 617355b06..95564dce6 100644 --- a/tilecloud/filter/benchmark.py +++ b/tilecloud/filter/benchmark.py @@ -11,15 +11,15 @@ class Statistics: - def __init__(self, format: str = "%f"): - self.format = format - self.n = 0 + def __init__(self, format_pattern: str = "%f"): + self.format = format_pattern + self.n = 0 # pylint: disable=invalid-name self.sum = 0.0 self.sum_of_squares = 0.0 self.minimum: Optional[float] = None self.maximum: Optional[float] = None - def add(self, x: float) -> None: + def add(self, x: float) -> None: # pylint: disable=invalid-name self.n += 1 self.sum += x self.sum_of_squares += x * x diff --git a/tilecloud/filter/consistenthash.py b/tilecloud/filter/consistenthash.py index 11b0a1c71..2eee276cd 100644 --- a/tilecloud/filter/consistenthash.py +++ b/tilecloud/filter/consistenthash.py @@ -18,12 +18,11 @@ class EveryNth: ``n`` in the above calculation. """ - def __init__(self, n: int, i: int) -> None: - self.n = n + def __init__(self, n: int, i: int) -> None: # pylint: disable=invalid-name + self.n = n # pylint: disable=invalid-name self.i = i def __call__(self, tile: Tile) -> Optional[Tile]: if hash(tile.tilecoord) % self.n == self.i: return tile - else: - return None + return None diff --git a/tilecloud/filter/error.py b/tilecloud/filter/error.py index 3fbf06864..4d2b1b87a 100644 --- a/tilecloud/filter/error.py +++ b/tilecloud/filter/error.py @@ -29,8 +29,7 @@ class DropErrors: def __call__(self, tile: Tile) -> Optional[Tile]: if not tile or tile.error: return None - else: - return tile + return tile class LogErrors(Logger): diff --git a/tilecloud/filter/image.py b/tilecloud/filter/image.py index ffa3f5996..9363ed8fa 100644 --- a/tilecloud/filter/image.py +++ b/tilecloud/filter/image.py @@ -63,10 +63,10 @@ def __call__(self, tile: Tile) -> Tile: assert tile.data is not None image = PIL.Image.open(BytesIO(tile.data)) for tilestore in self.tilestores: - t = tilestore.get_one(Tile(tile.tilecoord)) - if t is not None: - assert t.data is not None - image2 = PIL.Image.open(BytesIO(t.data)) + sub_tile = tilestore.get_one(Tile(tile.tilecoord)) + if sub_tile is not None: + assert sub_tile.data is not None + image2 = PIL.Image.open(BytesIO(sub_tile.data)) image.paste(image2, None, image2) content_type = self.content_type if content_type is None: @@ -90,8 +90,8 @@ class PILImageFilter: Extra params passed to the PIL ``save`` function. """ - def __init__(self, filter: Callable[[Tile], Tile], **kwargs: Any): - self.filter = filter + def __init__(self, filter_pattern: Callable[[Tile], Tile], **kwargs: Any): + self.filter = filter_pattern self.kwargs = kwargs def __call__(self, tile: Tile) -> Tile: diff --git a/tilecloud/filter/optipng.py b/tilecloud/filter/optipng.py index cff4f386e..eee770f63 100644 --- a/tilecloud/filter/optipng.py +++ b/tilecloud/filter/optipng.py @@ -17,8 +17,8 @@ def __call__(self, tile: Tile) -> Tile: ntf.close() retcode = call(self.args + [ntf.name]) # nosec if retcode == 0: - with open(ntf.name, "rb") as f: - tile.data = f.read() + with open(ntf.name, "rb") as file: + tile.data = file.read() finally: try: os.unlink(ntf.name) diff --git a/tilecloud/grid/free.py b/tilecloud/grid/free.py index 3e694d692..308c4a4d0 100644 --- a/tilecloud/grid/free.py +++ b/tilecloud/grid/free.py @@ -2,7 +2,7 @@ from math import floor from typing import Optional, Union -from tilecloud import TileCoord, TileGrid +from tilecloud import Bounds, NotSupportedOperation, TileCoord, TileGrid class FreeTileGrid(TileGrid): @@ -36,21 +36,21 @@ def children(self, tilecoord: TileCoord) -> Iterator[TileCoord]: for child_z in self.child_zs[tilecoord.z]: factor = self.resolutions[tilecoord.z] / self.resolutions[child_z] for i in range(0, int(factor)): - x = round(factor * tilecoord.x + i) + x = round(factor * tilecoord.x + i) # pylint: disable=invalid-name for j in range(0, int(factor)): - y = round(factor * tilecoord.y + j) + y = round(factor * tilecoord.y + j) # pylint: disable=invalid-name yield TileCoord(child_z, x, y) def extent(self, tilecoord: TileCoord, border: float = 0) -> tuple[float, float, float, float]: assert self.max_extent - y: float = tilecoord.y + y: float = tilecoord.y # pylint: disable=invalid-name if not self.flip_y: - n = ( + n = ( # pylint: disable=invalid-name self.scale * (self.max_extent[3] - self.max_extent[1]) / float(self.tile_size * self.resolutions[tilecoord.z]) ) - y = n - y - tilecoord.n + y = n - y - tilecoord.n # pylint: disable=invalid-name minx = ( self.max_extent[0] + (self.tile_size * tilecoord.x - border) * self.resolutions[tilecoord.z] / self.scale @@ -72,36 +72,45 @@ def parent(self, tilecoord: TileCoord) -> Optional[TileCoord]: parent_z = self.parent_zs[tilecoord.z] if parent_z is None: return None - else: - factor = self.resolutions[parent_z] / self.resolutions[tilecoord.z] - return TileCoord(parent_z, int(tilecoord.x // factor), int(tilecoord.y // factor)) + factor = self.resolutions[parent_z] / self.resolutions[tilecoord.z] + return TileCoord(parent_z, int(tilecoord.x // factor), int(tilecoord.y // factor)) def roots(self) -> Iterator[TileCoord]: - for z, parent_z in enumerate(self.parent_zs): - if parent_z is None: - x, s = 0, 0.0 + for zoom, parent_zoom in enumerate(self.parent_zs): + if parent_zoom is None: + x, s = 0, 0.0 # pylint: disable=invalid-name while s < self.resolutions[0]: - y, t = 0, 0.0 + y, t = 0, 0.0 # pylint: disable=invalid-name while t < self.resolutions[0]: - yield TileCoord(z, x, y) - y += 1 - t += self.resolutions[z] - x += 1 - s += self.resolutions[z] + yield TileCoord(zoom, x, y) + y += 1 # pylint: disable=invalid-name + t += self.resolutions[zoom] # pylint: disable=invalid-name + x += 1 # pylint: disable=invalid-name + s += self.resolutions[zoom] # pylint: disable=invalid-name def tilecoord(self, z: int, x: float, y: float) -> TileCoord: - tx = self.scale * (x - self.max_extent[0]) / (self.resolutions[z] * self.tile_size) - ty = self.scale * (y - self.max_extent[1]) / float(self.resolutions[z] * self.tile_size) + tx = ( # pylint: disable=invalid-name + self.scale * (x - self.max_extent[0]) / (self.resolutions[z] * self.tile_size) + ) + ty = ( # pylint: disable=invalid-name + self.scale * (y - self.max_extent[1]) / float(self.resolutions[z] * self.tile_size) + ) if not self.flip_y: - n = ( + n = ( # pylint: disable=invalid-name self.scale * (self.max_extent[3] - self.max_extent[1]) / float(self.tile_size * self.resolutions[z]) ) - ty = n - ty + ty = n - ty # pylint: disable=invalid-name return TileCoord(z, int(floor(tx)), int(floor(ty))) def zs(self) -> range: return range(len(self.resolutions)) + + def fill_up(self, z: int, bounds: tuple[Bounds, Bounds]) -> tuple[Bounds, Bounds]: + raise NotSupportedOperation() + + def fill_down(self, z: int, bounds: tuple[Bounds, Bounds]) -> tuple[Bounds, Bounds]: + raise NotSupportedOperation() diff --git a/tilecloud/grid/quad.py b/tilecloud/grid/quad.py index 0e6ed9367..dd86923c5 100644 --- a/tilecloud/grid/quad.py +++ b/tilecloud/grid/quad.py @@ -24,23 +24,23 @@ def children(self, tilecoord: TileCoord) -> Iterator[TileCoord]: yield TileCoord(tilecoord.z + 1, 2 * tilecoord.x + 1, 2 * tilecoord.y + 1) def extent(self, tilecoord: TileCoord, border: float = 0) -> tuple[float, float, float, float]: - y = tilecoord.y + y = tilecoord.y # pylint: disable=invalid-name if not self.flip_y: - y = (1 << tilecoord.z) - y - tilecoord.n + y = (1 << tilecoord.z) - y - tilecoord.n # pylint: disable=invalid-name delta = float(border) / self.tile_size if border else 0 - minx = self.max_extent[0] + (self.max_extent[2] - self.max_extent[0]) * (tilecoord.x - delta) / ( + min_x = self.max_extent[0] + (self.max_extent[2] - self.max_extent[0]) * (tilecoord.x - delta) / ( 1 << tilecoord.z ) - miny = self.max_extent[1] + (self.max_extent[3] - self.max_extent[1]) * (y - delta) / ( + min_y = self.max_extent[1] + (self.max_extent[3] - self.max_extent[1]) * (y - delta) / ( 1 << tilecoord.z ) - maxx = self.max_extent[0] + (self.max_extent[2] - self.max_extent[0]) * ( + max_x = self.max_extent[0] + (self.max_extent[2] - self.max_extent[0]) * ( tilecoord.x + tilecoord.n + delta ) / (1 << tilecoord.z) - maxy = self.max_extent[1] + (self.max_extent[3] - self.max_extent[1]) * (y + tilecoord.n + delta) / ( + max_y = self.max_extent[1] + (self.max_extent[3] - self.max_extent[1]) * (y + tilecoord.n + delta) / ( 1 << tilecoord.z ) - return (minx, miny, maxx, maxy) + return (min_x, min_y, max_x, max_y) def fill_down(self, z: int, bounds: tuple[Bounds, Bounds]) -> tuple[Bounds, Bounds]: xbounds, ybounds = bounds @@ -65,8 +65,7 @@ def fill_up(self, z: int, bounds: tuple[Bounds, Bounds]) -> tuple[Bounds, Bounds def parent(self, tilecoord: TileCoord) -> Optional[TileCoord]: if tilecoord.z == 0: return None - else: - return TileCoord(tilecoord.z - 1, int(tilecoord.x // 2), int(tilecoord.y // 2)) + return TileCoord(tilecoord.z - 1, int(tilecoord.x // 2), int(tilecoord.y // 2)) def roots(self) -> Iterator[TileCoord]: yield TileCoord(0, 0, 0) @@ -81,5 +80,4 @@ def tilecoord(self, z: int, x: float, y: float) -> TileCoord: def zs(self) -> Iterable[int]: if self.max_zoom: return range(0, self.max_zoom + 1) - else: - return count(0) + return count(0) diff --git a/tilecloud/layout/i3d.py b/tilecloud/layout/i3d.py index 9daa9277f..0b29265b4 100644 --- a/tilecloud/layout/i3d.py +++ b/tilecloud/layout/i3d.py @@ -26,21 +26,21 @@ def _tilecoord(match: Match[str]) -> TileCoord: @staticmethod def quadcode_from_tilecoord(tilecoord: TileCoord) -> str: - x, y = int(tilecoord.x), int(tilecoord.y) + x, y = int(tilecoord.x), int(tilecoord.y) # pylint: disable=invalid-name result = "" for _ in range(0, tilecoord.z): result += "0123"[(x & 1) + ((y & 1) << 1)] - x >>= 1 - y >>= 1 + x >>= 1 # pylint: disable=invalid-name + y >>= 1 # pylint: disable=invalid-name return result[::-1] @staticmethod def tilecoord_from_quadcode(quadcode: str) -> TileCoord: - z, x, y = len(quadcode), 0, 0 - for i, c in enumerate(quadcode): + z, x, y = len(quadcode), 0, 0 # pylint: disable=invalid-name + for i, code in enumerate(quadcode): mask = 1 << (z - i - 1) - if c in ["1", "3"]: - x |= mask - if c in ["2", "3"]: - y |= mask + if code in ["1", "3"]: + x |= mask # pylint: disable=invalid-name + if code in ["2", "3"]: + y |= mask # pylint: disable=invalid-name return TileCoord(z, x, y) diff --git a/tilecloud/layout/tilecache.py b/tilecloud/layout/tilecache.py index 72475cdb6..c194d024c 100644 --- a/tilecloud/layout/tilecache.py +++ b/tilecloud/layout/tilecache.py @@ -18,15 +18,25 @@ def __init__(self) -> None: RETileLayout.__init__(self, self.PATTERN, self.RE) def filename(self, tilecoord: TileCoord, metadata: Optional[Any] = None) -> str: - zs = f"{tilecoord.z:02d}" - xs = f"{tilecoord.x:09f}" - ys = f"{tilecoord.y:09f}" - return "/".join((zs, xs[0:3], xs[3:6], xs[6:9], ys[0:3], ys[3:6], ys[6:9])) + zoom_string = f"{tilecoord.z:02d}" + x_string = f"{tilecoord.x:09f}" + y_string = f"{tilecoord.y:09f}" + return "/".join( + ( + zoom_string, + x_string[0:3], + x_string[3:6], + x_string[6:9], + y_string[0:3], + y_string[3:6], + y_string[6:9], + ) + ) @staticmethod def _tilecoord(_match: Match[str]) -> TileCoord: ints = list(map(int, _match.groups())) - z = ints[0] - x = 1000000 * ints[1] + 1000 * ints[2] + ints[3] - y = 1000000 * ints[4] + 1000 * ints[5] + ints[6] - return TileCoord(z, x, y) + zoom = ints[0] + x = 1000000 * ints[1] + 1000 * ints[2] + ints[3] # pylint: disable=invalid-name + y = 1000000 * ints[4] + 1000 * ints[5] + ints[6] # pylint: disable=invalid-name + return TileCoord(zoom, x, y) diff --git a/tilecloud/layout/wms.py b/tilecloud/layout/wms.py index 650cd2723..bdadb4071 100644 --- a/tilecloud/layout/wms.py +++ b/tilecloud/layout/wms.py @@ -1,7 +1,7 @@ from typing import Any, Optional from urllib.parse import urlencode -from tilecloud import TileCoord, TileGrid, TileLayout +from tilecloud import NotSupportedOperation, TileCoord, TileGrid, TileLayout class WMSTileLayout(TileLayout): @@ -10,7 +10,7 @@ def __init__( url: str, layers: str, srs: str, - format: str, + format_pattern: str, tilegrid: TileGrid, border: int = 0, params: Optional[dict[str, str]] = None, @@ -22,8 +22,8 @@ def __init__( self.border = border self.params = { "LAYERS": layers, - "FORMAT": format, - "TRANSPARENT": "TRUE" if format == "image/png" else "FALSE", + "FORMAT": format_pattern, + "TRANSPARENT": "TRUE" if format_pattern == "image/png" else "FALSE", "SERVICE": "WMS", "VERSION": "1.1.1", "REQUEST": "GetMap", @@ -39,10 +39,13 @@ def filename(self, tilecoord: TileCoord, metadata: Optional[Any] = None) -> str: bbox = self.tilegrid.extent(tilecoord, self.border) size = tilecoord.n * self.tilegrid.tile_size + 2 * self.border params = self.params.copy() - for k, v in metadata.items(): + for k, value in metadata.items(): if k.startswith("dimension_"): - params[k[len("dimension_") :]] = v + params[k[len("dimension_") :]] = value params["BBOX"] = f"{bbox[0]:f},{bbox[1]:f},{bbox[2]:f},{bbox[3]:f}" params["WIDTH"] = str(size) params["HEIGHT"] = str(size) return self.url + "?" + urlencode(params) + + def tilecoord(self, filename: str) -> TileCoord: + raise NotSupportedOperation() diff --git a/tilecloud/layout/wmts.py b/tilecloud/layout/wmts.py index 64880f5dd..5719f2a58 100644 --- a/tilecloud/layout/wmts.py +++ b/tilecloud/layout/wmts.py @@ -1,7 +1,7 @@ from collections.abc import Iterable from typing import Callable, Optional -from tilecloud import TileCoord, TileLayout +from tilecloud import NotSupportedOperation, TileCoord, TileLayout class WMTSTileLayout(TileLayout): @@ -10,7 +10,7 @@ def __init__( url: str = "", layer: Optional[str] = None, style: Optional[str] = None, - format: Optional[str] = None, + format_pattern: Optional[str] = None, tile_matrix_set: Optional[str] = None, tile_matrix: Callable[[int], str] = str, dimensions_name: Iterable[str] = (), @@ -21,8 +21,8 @@ def __init__( self.layer = layer assert style is not None self.style = style - assert format is not None - self.format = format + assert format_pattern is not None + self.format = format_pattern assert tile_matrix_set is not None self.tile_matrix_set = tile_matrix_set self.tile_matrix = tile_matrix @@ -57,5 +57,7 @@ def filename(self, tilecoord: TileCoord, metadata: Optional[dict[str, str]] = No ) if self.request_encoding == "KVP": return self.url + "&".join("=".join(p) for p in query) - else: - return self.url + "/".join(p[1] for p in query) + self.format + return self.url + "/".join(p[1] for p in query) + self.format + + def tilecoord(self, filename: str) -> TileCoord: + raise NotSupportedOperation() diff --git a/tilecloud/lib/PIL_.py b/tilecloud/lib/PIL_.py index b3ad1b3fa..aafd4c9a3 100644 --- a/tilecloud/lib/PIL_.py +++ b/tilecloud/lib/PIL_.py @@ -1 +1,2 @@ +# pylint: disable=invalid-name FORMAT_BY_CONTENT_TYPE = {"image/jpeg": "JPEG", "image/png": "PNG"} diff --git a/tilecloud/lib/memcached.py b/tilecloud/lib/memcached.py index f23d3be46..1a7142e74 100644 --- a/tilecloud/lib/memcached.py +++ b/tilecloud/lib/memcached.py @@ -19,23 +19,22 @@ def delete(self, key: str) -> bool: line = self.readline() if line == b"DELETED": return True - elif line == b"NOT_FOUND": + if line == b"NOT_FOUND": return False - else: - raise MemcachedError(line) + raise MemcachedError(line) def get(self, key: str) -> tuple[Optional[int], Optional[bytes], Optional[int]]: self.writeline(f"get {key}".encode()) line = self.readline() if line == b"END": return None, None, None - m = self.VALUE_RE.match(line) - if not m: + match = self.VALUE_RE.match(line) + if not match: raise MemcachedError(line) - assert m.group("key") == key.encode() - flags = int(m.group("flags")) - value = self.readvalue(int(m.group("bytes"))) - cas = None if m.group("cas") is None else int(m.group("cas")) + assert match.group("key") == key.encode() + flags = int(match.group("flags")) + value = self.readvalue(int(match.group("bytes"))) + cas = None if match.group("cas") is None else int(match.group("cas")) line = self.readline() if line != b"END": raise MemcachedError(line) @@ -48,7 +47,7 @@ def set(self, key: str, flags: int, exptime: int, value: bytes) -> None: if line != b"STORED": raise MemcachedError(line) - def readvalue(self, n: int) -> bytes: + def readvalue(self, n: int) -> bytes: # pylint: disable=invalid-name while len(self.buffer) < n + 2: self.buffer += self.socket.recv(n + 2 - len(self.buffer)) if self.buffer[n : n + 2] != b"\r\n": diff --git a/tilecloud/lib/wmts.py b/tilecloud/lib/wmts.py index faf7ede2e..b899be15f 100644 --- a/tilecloud/lib/wmts.py +++ b/tilecloud/lib/wmts.py @@ -4,12 +4,12 @@ from bottle import jinja2_template from pyproj import Proj, transform -from tilecloud.lib.wmts_get_capabilities_template import wmts_get_capabilities_template +from tilecloud.lib.wmts_get_capabilities_template import WMTS_GET_CAPABILITIES_TEMPLATE METERS_PER_UNIT = {"feet": 3.28084, "meters": 1, "degrees": 111118.752, "inch": 39.3700787} -def to_wsg84(srs: str, x: float, y: float) -> tuple[float, float]: +def to_wsg84(srs: str, x: float, y: float) -> tuple[float, float]: # pylint: disable=invalid-name return cast( tuple[float, float], transform(Proj(init=srs.lower()), Proj(proj="latlong", datum="WGS84"), x, y) ) @@ -101,7 +101,7 @@ def get_capabilities(layers: list[Layer], tile_matrix_set: TileMatrixSet, wmts_g return cast( str, jinja2_template( - wmts_get_capabilities_template, + WMTS_GET_CAPABILITIES_TEMPLATE, layers=layers, matrix_sets=matrix_sets(tile_matrix_set), wmts_gettile=wmts_gettile, diff --git a/tilecloud/lib/wmts_get_capabilities_template.py b/tilecloud/lib/wmts_get_capabilities_template.py index 486d4c05a..77f837ad5 100644 --- a/tilecloud/lib/wmts_get_capabilities_template.py +++ b/tilecloud/lib/wmts_get_capabilities_template.py @@ -1,4 +1,4 @@ -wmts_get_capabilities_template = """ +WMTS_GET_CAPABILITIES_TEMPLATE = """ Tile: if not self.dry_run: blob = self.container_client.get_blob_client(blob=key_name) blob.delete_blob() - except Exception as exc: + except Exception as exc: # pylint: disable=broad-except tile.error = exc return tile @@ -71,7 +71,7 @@ def get_one(self, tile: Tile) -> Optional[Tile]: tile.content_type = properties.content_settings.content_type except ResourceNotFoundError: return None - except Exception as exc: + except Exception as exc: # pylint: disable=broad-except LOGGER.exception(exc) tile.error = exc return tile @@ -102,7 +102,7 @@ def put_one(self, tile: Tile) -> Tile: cache_control=self.cache_control, ), ) - except Exception as exc: + except Exception as exc: # pylint: disable=broad-except tile.error = exc return tile diff --git a/tilecloud/store/boundingpyramid.py b/tilecloud/store/boundingpyramid.py index f116b5062..804525e7e 100644 --- a/tilecloud/store/boundingpyramid.py +++ b/tilecloud/store/boundingpyramid.py @@ -1,6 +1,6 @@ from typing import Any, Optional -from tilecloud import BoundingPyramid, Tile, TileStore +from tilecloud import BoundingPyramid, NotSupportedOperation, Tile, TileStore class BoundingPyramidTileStore(TileStore): @@ -15,8 +15,7 @@ def __init__(self, bounding_pyramid: Optional[BoundingPyramid] = None, **kwargs: def get_one(self, tile: Tile) -> Optional[Tile]: if tile and tile.tilecoord in self.get_cheap_bounding_pyramid(): return tile - else: - return None + return None def get_cheap_bounding_pyramid(self) -> BoundingPyramid: assert self.bounding_pyramid is not None @@ -25,3 +24,6 @@ def get_cheap_bounding_pyramid(self) -> BoundingPyramid: def put_one(self, tile: Tile) -> Tile: self.get_cheap_bounding_pyramid().add(tile.tilecoord) return tile + + def delete_one(self, tile: Tile) -> Tile: + raise NotSupportedOperation() diff --git a/tilecloud/store/bsddb.py b/tilecloud/store/bsddb.py index 4092cc9c1..9de302719 100644 --- a/tilecloud/store/bsddb.py +++ b/tilecloud/store/bsddb.py @@ -8,7 +8,7 @@ class BSDDBTileStore(TileStore): def __init__(self, db: bsddb.DB, **kwargs: Any): - self.db = db + self.db = db # pylint: disable=invalid-name TileStore.__init__(self, **kwargs) def __contains__(self, tile: Tile) -> bool: diff --git a/tilecloud/store/debug.py b/tilecloud/store/debug.py index a230fc02c..8ea8c04e0 100644 --- a/tilecloud/store/debug.py +++ b/tilecloud/store/debug.py @@ -5,7 +5,7 @@ import PIL.ImageDraw import PIL.ImageFont -from tilecloud import Tile, TileStore +from tilecloud import NotSupportedOperation, Tile, TileStore from tilecloud.lib.PIL_ import FORMAT_BY_CONTENT_TYPE @@ -27,3 +27,9 @@ def get_one(self, tile: Tile) -> Optional[Tile]: image.save(bytes_io, FORMAT_BY_CONTENT_TYPE[self.content_type]) tile.data = bytes_io.getvalue() return tile + + def put_one(self, tile: Tile) -> Tile: + raise NotSupportedOperation() + + def delete_one(self, tile: Tile) -> Tile: + raise NotSupportedOperation() diff --git a/tilecloud/store/dict.py b/tilecloud/store/dict.py index c7ad9ec70..9ca479ce4 100644 --- a/tilecloud/store/dict.py +++ b/tilecloud/store/dict.py @@ -23,8 +23,7 @@ def get_one(self, tile: Tile) -> Optional[Tile]: if tile and tile.tilecoord in self.tiles: tile.__dict__.update(self.tiles[tile.tilecoord]) return tile - else: - return None + return None def list(self) -> Iterator[Tile]: for tilecoord in self.tiles.keys(): diff --git a/tilecloud/store/filesystem.py b/tilecloud/store/filesystem.py index 4711f3d31..1591f91b4 100644 --- a/tilecloud/store/filesystem.py +++ b/tilecloud/store/filesystem.py @@ -20,8 +20,8 @@ def __init__(self, tilelayout: TileLayout, **kwargs: Any): def delete_one(self, tile: Tile) -> Tile: try: filename = self.tilelayout.filename(tile.tilecoord, tile.metadata) - except Exception as e: - tile.error = e + except Exception as exception: # pylint: disable=broad-except + tile.error = exception return tile if os.path.exists(filename): os.remove(filename) @@ -36,8 +36,8 @@ def get_all(self) -> Iterator[Tile]: def get_one(self, tile: Tile) -> Optional[Tile]: try: filename = self.tilelayout.filename(tile.tilecoord, tile.metadata) - except Exception as e: - tile.error = e + except Exception as exception: # pylint: disable=broad-except + tile.error = exception return tile try: with open(filename, "rb") as file: @@ -45,11 +45,10 @@ def get_one(self, tile: Tile) -> Optional[Tile]: if self.content_type is not None: tile.content_type = self.content_type return tile - except OSError as e: - if e.errno == errno.ENOENT: + except OSError as exception: + if exception.errno == errno.ENOENT: return None - else: - raise + raise def list(self) -> Iterator[Tile]: top = getattr(self.tilelayout, "prefix", ".") @@ -64,8 +63,8 @@ def put_one(self, tile: Tile) -> Tile: assert isinstance(tile.data, bytes) try: filename = self.tilelayout.filename(tile.tilecoord, tile.metadata) - except Exception as e: - tile.error = e + except Exception as exception: # pylint: disable=broad-except + tile.error = exception return tile dirname = os.path.dirname(filename) if not os.path.exists(dirname): diff --git a/tilecloud/store/filtered.py b/tilecloud/store/filtered.py index 223624c43..5db19d959 100644 --- a/tilecloud/store/filtered.py +++ b/tilecloud/store/filtered.py @@ -1,7 +1,7 @@ from functools import reduce from typing import Any, Callable, Optional -from tilecloud import Tile, TileStore +from tilecloud import NotSupportedOperation, Tile, TileStore class FilteredTileStore(TileStore): @@ -11,7 +11,15 @@ def __init__(self, tilestore: TileStore, filters: list[Callable[[Optional[Tile]] self.filters = filters def get_one(self, tile: Tile) -> Optional[Tile]: - def reduce_function(tile: Optional[Tile], filter: Callable[[Optional[Tile]], Tile]) -> Optional[Tile]: - return filter(tile) + def reduce_function( + tile: Optional[Tile], filter_pattern: Callable[[Optional[Tile]], Tile] + ) -> Optional[Tile]: + return filter_pattern(tile) return reduce(reduce_function, self.filters, self.tilestore.get_one(tile)) + + def put_one(self, tile: Tile) -> Tile: + raise NotSupportedOperation() + + def delete_one(self, tile: Tile) -> Tile: + raise NotSupportedOperation() diff --git a/tilecloud/store/findfirst.py b/tilecloud/store/findfirst.py index fdbf2f41d..5fa8e12df 100644 --- a/tilecloud/store/findfirst.py +++ b/tilecloud/store/findfirst.py @@ -1,7 +1,7 @@ from collections.abc import Iterator from typing import Any, Optional -from tilecloud import Tile, TileStore +from tilecloud import NotSupportedOperation, Tile, TileStore class FindFirstTileStore(TileStore): @@ -11,3 +11,9 @@ def __init__(self, tilestores: Iterator[TileStore], **kwargs: Any): def get_one(self, tile: Tile) -> Optional[Tile]: return next(filter(None, (store.get_one(tile) for store in self.tilestores)), None) + + def put_one(self, tile: Tile) -> Tile: + raise NotSupportedOperation() + + def delete_one(self, tile: Tile) -> Tile: + raise NotSupportedOperation() diff --git a/tilecloud/store/log.py b/tilecloud/store/log.py index 13f8209ab..1583700b6 100644 --- a/tilecloud/store/log.py +++ b/tilecloud/store/log.py @@ -2,7 +2,7 @@ from collections.abc import Iterator from typing import IO, Any -from tilecloud import Tile, TileStore +from tilecloud import NotSupportedOperation, Tile, TileStore from tilecloud.layout.re_ import RETileLayout @@ -31,3 +31,6 @@ def list(self) -> Iterator[Tile]: def put_one(self, tile: Tile) -> Tile: self.file.write(self.tilelayout.filename(tile.tilecoord, tile.metadata) + "\n") return tile + + def delete_one(self, tile: Tile) -> Tile: + raise NotSupportedOperation() diff --git a/tilecloud/store/mapnik_.py b/tilecloud/store/mapnik_.py index 975588352..8f1404b94 100644 --- a/tilecloud/store/mapnik_.py +++ b/tilecloud/store/mapnik_.py @@ -1,7 +1,7 @@ from json import dumps from typing import Any, Optional -from tilecloud import Tile, TileGrid, TileStore +from tilecloud import NotSupportedOperation, Tile, TileGrid, TileStore try: import mapnik2 as mapnik @@ -70,9 +70,11 @@ def get_one(self, tile: Tile) -> Optional[Tile]: if self.output_format == "grid": grid = mapnik.Grid(self.tilegrid.tile_size, self.tilegrid.tile_size) - for n, l in enumerate(self.mapnik.layers): - if l.name in self.layers_fields: - mapnik.render_layer(self.mapnik, grid, layer=n, fields=self.layers_fields[l.name]) + for number, layer in enumerate(self.mapnik.layers): + if layer.name in self.layers_fields: + mapnik.render_layer( + self.mapnik, grid, layer=number, fields=self.layers_fields[layer.name] + ) encode = grid.encode("utf", resolution=self.resolution) if self.drop_empty_utfgrid and len(encode["data"].keys()) == 0: @@ -80,8 +82,14 @@ def get_one(self, tile: Tile) -> Optional[Tile]: tile.data = dumps(encode).encode() else: # Render image with default Agg renderer - im = mapnik.Image(size, size) - mapnik.render(self.mapnik, im) - tile.data = im.tostring(self.output_format) + image = mapnik.Image(size, size) + mapnik.render(self.mapnik, image) + tile.data = image.tostring(self.output_format) return tile + + def put_one(self, tile: Tile) -> Tile: + raise NotSupportedOperation() + + def delete_one(self, tile: Tile) -> Tile: + raise NotSupportedOperation() diff --git a/tilecloud/store/mask.py b/tilecloud/store/mask.py index af27fb963..633ddc336 100644 --- a/tilecloud/store/mask.py +++ b/tilecloud/store/mask.py @@ -3,7 +3,7 @@ import PIL.Image -from tilecloud import BoundingPyramid, Bounds, Tile, TileCoord, TileStore +from tilecloud import BoundingPyramid, Bounds, NotSupportedOperation, Tile, TileCoord, TileStore class MaskTileStore(TileStore): @@ -13,7 +13,7 @@ class MaskTileStore(TileStore): def __init__(self, z: int, bounds: tuple[Bounds, Bounds], file: Optional[str] = None, **kwargs: Any): TileStore.__init__(self, **kwargs) - self.z = z + self.zoom = z self.xbounds, self.ybounds = bounds assert self.xbounds.start is not None assert self.xbounds.stop is not None @@ -22,7 +22,7 @@ def __init__(self, z: int, bounds: tuple[Bounds, Bounds], file: Optional[str] = self.width = self.xbounds.stop - self.xbounds.start self.height = self.ybounds.stop - self.ybounds.start if "bounding_pyramid" not in kwargs: - self.bounding_pyramid = BoundingPyramid({self.z: (self.xbounds, self.ybounds)}) + self.bounding_pyramid = BoundingPyramid({self.zoom: (self.xbounds, self.ybounds)}) if file: self.image = PIL.Image.open(file) assert self.image.mode == "1" @@ -34,9 +34,9 @@ def __init__(self, z: int, bounds: tuple[Bounds, Bounds], file: Optional[str] = def delete_one(self, tile: Tile) -> Tile: assert self.xbounds.start is not None assert self.ybounds.stop is not None - if tile.tilecoord.z == self.z: - x = tile.tilecoord.x - self.xbounds.start - y = self.ybounds.stop - tile.tilecoord.y - 1 + if tile.tilecoord.z == self.zoom: + x = tile.tilecoord.x - self.xbounds.start # pylint: disable=invalid-name + y = self.ybounds.stop - tile.tilecoord.y - 1 # pylint: disable=invalid-name if 0 <= x < self.width and 0 <= y < self.height: self.pixels[x, y] = 0 return tile @@ -44,19 +44,22 @@ def delete_one(self, tile: Tile) -> Tile: def list(self) -> Iterator[Tile]: assert self.xbounds.start is not None assert self.ybounds.stop is not None - for x in range(0, self.width): - for y in range(0, self.height): + for x in range(0, self.width): # pylint: disable=invalid-name + for y in range(0, self.height): # pylint: disable=invalid-name if self.pixels[x, y]: - yield Tile(TileCoord(self.z, self.xbounds.start + x, self.ybounds.stop - y - 1)) + yield Tile(TileCoord(self.zoom, self.xbounds.start + x, self.ybounds.stop - y - 1)) def put_one(self, tile: Tile) -> Tile: assert self.xbounds.start is not None assert self.ybounds.stop is not None - x = tile.tilecoord.x - self.xbounds.start - y = self.ybounds.stop - tile.tilecoord.y - 1 + x = tile.tilecoord.x - self.xbounds.start # pylint: disable=invalid-name + y = self.ybounds.stop - tile.tilecoord.y - 1 # pylint: disable=invalid-name if 0 <= x < self.width and 0 <= y < self.height: self.pixels[x, y] = 1 return tile - def save(self, file: str, format: type, **kwargs: Any) -> None: - self.image.save(file, format, **kwargs) + def save(self, file: str, format_pattern: type, **kwargs: Any) -> None: + self.image.save(file, format_pattern, **kwargs) + + def get_one(self, tile: Tile) -> Optional[Tile]: + raise NotSupportedOperation() diff --git a/tilecloud/store/mbtiles.py b/tilecloud/store/mbtiles.py index b757272e9..2f11c7ce8 100644 --- a/tilecloud/store/mbtiles.py +++ b/tilecloud/store/mbtiles.py @@ -51,21 +51,21 @@ def __init__(self, tilecoord_in_topleft: bool, *args: Any, **kwargs: Any) -> Non SQLiteDict.__init__(self, *args, **kwargs) def _packitem(self, key: TileCoord, value: Optional[bytes]) -> tuple[int, int, int, Optional[memoryview]]: - y = key.y if self.tilecoord_in_topleft else (1 << key.z) - key.y - 1 + y = key.y if self.tilecoord_in_topleft else (1 << key.z) - key.y - 1 # pylint: disable=invalid-name return (key.z, key.x, y, sqlite3.Binary(value) if value is not None else None) def _packkey(self, key: TileCoord) -> tuple[int, int, int]: - y = key.y if self.tilecoord_in_topleft else (1 << key.z) - key.y - 1 + y = key.y if self.tilecoord_in_topleft else (1 << key.z) - key.y - 1 # pylint: disable=invalid-name return (key.z, key.x, y) def _unpackitem(self, row: tuple[int, int, int, bytes]) -> tuple[TileCoord, bytes]: - z, x, y, data = row - y = y if self.tilecoord_in_topleft else (1 << z) - y - 1 + z, x, y, data = row # pylint: disable=invalid-name + y = y if self.tilecoord_in_topleft else (1 << z) - y - 1 # pylint: disable=invalid-name return (TileCoord(z, x, y), data) def _unpackkey(self, row: tuple[int, int, int]) -> TileCoord: - z, x, y = row - y = y if self.tilecoord_in_topleft else (1 << z) - y - 1 + z, x, y = row # pylint: disable=invalid-name + y = y if self.tilecoord_in_topleft else (1 << z) - y - 1 # pylint: disable=invalid-name return TileCoord(z, x, y) @@ -110,7 +110,9 @@ def get_all(self) -> Iterator[Tile]: def get_cheap_bounding_pyramid(self) -> BoundingPyramid: bounds = {} - for z, xstart, xstop, ystart, ystop in query(self.connection, self.BOUNDING_PYRAMID_SQL): + for z, xstart, xstop, ystart, ystop in query( # pylint: disable=invalid-name + self.connection, self.BOUNDING_PYRAMID_SQL + ): bounds[z] = (Bounds(xstart, xstop), Bounds(ystart, ystop)) return BoundingPyramid(bounds) diff --git a/tilecloud/store/metatile.py b/tilecloud/store/metatile.py index 25f143f08..111606bbd 100644 --- a/tilecloud/store/metatile.py +++ b/tilecloud/store/metatile.py @@ -4,13 +4,13 @@ from PIL import Image -from tilecloud import Tile, TileStore +from tilecloud import NotSupportedOperation, Tile, TileStore from tilecloud.lib.PIL_ import FORMAT_BY_CONTENT_TYPE class MetaTileSplitterTileStore(TileStore): - def __init__(self, format: str, tile_size: int = 256, border: int = 0, **kwargs: Any) -> None: - self.format = format + def __init__(self, format_pattern: str, tile_size: int = 256, border: int = 0, **kwargs: Any) -> None: + self.format = format_pattern self.tile_size = tile_size self.border = border TileStore.__init__(self, **kwargs) @@ -36,8 +36,12 @@ def get(self, tiles: Iterable[Optional[Tile]]) -> Iterator[Tile]: ) continue - x = self.border + (tilecoord.x - metatile.tilecoord.x) * self.tile_size - y = self.border + (tilecoord.y - metatile.tilecoord.y) * self.tile_size + x = ( # pylint: disable=invalid-name + self.border + (tilecoord.x - metatile.tilecoord.x) * self.tile_size + ) + y = ( # pylint: disable=invalid-name + self.border + (tilecoord.y - metatile.tilecoord.y) * self.tile_size + ) image = metaimage.crop((x, y, x + self.tile_size, y + self.tile_size)) bytes_io = BytesIO() image.save(bytes_io, FORMAT_BY_CONTENT_TYPE[self.format]) @@ -48,3 +52,12 @@ def get(self, tiles: Iterable[Optional[Tile]]) -> Iterator[Tile]: metadata=metatile.metadata, metatile=metatile, ) + + def get_one(self, tile: Tile) -> Tile: + raise NotSupportedOperation() + + def put_one(self, tile: Tile) -> Tile: + raise NotSupportedOperation() + + def delete_one(self, tile: Tile) -> Tile: + raise NotSupportedOperation() diff --git a/tilecloud/store/queue.py b/tilecloud/store/queue.py index 353b6cf50..aa993191e 100644 --- a/tilecloud/store/queue.py +++ b/tilecloud/store/queue.py @@ -22,9 +22,9 @@ def encode_message(tile: Tile) -> str: def decode_message(text: str, **kwargs: Any) -> Tile: body = json.loads(base64.b64decode(text).decode("utf-8")) - z = body.get("z") - x = body.get("x") - y = body.get("y") - n = body.get("n") + z = body.get("z") # pylint: disable=invalid-name + x = body.get("x") # pylint: disable=invalid-name + y = body.get("y") # pylint: disable=invalid-name + n = body.get("n") # pylint: disable=invalid-name metadata = body.get("metadata", {}) return Tile(TileCoord(z, x, y, n), metadata=metadata, **kwargs) diff --git a/tilecloud/store/redis.py b/tilecloud/store/redis.py index 9374f6d15..89c1615a9 100644 --- a/tilecloud/store/redis.py +++ b/tilecloud/store/redis.py @@ -90,8 +90,8 @@ def __init__( "Create the Redis stream name: %s, group name: %s, id: 0-0, MKSTREAM", name, STREAM_GROUP ) self._master.xgroup_create(name=self._name, groupname=STREAM_GROUP, id="0-0", mkstream=True) - except redis.ResponseError as e: - if "BUSYGROUP" not in str(e): + except redis.ResponseError as error: + if "BUSYGROUP" not in str(error): raise def __contains__(self, tile: Tile) -> bool: @@ -102,7 +102,7 @@ def get_one(self, tile: Tile) -> Tile: def list(self) -> Iterator[Tile]: count = 0 - while True: + while True: # pylint: disable=too-many-nested-blocks try: logger.debug( "Wait for new tiles, group name: %s, consumer name: %s, streams: %s, count: 1, " @@ -137,7 +137,7 @@ def list(self) -> Iterator[Tile]: try: tile = decode_message(body[b"message"], from_redis=True, sqs_message=id_) yield tile - except Exception: + except Exception: # pylint: disable=broad-except logger.warning("Failed decoding the Redis message", exc_info=True) _DECODE_ERROR_COUNTER.labels(self._name_str).inc() count += 1 @@ -160,9 +160,9 @@ def put_one(self, tile: Tile) -> Tile: "Add tile to the Redis stream name: %s, fields: %s", self._name, encode_message(tile) ) self._master.xadd(name=self._name, fields={"message": encode_message(tile)}) - except Exception as e: + except Exception as exception: # pylint: disable=broad-except logger.warning("Failed sending Redis message", exc_info=True) - tile.error = e + tile.error = exception return tile def put(self, tiles: Iterable[Tile]) -> Iterator[Tile]: @@ -315,9 +315,8 @@ def _claim_olds(self) -> tuple[Iterable[tuple[bytes, Any]], bool]: ) _STOLEN_COUNTER.labels(self._name_str).inc(len(to_steal)) return [(self._name, messages)], has_pendings - else: - # Empty means there are pending jobs, but they are not old enough to be stolen - return [], has_pendings + # Empty means there are pending jobs, but they are not old enough to be stolen + return [], has_pendings def get_status(self) -> dict[str, Union[str, int]]: """ diff --git a/tilecloud/store/renderingtheworld.py b/tilecloud/store/renderingtheworld.py index 67eede89b..cff9d51b0 100644 --- a/tilecloud/store/renderingtheworld.py +++ b/tilecloud/store/renderingtheworld.py @@ -2,7 +2,7 @@ from collections.abc import Iterator from typing import Callable, Deque, Optional -from tilecloud import Tile, TileGrid, TileStore +from tilecloud import NotSupportedOperation, Tile, TileGrid, TileStore from tilecloud.grid.quad import QuadTileGrid @@ -41,3 +41,9 @@ def put_one(self, tile: Tile) -> Tile: for tilecoord in self.tilegrid.children(tile.tilecoord): # type: ignore self.queue.append(Tile(tilecoord)) return tile + + def get_one(self, tile: Tile) -> Optional[Tile]: + raise NotSupportedOperation() + + def delete_one(self, tile: Tile) -> Tile: + raise NotSupportedOperation() diff --git a/tilecloud/store/s3.py b/tilecloud/store/s3.py index b00042113..ef37fee1f 100644 --- a/tilecloud/store/s3.py +++ b/tilecloud/store/s3.py @@ -47,8 +47,7 @@ def __contains__(self, tile: Tile) -> bool: except botocore.exceptions.ClientError as exc: if _get_status(exc) == 404: return False - else: - raise + raise def delete_one(self, tile: Tile) -> Tile: try: @@ -69,8 +68,7 @@ def get_one(self, tile: Tile) -> Optional[Tile]: except botocore.exceptions.ClientError as exc: if _get_status(exc) == 404: return None - else: - tile.error = exc + tile.error = exc return tile def list(self) -> Iterator[Tile]: diff --git a/tilecloud/store/searchup.py b/tilecloud/store/searchup.py index 5894b72f1..bd2063f72 100644 --- a/tilecloud/store/searchup.py +++ b/tilecloud/store/searchup.py @@ -1,6 +1,6 @@ from typing import Optional -from tilecloud import Tile, TileGrid, TileStore +from tilecloud import NotSupportedOperation, Tile, TileGrid, TileStore class SearchUpTileStore(TileStore): @@ -21,8 +21,13 @@ def get_one(self, tile: Tile) -> Optional[Tile]: if new_tile is not None: new_tile.tilecoord = tmp_tilecoord return new_tile - else: - tilecoord = self.tilegrid.parent(test_tile.tilecoord) - assert tilecoord is not None - test_tile.tilecoord = tilecoord + tilecoord = self.tilegrid.parent(test_tile.tilecoord) + assert tilecoord is not None + test_tile.tilecoord = tilecoord return None + + def put_one(self, tile: Tile) -> Tile: + raise NotSupportedOperation() + + def delete_one(self, tile: Tile) -> Tile: + raise NotSupportedOperation() diff --git a/tilecloud/store/sqs.py b/tilecloud/store/sqs.py index eacd81701..ff2835851 100644 --- a/tilecloud/store/sqs.py +++ b/tilecloud/store/sqs.py @@ -25,8 +25,7 @@ def maybe_stop(queue: "botocore.client.SQS") -> bool: if int(attributes["ApproximateNumberOfMessages"]) == 0: if int(attributes["ApproximateNumberOfMessagesNotVisible"]) == 0: return True - else: - time.sleep(int(attributes["VisibilityTimeout"]) / 4.0) + time.sleep(int(attributes["VisibilityTimeout"]) / 4.0) return False @@ -63,7 +62,7 @@ def list(self) -> Iterator[Tile]: try: tile = decode_message(sqs_message.body.encode("utf-8"), sqs_message=sqs_message) yield tile - except Exception: + except Exception: # pylint: disable=broad-except logger.warning("Failed decoding the SQS message", exc_info=True) sqs_message.delete() @@ -78,9 +77,9 @@ def put_one(self, tile: Tile) -> Tile: try: self.queue.send_message(MessageBody=sqs_message) - except Exception as e: + except Exception as exception: # pylint: disable=broad-except logger.warning("Failed sending SQS message", exc_info=True) - tile.error = e + tile.error = exception return tile def put(self, tiles: Iterable[Tile]) -> Iterator[Tile]: @@ -106,10 +105,10 @@ def _send_buffer(self, tiles: builtins.list[Tile]) -> None: logger.warning("Failed sending SQS message: %s", failed["Message"]) pos = int(failed["Id"]) tiles[pos].error = failed["Message"] - except Exception as e: + except Exception as exception: # pylint: disable=broad-except logger.warning("Failed sending SQS messages", exc_info=True) for tile in tiles: - tile.error = e + tile.error = exception def get_status(self) -> dict[str, str]: """ diff --git a/tilecloud/store/tilejson.py b/tilecloud/store/tilejson.py index e556f1a6a..e2f2be211 100644 --- a/tilecloud/store/tilejson.py +++ b/tilecloud/store/tilejson.py @@ -11,7 +11,7 @@ from urllib2 import urlopen # pylint: disable=import-error -from tilecloud import BoundingPyramid +from tilecloud import BoundingPyramid, NotSupportedOperation, Tile from tilecloud.layout.template import TemplateTileLayout from tilecloud.store.url import URLTileStore @@ -22,23 +22,23 @@ class TileJSONTileStore(URLTileStore): def __init__(self, tile_json: str, urls_key: str = "tiles", **kwargs: Any): # FIXME schema # FIXME version 1.0.0 support - d = json.loads(tile_json) - assert "tiles" in d - assert isinstance(d["tiles"], list) - assert len(d["tiles"]) > 0 + tile = json.loads(tile_json) + assert "tiles" in tile + assert isinstance(tile["tiles"], list) + assert len(tile["tiles"]) > 0 for key in self.KEYS: - kwargs.setdefault(key, d.get(key)) + kwargs.setdefault(key, tile.get(key)) if "bounding_pyramid" not in kwargs: - zmin, zmax = d.get("minzoom", 0), d.get("maxzoom", 22) - if "bounds" in d: - lonmin, latmin, lonmax, latmax = d["bounds"] + zmin, zmax = tile.get("minzoom", 0), tile.get("maxzoom", 22) + if "bounds" in tile: + lonmin, latmin, lonmax, latmax = tile["bounds"] bounding_pyramid = BoundingPyramid.from_wgs84( # type: ignore zmin, zmax, lonmin, lonmax, latmin, latmax ) else: bounding_pyramid = BoundingPyramid.full(zmin, zmax) kwargs["bounding_pyramid"] = bounding_pyramid - urls = d[urls_key] + urls = tile[urls_key] if "content_type" not in kwargs: exts = {os.path.splitext(urlparse(url1).path)[1] for url1 in urls} content_types = {mimetypes.types_map.get(ext) for ext in exts} @@ -51,3 +51,6 @@ def __init__(self, tile_json: str, urls_key: str = "tiles", **kwargs: Any): @classmethod def from_url(cls, url: str) -> Any: return cls(urlopen(url).read()) # nosec + + def delete_one(self, tile: Tile) -> Tile: + raise NotSupportedOperation() diff --git a/tilecloud/store/url.py b/tilecloud/store/url.py index 6294f65a5..e80508808 100644 --- a/tilecloud/store/url.py +++ b/tilecloud/store/url.py @@ -4,7 +4,7 @@ import requests -from tilecloud import Tile, TileLayout, TileStore +from tilecloud import NotSupportedOperation, Tile, TileLayout, TileStore logger = logging.getLogger(__name__) @@ -33,8 +33,8 @@ def get_one(self, tile: Tile) -> Optional[Tile]: tilelayout = self.tilelayouts[hash(tile.tilecoord) % len(self.tilelayouts)] try: url = tilelayout.filename(tile.tilecoord, tile.metadata) - except Exception as e: - tile.error = e + except Exception as exception: # pylint: disable=broad-except + tile.error = exception return tile logger.info("GET %s", url) @@ -60,6 +60,12 @@ def get_one(self, tile: Tile) -> Optional[Tile]: else: tile.error = response.reason - except requests.exceptions.RequestException as e: - tile.error = e + except requests.exceptions.RequestException as exception: + tile.error = exception return tile + + def put_one(self, tile: Tile) -> Tile: + raise NotSupportedOperation() + + def delete_one(self, tile: Tile) -> Tile: + raise NotSupportedOperation() diff --git a/tilecloud/store/wmts.py b/tilecloud/store/wmts.py index 7d5f6e5c6..eae5a01da 100644 --- a/tilecloud/store/wmts.py +++ b/tilecloud/store/wmts.py @@ -1,5 +1,6 @@ from typing import Any, Callable, Optional +from tilecloud import NotSupportedOperation, Tile from tilecloud.layout.wmts import WMTSTileLayout from tilecloud.store.url import URLTileStore @@ -10,10 +11,13 @@ def __init__( url: str = "", layer: Optional[str] = None, style: Optional[str] = None, - format: Optional[str] = None, + format_pattern: Optional[str] = None, tile_matrix_set: Optional[str] = None, tile_matrix: Callable[[int], str] = str, **kwargs: Any, ): - layout = WMTSTileLayout(url, layer, style, format, tile_matrix_set, tile_matrix) + layout = WMTSTileLayout(url, layer, style, format_pattern, tile_matrix_set, tile_matrix) URLTileStore.__init__(self, (layout,), **kwargs) + + def delete_one(self, tile: Tile) -> Tile: + raise NotSupportedOperation() diff --git a/tilecloud/store/zip.py b/tilecloud/store/zip.py index b4a6c7701..2d5c54893 100644 --- a/tilecloud/store/zip.py +++ b/tilecloud/store/zip.py @@ -6,7 +6,7 @@ from datetime import datetime from typing import Any, Optional -from tilecloud import Tile, TileLayout, TileStore +from tilecloud import NotSupportedOperation, Tile, TileLayout, TileStore from tilecloud.layout.osm import OSMTileLayout from tilecloud.layout.wrapped import WrappedTileLayout @@ -69,3 +69,6 @@ def put_one(self, tile: Tile) -> Tile: zipinfo.external_attr = 0o644 << 16 self.zipfile.writestr(zipinfo, tile.data) return tile + + def delete_one(self, tile: Tile) -> Tile: + raise NotSupportedOperation() diff --git a/tilecloud/tests/test_tilelayout.py b/tilecloud/tests/test_tilelayout.py index 3cf10924f..e088c4489 100644 --- a/tilecloud/tests/test_tilelayout.py +++ b/tilecloud/tests/test_tilelayout.py @@ -37,7 +37,7 @@ def setUp(self) -> None: url="test", layer="layer", style="default", - format=".png", + format_pattern=".png", dimensions_name=("DATE",), tile_matrix_set="swissgrid", request_encoding="REST", @@ -46,7 +46,7 @@ def setUp(self) -> None: url="test", layer="layer", style="default", - format=".png", + format_pattern=".png", dimensions_name=("DATE",), tile_matrix_set="swissgrid", request_encoding="KVP", @@ -54,7 +54,7 @@ def setUp(self) -> None: self.rest_nourl = WMTSTileLayout( layer="layer", style="default", - format=".png", + format_pattern=".png", dimensions_name=("DATE",), tile_matrix_set="swissgrid", request_encoding="REST", @@ -62,7 +62,7 @@ def setUp(self) -> None: self.kvp_nourl = WMTSTileLayout( layer="layer", style="default", - format=".png", + format_pattern=".png", dimensions_name=("DATE",), tile_matrix_set="swissgrid", request_encoding="KVP", @@ -104,7 +104,7 @@ def test_png(self) -> None: url="http://example.com/folder", layers="l1,l2", srs="EPSG:1000", - format="image/png", + format_pattern="image/png", tilegrid=self.tilegrid, ) result = urlparse(layout.filename(TileCoord(0, 0, 0))) @@ -128,7 +128,7 @@ def test_jpeg(self) -> None: url="http://example.com/folder", layers="l1,l2", srs="EPSG:1000", - format="image/jpeg", + format_pattern="image/jpeg", tilegrid=self.tilegrid, ) result = urlparse(layout.filename(TileCoord(0, 0, 0))) @@ -152,7 +152,7 @@ def test_border(self) -> None: url="http://example.com/folder", layers="l1,l2", srs="EPSG:1000", - format="image/png", + format_pattern="image/png", tilegrid=self.tilegrid, border=10, ) @@ -178,7 +178,7 @@ def test_subx_metric(self) -> None: url="http://example.com/folder", layers="l1,l2", srs="EPSG:1000", - format="image/png", + format_pattern="image/png", tilegrid=self.tilegrid, ) result = urlparse(layout.filename(TileCoord(2, 0, 0))) @@ -202,7 +202,7 @@ def test_metatile(self) -> None: url="http://example.com/folder", layers="l1,l2", srs="EPSG:1000", - format="image/png", + format_pattern="image/png", tilegrid=self.tilegrid, ) result = urlparse(layout.filename(TileCoord(1, 0, 0, 2))) @@ -226,7 +226,7 @@ def test_metatile_border(self) -> None: url="http://example.com/folder", layers="l1,l2", srs="EPSG:1000", - format="image/png", + format_pattern="image/png", tilegrid=self.tilegrid, border=5, ) @@ -251,7 +251,7 @@ def test_params(self) -> None: url="http://example.com/folder", layers="l1,l2", srs="EPSG:1000", - format="image/png", + format_pattern="image/png", tilegrid=self.tilegrid, params={"TRANSPARENT": "FALSE", "PARAM": "Value", "FILTER": 'l1:"field" = ' "{PARAM}" ""}, )