diff --git a/.coveragerc b/.coveragerc index 171cfe5..c339195 100644 --- a/.coveragerc +++ b/.coveragerc @@ -5,4 +5,5 @@ sigterm = True exclude_lines = pragma: no cover if __name__ == .__main__.: + if TYPE_CHECKING: pass diff --git a/.gitignore b/.gitignore index 43f367b..76c20f1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,22 +1,27 @@ +# Common Files *.egg-info *.pyc *.pyo .DS_Store .coverage* -.fleet -.idea -.ipynb_checkpoints +uv.lock + +# Common Directories +.fleet/ +.idea/ +.ipynb_checkpoints/ .python-version -.sandbox -.vs -.vscode +.vs/ +.vscode/ +.sandbox/ +build/ +dist/ +docs/_build/ +docs/generated/ +node_modules/ +references/ __pycache__ -build -dist -docs/_build -docs/generated -references +# Project Directories zenodo -uv.lock diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1b50f8a..c52c83d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: "v4.5.0" + rev: "v5.0.0" hooks: - id: check-added-large-files - id: check-case-conflict @@ -15,32 +15,29 @@ repos: - id: requirements-txt-fixer - id: trailing-whitespace - repo: https://github.com/codespell-project/codespell - rev: v2.2.6 + rev: v2.3.0 hooks: - id: codespell - args: ["--ignore-words-list=exitance,seperately"] + args: + ["--ignore-words-list=assertIn,exitance,seperately,socio-economic"] exclude: "BIBLIOGRAPHY.bib|CONTRIBUTORS.rst" - - repo: https://github.com/ikamensh/flynt - rev: "1.0.1" - hooks: - - id: flynt - args: [--verbose] - repo: https://github.com/PyCQA/isort rev: "5.13.2" hooks: - id: isort - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.1.14" + rev: "v0.8.2" hooks: - id: ruff-format - id: ruff + args: [--fix] - repo: https://github.com/adamchainz/blacken-docs - rev: 1.16.0 + rev: 1.19.1 hooks: - id: blacken-docs language_version: python3.10 - repo: https://github.com/pre-commit/mirrors-prettier - rev: "v3.1.0" + rev: "v4.0.0-alpha.8" hooks: - id: prettier - repo: https://github.com/pre-commit/pygrep-hooks diff --git a/README.rst b/README.rst index 2f18555..25e858e 100644 --- a/README.rst +++ b/README.rst @@ -217,7 +217,8 @@ The *Colour Developers* can be reached via different means: - `Facebook `__ - `Github Discussions `__ - `Gitter `__ -- `Twitter `__ +- `X `__ +- `Bluesky `__ About ----- diff --git a/colour_datasets/__init__.py b/colour_datasets/__init__.py index b621187..c06086b 100644 --- a/colour_datasets/__init__.py +++ b/colour_datasets/__init__.py @@ -15,6 +15,10 @@ - utilities: Various utilities. """ +# isort: skip_file + +from __future__ import annotations + import contextlib import os import subprocess @@ -52,19 +56,19 @@ __major_version__ = "0" __minor_version__ = "2" __change_version__ = "6" -__version__ = ".".join((__major_version__, __minor_version__, __change_version__)) +__version__ = f"{__major_version__}.{__minor_version__}.{__change_version__}" try: _version = ( - subprocess.check_output( - ["git", "describe"], # noqa: S603, S607 + subprocess.check_output( # noqa: S603 + ["git", "describe"], # noqa: S607 cwd=os.path.dirname(__file__), stderr=subprocess.STDOUT, ) .strip() .decode("utf-8") ) -except Exception: +except Exception: # noqa: BLE001 _version = __version__ colour.utilities.ANCILLARY_COLOUR_SCIENCE_PACKAGES["colour-datasets"] = _version # pyright: ignore diff --git a/colour_datasets/loaders/__init__.py b/colour_datasets/loaders/__init__.py index ec8dcaa..76ecf2d 100644 --- a/colour_datasets/loaders/__init__.py +++ b/colour_datasets/loaders/__init__.py @@ -1,8 +1,13 @@ +# isort: skip_file + from __future__ import annotations import sys +import typing + +if typing.TYPE_CHECKING: + from colour.hints import Any -from colour.hints import Any from colour.utilities import CanonicalMapping, warning from colour_datasets.records import datasets diff --git a/colour_datasets/loaders/abstract.py b/colour_datasets/loaders/abstract.py index 433b195..733cb36 100644 --- a/colour_datasets/loaders/abstract.py +++ b/colour_datasets/loaders/abstract.py @@ -9,11 +9,13 @@ from __future__ import annotations +import typing from abc import ABC, abstractmethod -from colour.hints import Any +if typing.TYPE_CHECKING: + from colour.hints import Any -from colour_datasets.records import Record + from colour_datasets.records import Record __author__ = "Colour Developers" __copyright__ = "Copyright 2019 Colour Developers" @@ -122,7 +124,7 @@ def load(self) -> Any: when they implement it, e.g., ``super().sync()``. """ - def sync(self): + def sync(self) -> None: """ Sync the dataset content, i.e., checks whether it is synced and pulls it if required. diff --git a/colour_datasets/loaders/asano2015.py b/colour_datasets/loaders/asano2015.py index 66b998f..13a6a8c 100644 --- a/colour_datasets/loaders/asano2015.py +++ b/colour_datasets/loaders/asano2015.py @@ -17,7 +17,8 @@ from __future__ import annotations import os -from collections import namedtuple +import typing +from dataclasses import dataclass, field import numpy as np import xlrd @@ -26,7 +27,10 @@ LMS_ConeFundamentals, XYZ_ColourMatchingFunctions, ) -from colour.hints import Dict, NDArrayFloat + +if typing.TYPE_CHECKING: + from colour.hints import Dict + from colour.utilities import as_float_array, tstack from colour_datasets.loaders import AbstractDatasetLoader @@ -47,12 +51,8 @@ ] -class Specification_Asano2015( - namedtuple( - "Specification_Asano2015", - ("XYZ_2", "XYZ_10", "LMS_2", "LMS_10", "parameters", "others"), - ) -): +@dataclass(frozen=True) +class Specification_Asano2015: """ Define the *Asano (2015)* specification for an observer. @@ -74,24 +74,14 @@ class Specification_Asano2015( References ---------- :cite:`Asano2015` - """ # noqa: D405, D407, D410, D411 - - def __new__( - cls, - XYZ_2: XYZ_ColourMatchingFunctions, - XYZ_10: XYZ_ColourMatchingFunctions, - LMS_2: LMS_ConeFundamentals, - LMS_10: LMS_ConeFundamentals, - parameters: NDArrayFloat, - others: Dict | None = None, - ): - """ - Return a new instance of the - :class:`colour_datasets.loaders.asano2015.Specification_Asano2015` - class. - """ + """ - return super().__new__(cls, XYZ_2, XYZ_10, LMS_2, LMS_10, parameters, others) + XYZ_2: XYZ_ColourMatchingFunctions + XYZ_10: XYZ_ColourMatchingFunctions + LMS_2: LMS_ConeFundamentals + LMS_10: LMS_ConeFundamentals + parameters: Dict + others: Dict = field(default_factory=dict) class DatasetLoader_Asano2015(AbstractDatasetLoader): @@ -199,7 +189,7 @@ def load(self) -> Dict[str, Dict[int, Specification_Asano2015]]: observer["LMS_2"], observer["LMS_10"], observer["parameters"], - dict(zip(header, values[i])), + dict(zip(header, values[i], strict=False)), ) return self._content @@ -284,7 +274,9 @@ def parse_workbook_Asano2015( for i in range(observers[1]): observer = i + 1 - data[observer]["parameters"] = dict(zip(header, as_float_array(values[i]))) + data[observer]["parameters"] = dict( + zip(header, as_float_array(values[i]), strict=False) + ) return data diff --git a/colour_datasets/loaders/brendel2020.py b/colour_datasets/loaders/brendel2020.py index 343b636..c7d311f 100644 --- a/colour_datasets/loaders/brendel2020.py +++ b/colour_datasets/loaders/brendel2020.py @@ -18,10 +18,14 @@ from __future__ import annotations import os +import typing import numpy as np from colour import LinearInterpolator, SpectralDistribution, SpectralShape -from colour.hints import Dict + +if typing.TYPE_CHECKING: + from colour.hints import Dict + from colour.utilities import as_int from colour_datasets.loaders import AbstractDatasetLoader diff --git a/colour_datasets/loaders/dyer2017.py b/colour_datasets/loaders/dyer2017.py index 7f69217..44098b2 100644 --- a/colour_datasets/loaders/dyer2017.py +++ b/colour_datasets/loaders/dyer2017.py @@ -19,10 +19,14 @@ import glob import json import os +import typing from colour import MultiSpectralDistributions, SpectralDistribution from colour.continuous import MultiSignals, Signal -from colour.hints import Any, Dict, Literal + +if typing.TYPE_CHECKING: + from colour.hints import Any, Dict, Literal + from colour.utilities import attest, is_numeric, optional from colour_datasets.loaders import AbstractDatasetLoader @@ -163,7 +167,7 @@ def schema_version(self) -> str | None: return self._schema_version @schema_version.setter - def schema_version(self, value: str | None): + def schema_version(self, value: str | None) -> None: """Setter for the **self.schema_version** property.""" if value is not None: @@ -193,7 +197,7 @@ def catalog_number(self) -> str | None: return self._catalog_number @catalog_number.setter - def catalog_number(self, value: str | None): + def catalog_number(self, value: str | None) -> None: """Setter for the **self.catalog_number** property.""" if value is not None: @@ -223,7 +227,7 @@ def description(self) -> str | None: return self._description @description.setter - def description(self, value: str | None): + def description(self, value: str | None) -> None: """Setter for the **self.description** property.""" if value is not None: @@ -253,7 +257,7 @@ def document_creator(self) -> str | None: return self._document_creator @document_creator.setter - def document_creator(self, value: str | None): + def document_creator(self, value: str | None) -> None: """Setter for the **self.document_creator** property.""" if value is not None: @@ -283,7 +287,7 @@ def unique_identifier(self) -> str | None: return self._unique_identifier @unique_identifier.setter - def unique_identifier(self, value: str | None): + def unique_identifier(self, value: str | None) -> None: """Setter for the **self.unique_identifier** property.""" if value is not None: @@ -313,7 +317,7 @@ def measurement_equipment(self) -> str | None: return self._measurement_equipment @measurement_equipment.setter - def measurement_equipment(self, value: str | None): + def measurement_equipment(self, value: str | None) -> None: """Setter for the **self.measurement_equipment** property.""" if value is not None: @@ -343,7 +347,7 @@ def laboratory(self) -> str | None: return self._laboratory @laboratory.setter - def laboratory(self, value: str | None): + def laboratory(self, value: str | None) -> None: """Setter for the **self.measurement_equipment** property.""" if value is not None: @@ -373,7 +377,7 @@ def document_creation_date(self) -> str | None: return self._document_creation_date @document_creation_date.setter - def document_creation_date(self, value: str | None): + def document_creation_date(self, value: str | None) -> None: """Setter for the **self.document_creation_date** property.""" if value is not None: @@ -403,7 +407,7 @@ def comments(self) -> str | None: return self._comments @comments.setter - def comments(self, value: str | None): + def comments(self, value: str | None) -> None: """Setter for the **self.comments** property.""" if value is not None: @@ -433,7 +437,7 @@ def license(self) -> str | None: return self._license @license.setter - def license(self, value: str | None): + def license(self, value: str | None) -> None: """Setter for the **self.license** property.""" if value is not None: @@ -564,39 +568,45 @@ def __init__( self.path = path self._header: SpectralDataHeader_AMPAS = SpectralDataHeader_AMPAS() self.header = optional(header, self._header) - self._units: Literal[ - "absorptance", - "exitance", - "flux", - "intensity", - "irradiance", - "radiance", - "reflectance", - "relative", - "transmittance", - "R-Factor", - "T-Factor", - "other", - ] | None = None + self._units: ( + Literal[ + "absorptance", + "exitance", + "flux", + "intensity", + "irradiance", + "radiance", + "reflectance", + "relative", + "transmittance", + "R-Factor", + "T-Factor", + "other", + ] + | None + ) = None self.units = units - self._reflection_geometry: Literal[ - "di:8", - "de:8", - "8:di", - "8:de", - "d:d", - "d:0", - "45a:0", - "45c:0", - "0:45a", - "45x:0", - "0:45x", - "other", - ] | None = None + self._reflection_geometry: ( + Literal[ + "di:8", + "de:8", + "8:di", + "8:de", + "d:d", + "d:0", + "45a:0", + "45c:0", + "0:45a", + "45x:0", + "0:45x", + "other", + ] + | None + ) = None self.reflection_geometry = reflection_geometry - self._transmission_geometry: Literal[ - "0:0", "di:0", "de:0", "0:di", "0:de", "d:d", "other" - ] | None = None + self._transmission_geometry: ( + Literal["0:0", "di:0", "de:0", "0:di", "0:de", "d:d", "other"] | None + ) = None self.transmission_geometry = transmission_geometry self._bandwidth_FWHM: float | None = None self.bandwidth_FWHM = bandwidth_FWHM @@ -622,7 +632,7 @@ def path(self) -> str | None: return self._path @path.setter - def path(self, value: str | None): + def path(self, value: str | None) -> None: """Setter for the **self.path** property.""" if value is not None: @@ -651,7 +661,7 @@ def header(self) -> SpectralDataHeader_AMPAS: return self._header @header.setter - def header(self, value: SpectralDataHeader_AMPAS): + def header(self, value: SpectralDataHeader_AMPAS) -> None: """Setter for the **self.header** property.""" attest( @@ -714,7 +724,7 @@ def units( "other", ] | None, - ): + ) -> None: """Setter for the **self.units** property.""" if value is not None: @@ -779,7 +789,7 @@ def reflection_geometry( "other", ] | None, - ): + ) -> None: """Setter for the **self.reflection_geometry** property.""" if value is not None: @@ -814,7 +824,7 @@ def transmission_geometry( def transmission_geometry( self, value: Literal["0:0", "di:0", "de:0", "0:di", "0:de", "d:d", "other"] | None, - ): + ) -> None: """Setter for the **self.transmission_geometry** property.""" if value is not None: @@ -844,7 +854,7 @@ def bandwidth_FWHM(self) -> float | None: return self._bandwidth_FWHM @bandwidth_FWHM.setter - def bandwidth_FWHM(self, value: float | None): + def bandwidth_FWHM(self, value: float | None) -> None: """Setter for the **self.bandwidth_FWHM** property.""" if value is not None: @@ -875,7 +885,7 @@ def bandwidth_corrected(self) -> bool | None: return self._bandwidth_corrected @bandwidth_corrected.setter - def bandwidth_corrected(self, value: bool | None): + def bandwidth_corrected(self, value: bool | None) -> None: """Setter for the **self.bandwidth_corrected** property.""" if value is not None: @@ -924,23 +934,23 @@ def read(self) -> SpectralDistribution: # attributes according to outcome of # https://github.com/ampas/rawtoaces/issues/114. if ( - "manufacturer" in self._header._kwargs - and "model" in self._header._kwargs + "manufacturer" in self._header._kwargs # noqa: SLF001 + and "model" in self._header._kwargs # noqa: SLF001 ): self.name = ( - f"{self._header._kwargs['manufacturer']} " - f"{self._header._kwargs['model']}" + f"{self._header._kwargs['manufacturer']} " # noqa: SLF001 + f"{self._header._kwargs['model']}" # noqa: SLF001 ) - elif "illuminant" in self._header._kwargs: - self.name = self._header._kwargs["illuminant"] - elif "type" in self._header._kwargs: - self.name = self._header._kwargs["type"] + elif "illuminant" in self._header._kwargs: # noqa: SLF001 + self.name = self._header._kwargs["illuminant"] # noqa: SLF001 + elif "type" in self._header._kwargs: # noqa: SLF001 + self.name = self._header._kwargs["type"] # noqa: SLF001 self.display_name = self.name return self - else: - raise ValueError("The spectral distribution path is undefined!") + msg = "The spectral distribution path is undefined!" + raise ValueError(msg) class MultiSpectralDistributions_AMPAS(MultiSpectralDistributions): @@ -1062,39 +1072,45 @@ def __init__( self.path = path self._header: SpectralDataHeader_AMPAS = SpectralDataHeader_AMPAS() self.header = optional(header, self._header) - self._units: Literal[ - "absorptance", - "exitance", - "flux", - "intensity", - "irradiance", - "radiance", - "reflectance", - "relative", - "transmittance", - "R-Factor", - "T-Factor", - "other", - ] | None = None + self._units: ( + Literal[ + "absorptance", + "exitance", + "flux", + "intensity", + "irradiance", + "radiance", + "reflectance", + "relative", + "transmittance", + "R-Factor", + "T-Factor", + "other", + ] + | None + ) = None self.units = units - self._reflection_geometry: Literal[ - "di:8", - "de:8", - "8:di", - "8:de", - "d:d", - "d:0", - "45a:0", - "45c:0", - "0:45a", - "45x:0", - "0:45x", - "other", - ] | None = None + self._reflection_geometry: ( + Literal[ + "di:8", + "de:8", + "8:di", + "8:de", + "d:d", + "d:0", + "45a:0", + "45c:0", + "0:45a", + "45x:0", + "0:45x", + "other", + ] + | None + ) = None self.reflection_geometry = reflection_geometry - self._transmission_geometry: Literal[ - "0:0", "di:0", "de:0", "0:di", "0:de", "d:d", "other" - ] | None = None + self._transmission_geometry: ( + Literal["0:0", "di:0", "de:0", "0:di", "0:de", "d:d", "other"] | None + ) = None self.transmission_geometry = transmission_geometry self._bandwidth_FWHM: float | None = None self.bandwidth_FWHM = bandwidth_FWHM @@ -1120,7 +1136,7 @@ def path(self) -> str | None: return self._path @path.setter - def path(self, value: str | None): + def path(self, value: str | None) -> None: """Setter for the **self.path** property.""" if value is not None: @@ -1149,7 +1165,7 @@ def header(self) -> SpectralDataHeader_AMPAS: return self._header @header.setter - def header(self, value: SpectralDataHeader_AMPAS): + def header(self, value: SpectralDataHeader_AMPAS) -> None: """Setter for the **self.header** property.""" attest( @@ -1212,7 +1228,7 @@ def units( "other", ] | None, - ): + ) -> None: """Setter for the **self.units** property.""" if value is not None: @@ -1277,7 +1293,7 @@ def reflection_geometry( "other", ] | None, - ): + ) -> None: """Setter for the **self.reflection_geometry** property.""" if value is not None: @@ -1312,7 +1328,7 @@ def transmission_geometry( def transmission_geometry( self, value: Literal["0:0", "di:0", "de:0", "0:di", "0:de", "d:d", "other"] | None, - ): + ) -> None: """Setter for the **self.transmission_geometry** property.""" if value is not None: @@ -1342,7 +1358,7 @@ def bandwidth_FWHM(self) -> float | None: return self._bandwidth_FWHM @bandwidth_FWHM.setter - def bandwidth_FWHM(self, value: float | None): + def bandwidth_FWHM(self, value: float | None) -> None: """Setter for the **self.bandwidth_FWHM** property.""" if value is not None: @@ -1373,7 +1389,7 @@ def bandwidth_corrected(self) -> bool | None: return self._bandwidth_corrected @bandwidth_corrected.setter - def bandwidth_corrected(self, value: bool | None): + def bandwidth_corrected(self, value: bool | None) -> None: """Setter for the **self.bandwidth_corrected** property.""" if value is not None: @@ -1421,24 +1437,24 @@ def read(self) -> MultiSpectralDistributions: # attributes according to outcome of # https://github.com/ampas/rawtoaces/issues/114. if ( - "manufacturer" in self._header._kwargs - and "model" in self._header._kwargs + "manufacturer" in self._header._kwargs # noqa: SLF001 + and "model" in self._header._kwargs # noqa: SLF001 ): self.name = ( - f"{self._header._kwargs['manufacturer']} " - f"{self._header._kwargs['model']}" + f"{self._header._kwargs['manufacturer']} " # noqa: SLF001 + f"{self._header._kwargs['model']}" # noqa: SLF001 ) - elif "illuminant" in self._header._kwargs: - self.name = self._header._kwargs["illuminant"] - elif "type" in self._header._kwargs: - self.name = self._header._kwargs["type"] + elif "illuminant" in self._header._kwargs: # noqa: SLF001 + self.name = self._header._kwargs["illuminant"] # noqa: SLF001 + elif "type" in self._header._kwargs: # noqa: SLF001 + self.name = self._header._kwargs["type"] # noqa: SLF001 self.display_name = self.name self.display_labels = self.labels return self - else: - raise ValueError("The multi-spectral distributions path is undefined!") + msg = "The multi-spectral distributions path is undefined!" + raise ValueError(msg) class DatasetLoader_Dyer2017(AbstractDatasetLoader): diff --git a/colour_datasets/loaders/ebner1998.py b/colour_datasets/loaders/ebner1998.py index 2552ea0..8b1141e 100644 --- a/colour_datasets/loaders/ebner1998.py +++ b/colour_datasets/loaders/ebner1998.py @@ -20,10 +20,14 @@ import codecs import os -from collections import namedtuple +import typing +from dataclasses import dataclass import numpy as np -from colour.hints import Dict, NDArrayFloat + +if typing.TYPE_CHECKING: + from colour.hints import Dict, NDArrayFloat + from colour.utilities import as_float_array from colour_datasets.loaders import AbstractDatasetLoader @@ -43,12 +47,8 @@ ] -class ConstantPerceivedHueColourMatches_Ebner1998( - namedtuple( - "ConstantPerceivedHueColourMatches_Ebner1998", - ("name", "XYZ_r", "XYZ_cr", "XYZ_ct", "metadata"), - ) -): +@dataclass(frozen=True) +class ConstantPerceivedHueColourMatches_Ebner1998: """ Define *Ebner and Fairchild (1998)* *Constant Perceived-Hue Data* colour matches data for a given hue angle. @@ -70,6 +70,19 @@ class ConstantPerceivedHueColourMatches_Ebner1998( Dataset metadata. """ + name: str + XYZ_r: NDArrayFloat + XYZ_cr: NDArrayFloat + XYZ_ct: NDArrayFloat + metadata: Dict + + def __post_init__(self) -> None: + """Post-initialise the class.""" + + object.__setattr__(self, "XYZ_r", as_float_array(self.XYZ_r)) + object.__setattr__(self, "XYZ_cr", as_float_array(self.XYZ_cr)) + object.__setattr__(self, "XYZ_ct", as_float_array(self.XYZ_ct)) + class DatasetLoader_Ebner1998(AbstractDatasetLoader): """ @@ -148,14 +161,14 @@ def _parse_float_values(data: str) -> NDArrayFloat: attribute, value = line.split("\t", 1) hue, data = int(attribute), _parse_float_values(value) - self._content["Constant Perceived-Hue Data"][ - hue - ] = ConstantPerceivedHueColourMatches_Ebner1998( - f"Reference Hue Angle - {hue}", - XYZ_r, - data[0], - data[1:], - {"h": hue}, + self._content["Constant Perceived-Hue Data"][hue] = ( + ConstantPerceivedHueColourMatches_Ebner1998( + f"Reference Hue Angle - {hue}", + XYZ_r, + data[0], + data[1:], + {"h": hue}, + ) ) return self._content diff --git a/colour_datasets/loaders/hung1995.py b/colour_datasets/loaders/hung1995.py index 27a7bea..6d89d73 100644 --- a/colour_datasets/loaders/hung1995.py +++ b/colour_datasets/loaders/hung1995.py @@ -19,11 +19,16 @@ from __future__ import annotations import os -from collections import namedtuple +import typing +from dataclasses import dataclass import numpy as np from colour import CCS_ILLUMINANTS, xy_to_XYZ, xyY_to_XYZ -from colour.hints import Dict + +if typing.TYPE_CHECKING: + from colour.hints import Dict, NDArrayFloat + +from colour.utilities import as_float_array from colour_datasets.loaders import AbstractDatasetLoader from colour_datasets.records import datasets @@ -42,12 +47,8 @@ ] -class ConstantPerceivedHueColourMatches_Hung1995( - namedtuple( - "ConstantPerceivedHueColourMatches_Hung1995", - ("name", "XYZ_r", "XYZ_cr", "XYZ_ct", "metadata"), - ) -): +@dataclass(frozen=True) +class ConstantPerceivedHueColourMatches_Hung1995: """ Define *Hung and Berns (1995)* *Constant Hue Loci Data* colour matches data for a given hue angle. @@ -69,6 +70,19 @@ class ConstantPerceivedHueColourMatches_Hung1995( Dataset metadata. """ + name: str + XYZ_r: NDArrayFloat + XYZ_cr: NDArrayFloat + XYZ_ct: NDArrayFloat + metadata: Dict + + def __post_init__(self) -> None: + """Post-initialise the class.""" + + object.__setattr__(self, "XYZ_r", as_float_array(self.XYZ_r)) + object.__setattr__(self, "XYZ_cr", as_float_array(self.XYZ_cr)) + object.__setattr__(self, "XYZ_ct", as_float_array(self.XYZ_ct)) + class DatasetLoader_Hung1995(AbstractDatasetLoader): """ diff --git a/colour_datasets/loaders/jakob2019.py b/colour_datasets/loaders/jakob2019.py index 3d6155c..a132a9f 100644 --- a/colour_datasets/loaders/jakob2019.py +++ b/colour_datasets/loaders/jakob2019.py @@ -19,8 +19,11 @@ import glob import os +import typing + +if typing.TYPE_CHECKING: + from colour.hints import Dict -from colour.hints import Dict from colour.recovery import LUT3D_Jakob2019 from colour_datasets.loaders import AbstractDatasetLoader diff --git a/colour_datasets/loaders/jiang2013.py b/colour_datasets/loaders/jiang2013.py index 8f7f157..9b34dc1 100644 --- a/colour_datasets/loaders/jiang2013.py +++ b/colour_datasets/loaders/jiang2013.py @@ -21,11 +21,15 @@ import codecs import os import re +import typing import numpy as np from colour import SpectralShape + +if typing.TYPE_CHECKING: + from colour.hints import Dict + from colour.characterisation import RGB_CameraSensitivities -from colour.hints import Dict from colour.utilities import as_float_array from colour_datasets.loaders import AbstractDatasetLoader diff --git a/colour_datasets/loaders/karge2015.py b/colour_datasets/loaders/karge2015.py index 5e5526a..048aa8b 100644 --- a/colour_datasets/loaders/karge2015.py +++ b/colour_datasets/loaders/karge2015.py @@ -20,10 +20,14 @@ import os import re +import typing from collections import defaultdict from colour.algebra import LinearInterpolator -from colour.hints import Dict + +if typing.TYPE_CHECKING: + from colour.hints import Dict + from colour.io import read_sds_from_csv_file from colour_datasets.loaders import AbstractDatasetLoader diff --git a/colour_datasets/loaders/kuopio.py b/colour_datasets/loaders/kuopio.py index 75113db..c85dc89 100644 --- a/colour_datasets/loaders/kuopio.py +++ b/colour_datasets/loaders/kuopio.py @@ -40,13 +40,18 @@ import os import re import sys -from collections import namedtuple +import typing +from dataclasses import dataclass from typing import ClassVar import numpy as np import scipy.io from colour import SpectralDistribution, SpectralShape -from colour.hints import Any, Dict, Tuple, Type, cast + +if typing.TYPE_CHECKING: + from colour.hints import Any, Dict, Tuple, Type + +from colour.hints import cast from colour_datasets.loaders import AbstractDatasetLoader from colour_datasets.records import datasets @@ -68,12 +73,8 @@ ] -class MatFileMetadata_KuopioUniversity( - namedtuple( - "MatFileMetadata_KuopioUniversity", - ("key", "shape", "transpose", "identifiers"), - ) -): +@dataclass(frozen=True) +class MatFileMetadata_KuopioUniversity: """ Metadata storage for an *University of Kuopio* dataset spectral distributions. @@ -90,6 +91,11 @@ class MatFileMetadata_KuopioUniversity( Identifiers for the spectral distributions. """ + key: str + shape: SpectralShape + transpose: bool + identifiers: str | None + def read_sds_from_mat_file_KuopioUniversity( mat_file: str, metadata: MatFileMetadata_KuopioUniversity @@ -132,7 +138,7 @@ def read_sds_from_mat_file_KuopioUniversity( identifier = f"{identifier} ({i})" sds[identifier] = SpectralDistribution( - dict(zip(wavelengths, data)), name=identifier + dict(zip(wavelengths, data, strict=False)), name=identifier ) return sds diff --git a/colour_datasets/loaders/labsphere2019.py b/colour_datasets/loaders/labsphere2019.py index 2f57d3c..c8ca2d0 100644 --- a/colour_datasets/loaders/labsphere2019.py +++ b/colour_datasets/loaders/labsphere2019.py @@ -14,11 +14,17 @@ doi:10.5281/zenodo.3245875 """ +from __future__ import annotations + import os +import typing import numpy as np from colour import SpectralDistribution -from colour.hints import Dict, Optional + +if typing.TYPE_CHECKING: + from colour.hints import Dict + from colour.utilities import tsplit from colour_datasets.loaders import AbstractDatasetLoader @@ -95,7 +101,7 @@ def load(self) -> Dict[str, SpectralDistribution]: return self._content -_DATASET_LOADER_LABSPHERE2019: Optional[DatasetLoader_Labsphere2019] = None +_DATASET_LOADER_LABSPHERE2019: DatasetLoader_Labsphere2019 | None = None """ Singleton instance of the *Labsphere (2019)* *Labsphere SRS-99-020* dataset loader. diff --git a/colour_datasets/loaders/luo1997.py b/colour_datasets/loaders/luo1997.py index 31d1a17..4a1937c 100644 --- a/colour_datasets/loaders/luo1997.py +++ b/colour_datasets/loaders/luo1997.py @@ -32,10 +32,14 @@ from __future__ import annotations import os -from collections import namedtuple +import typing +from dataclasses import dataclass import numpy as np -from colour.hints import Dict, Tuple + +if typing.TYPE_CHECKING: + from colour.hints import Dict, NDArrayFloat, Tuple + from colour.utilities import as_float_array, usage_warning from colour_datasets.loaders import AbstractDatasetLoader @@ -56,9 +60,8 @@ ] -class ExperimentalGroupLuo1997( - namedtuple("ExperimentalGroupLuo1997", ("name", "phases", "metadata")) -): +@dataclass(frozen=True) +class ExperimentalGroupLuo1997: """ Define a *Luo and Rhodes (1997)* *LUTCHI Colour Appearance Data* experimental group, i.e., a group of experimental phases. @@ -74,22 +77,13 @@ class ExperimentalGroupLuo1997( Experimental group metadata. """ + name: str + phases: Dict + metadata: Dict -class ExperimentalPhaseLuo1997( - namedtuple( - "ExperimentalPhaseLuo1997", - ( - "name", - "JQCH_v", - "xyY_c", - "S_Y_c", - "Y_b", - "Y_r", - "XYZ_o", - "metadata", - ), - ) -): + +@dataclass(frozen=True) +class ExperimentalPhaseLuo1997: """ Define a *Luo and Rhodes (1997)* *LUTCHI Colour Appearance Data* experimental phase. @@ -123,6 +117,15 @@ class ExperimentalPhaseLuo1997( Experimental phase metadata. """ + name: str + JQCH_v: NDArrayFloat + xyY_c: NDArrayFloat + S_Y_c: NDArrayFloat + Y_b: float + Y_r: float + XYZ_o: NDArrayFloat + metadata: Dict + class DatasetLoader_Luo1997(AbstractDatasetLoader): """ @@ -1032,6 +1035,7 @@ def load(self) -> Dict[str, ExperimentalGroupLuo1997]: zip( phase_metadata_headers, [samples_count, (neutrals_start, neutrals_end)], + strict=False, ) ), ) @@ -1043,6 +1047,7 @@ def load(self) -> Dict[str, ExperimentalGroupLuo1997]: zip( group_metadata_headers, experimental_groups_summary[group], + strict=False, ) ), ) diff --git a/colour_datasets/loaders/luo1999.py b/colour_datasets/loaders/luo1999.py index eba8365..01815fa 100644 --- a/colour_datasets/loaders/luo1999.py +++ b/colour_datasets/loaders/luo1999.py @@ -27,10 +27,15 @@ import codecs import os -from collections import namedtuple +import typing +from dataclasses import dataclass import numpy as np -from colour.hints import Dict, Tuple, cast + +if typing.TYPE_CHECKING: + from colour.hints import Dict, NDArrayFloat, Tuple + +from colour.hints import cast from colour.utilities import as_float_array from colour_datasets.loaders import AbstractDatasetLoader @@ -50,23 +55,8 @@ ] -class CorrespondingColourDataset_Luo1999( - namedtuple( - "CorrespondingColourDataset_Luo1999", - ( - "name", - "XYZ_r", - "XYZ_t", - "XYZ_cr", - "XYZ_ct", - "Y_r", - "Y_t", - "B_r", - "B_t", - "metadata", - ), - ) -): +@dataclass(frozen=True) +class CorrespondingColourDataset_Luo1999: """ Define a *Luo and Rhodes (1999)* *Corresponding-Colour Datasets* dataset. @@ -97,6 +87,17 @@ class CorrespondingColourDataset_Luo1999( Dataset metadata. """ + name: str + XYZ_r: NDArrayFloat + XYZ_t: NDArrayFloat + XYZ_cr: NDArrayFloat + XYZ_ct: NDArrayFloat + Y_r: float + Y_t: float + B_r: float + B_t: float + metadata: Dict + class DatasetLoader_Luo1999(AbstractDatasetLoader): """ @@ -449,7 +450,7 @@ def load(self) -> Dict[str, CorrespondingColourDataset_Luo1999]: XYZ_ct.append(list(map(float, values[3:]))) name = f"{key} - {filename.split('.')[1]}" - dataset_metadata = dict(zip(metadata_headers, metadata)) + dataset_metadata = dict(zip(metadata_headers, metadata, strict=False)) Y_r = dataset_metadata["Illuminance (lux)"][i][0] Y_t = dataset_metadata["Illuminance (lux)"][i][1] diff --git a/colour_datasets/loaders/solomotav2023.py b/colour_datasets/loaders/solomotav2023.py index e33a89f..bff8e21 100644 --- a/colour_datasets/loaders/solomotav2023.py +++ b/colour_datasets/loaders/solomotav2023.py @@ -19,9 +19,13 @@ import glob import os +import typing from colour.characterisation import RGB_CameraSensitivities -from colour.hints import Dict + +if typing.TYPE_CHECKING: + from colour.hints import Dict + from colour.io import read_sds_from_csv_file from colour_datasets.loaders import AbstractDatasetLoader diff --git a/colour_datasets/loaders/tests/test_abstract.py b/colour_datasets/loaders/tests/test_abstract.py index af59abb..22c0d6a 100644 --- a/colour_datasets/loaders/tests/test_abstract.py +++ b/colour_datasets/loaders/tests/test_abstract.py @@ -20,7 +20,7 @@ class TestAbstractDatasetLoader: class unit tests methods. """ - def test_required_attributes(self): + def test_required_attributes(self) -> None: """Test the presence of required attributes.""" required_attributes = ("ID", "record", "id", "content") @@ -28,7 +28,7 @@ def test_required_attributes(self): for attribute in required_attributes: assert attribute in dir(AbstractDatasetLoader) - def test_required_methods(self): + def test_required_methods(self) -> None: """Test the presence of required methods.""" required_methods = ("__init__", "load", "sync") diff --git a/colour_datasets/loaders/tests/test_asano2015.py b/colour_datasets/loaders/tests/test_asano2015.py index eb48800..dfa57fd 100644 --- a/colour_datasets/loaders/tests/test_asano2015.py +++ b/colour_datasets/loaders/tests/test_asano2015.py @@ -1,6 +1,5 @@ """Define the unit tests for the :mod:`colour_datasets.loaders.asano2015` module.""" - import numpy as np from colour import SpectralShape from colour.constants import TOLERANCE_ABSOLUTE_TESTS @@ -26,7 +25,7 @@ class TestDatasetLoader_Asano2015: class unit tests methods. """ - def test_required_attributes(self): + def test_required_attributes(self) -> None: """Test the presence of required attributes.""" required_attributes = ("ID",) @@ -34,7 +33,7 @@ def test_required_attributes(self): for attribute in required_attributes: assert attribute in dir(DatasetLoader_Asano2015) - def test_required_methods(self): + def test_required_methods(self) -> None: """Test the presence of required methods.""" required_methods = ("__init__", "load", "parse_workbook_Asano2015") @@ -42,7 +41,7 @@ def test_required_methods(self): for method in required_methods: assert method in dir(DatasetLoader_Asano2015) - def test_load(self): + def test_load(self) -> None: """ Test :func:`colour_datasets.loaders.asano2015.\ DatasetLoader_Asano2015.load` method. @@ -134,7 +133,7 @@ class TestBuildAsano2015: definition unit tests methods. """ - def test_build_Asano2015(self): + def test_build_Asano2015(self) -> None: """ Test :func:`colour_datasets.loaders.asano2015.build_Asano2015` definition. diff --git a/colour_datasets/loaders/tests/test_brendel2020.py b/colour_datasets/loaders/tests/test_brendel2020.py index d8ae053..045803c 100644 --- a/colour_datasets/loaders/tests/test_brendel2020.py +++ b/colour_datasets/loaders/tests/test_brendel2020.py @@ -29,7 +29,7 @@ class TestDatasetLoader_Brendel2020: DatasetLoader_Brendel2020` class unit tests methods. """ - def test_required_attributes(self): + def test_required_attributes(self) -> None: """Test the presence of required attributes.""" required_attributes = ("ID",) @@ -37,7 +37,7 @@ def test_required_attributes(self): for attribute in required_attributes: assert attribute in dir(DatasetLoader_Brendel2020) - def test_required_methods(self): + def test_required_methods(self) -> None: """Test the presence of required methods.""" required_methods = ("__init__", "load") @@ -45,7 +45,7 @@ def test_required_methods(self): for method in required_methods: assert method in dir(DatasetLoader_Brendel2020) - def test_load(self): + def test_load(self) -> None: """ Test :func:`colour_datasets.loaders.brendel2020.\ DatasetLoader_Brendel2020.load` method. @@ -66,7 +66,7 @@ class TestBuildBrendel2020: definition unit tests methods. """ - def test_build_Brendel2020(self): + def test_build_Brendel2020(self) -> None: """ Test :func:`colour_datasets.loaders.brendel2020.build_Brendel2020` definition. diff --git a/colour_datasets/loaders/tests/test_dyer2017.py b/colour_datasets/loaders/tests/test_dyer2017.py index 7708e53..40d462e 100644 --- a/colour_datasets/loaders/tests/test_dyer2017.py +++ b/colour_datasets/loaders/tests/test_dyer2017.py @@ -1,6 +1,5 @@ """Define the unit tests for the :mod:`colour_datasets.loaders.dyer2017` module.""" - import numpy as np from colour.constants import TOLERANCE_ABSOLUTE_TESTS @@ -25,7 +24,7 @@ class TestDatasetLoader_Dyer2017: class unit tests methods. """ - def test_required_attributes(self): + def test_required_attributes(self) -> None: """Test the presence of required attributes.""" required_attributes = ("ID",) @@ -33,7 +32,7 @@ def test_required_attributes(self): for attribute in required_attributes: assert attribute in dir(DatasetLoader_Dyer2017) - def test_required_methods(self): + def test_required_methods(self) -> None: """Test the presence of required methods.""" required_methods = ("__init__", "load") @@ -41,7 +40,7 @@ def test_required_methods(self): for method in required_methods: assert method in dir(DatasetLoader_Dyer2017) - def test_load(self): + def test_load(self) -> None: """ Test :func:`colour_datasets.loaders.dyer2017.DatasetLoader_Dyer2017.\ load` method. @@ -288,7 +287,7 @@ class TestBuildDyer2017: definition unit tests methods. """ - def test_build_Dyer2017(self): + def test_build_Dyer2017(self) -> None: """ Test :func:`colour_datasets.loaders.dyer2017.build_Dyer2017` definition. diff --git a/colour_datasets/loaders/tests/test_ebner1998.py b/colour_datasets/loaders/tests/test_ebner1998.py index 7ce9655..9153f75 100644 --- a/colour_datasets/loaders/tests/test_ebner1998.py +++ b/colour_datasets/loaders/tests/test_ebner1998.py @@ -24,7 +24,7 @@ class TestDatasetLoader_Ebner1998: class unit tests methods. """ - def test_required_attributes(self): + def test_required_attributes(self) -> None: """Test the presence of required attributes.""" required_attributes = ("ID",) @@ -32,7 +32,7 @@ def test_required_attributes(self): for attribute in required_attributes: assert attribute in dir(DatasetLoader_Ebner1998) - def test_required_methods(self): + def test_required_methods(self) -> None: """Test the presence of required methods.""" required_methods = ("__init__", "load") @@ -40,7 +40,7 @@ def test_required_methods(self): for method in required_methods: assert method in dir(DatasetLoader_Ebner1998) - def test_load(self): + def test_load(self) -> None: """ Test :func:`colour_datasets.loaders.ebner1998.\ DatasetLoader_Ebner1998.load` method. @@ -125,7 +125,7 @@ class TestBuildEbner1998: definition unit tests methods. """ - def test_build_Ebner1998(self): + def test_build_Ebner1998(self) -> None: """ Test :func:`colour_datasets.loaders.ebner1998.build_Ebner1998` definition. diff --git a/colour_datasets/loaders/tests/test_hung1995.py b/colour_datasets/loaders/tests/test_hung1995.py index a976cae..c73fbbc 100644 --- a/colour_datasets/loaders/tests/test_hung1995.py +++ b/colour_datasets/loaders/tests/test_hung1995.py @@ -1,6 +1,5 @@ """Define the unit tests for the :mod:`colour_datasets.loaders.hung1995` module.""" - import numpy as np from colour.constants import TOLERANCE_ABSOLUTE_TESTS @@ -25,7 +24,7 @@ class TestDatasetLoader_Hung1995: class unit tests methods. """ - def test_required_attributes(self): + def test_required_attributes(self) -> None: """Test the presence of required attributes.""" required_attributes = ("ID",) @@ -33,7 +32,7 @@ def test_required_attributes(self): for attribute in required_attributes: assert attribute in dir(DatasetLoader_Hung1995) - def test_required_methods(self): + def test_required_methods(self) -> None: """Test the presence of required methods.""" required_methods = ("__init__", "load") @@ -41,7 +40,7 @@ def test_required_methods(self): for method in required_methods: assert method in dir(DatasetLoader_Hung1995) - def test_load(self): + def test_load(self) -> None: """ Test :func:`colour_datasets.loaders.hung1995.DatasetLoader_Hung1995.\ load` method. @@ -113,7 +112,7 @@ class TestBuildHung1995: definition unit tests methods. """ - def test_build_Hung1995(self): + def test_build_Hung1995(self) -> None: """ Test :func:`colour_datasets.loaders.hung1995.build_Hung1995` definition. diff --git a/colour_datasets/loaders/tests/test_jakob2019.py b/colour_datasets/loaders/tests/test_jakob2019.py index 9e124ec..5ce3f33 100644 --- a/colour_datasets/loaders/tests/test_jakob2019.py +++ b/colour_datasets/loaders/tests/test_jakob2019.py @@ -1,6 +1,5 @@ """Define the unit tests for the :mod:`colour_datasets.loaders.jakob2019` module.""" - from colour_datasets.loaders import DatasetLoader_Jakob2019, build_Jakob2019 __author__ = "Colour Developers" @@ -22,7 +21,7 @@ class TestDatasetLoader_Jakob2019: class unit tests methods. """ - def test_required_attributes(self): + def test_required_attributes(self) -> None: """Test the presence of required attributes.""" required_attributes = ("ID",) @@ -30,7 +29,7 @@ def test_required_attributes(self): for attribute in required_attributes: assert attribute in dir(DatasetLoader_Jakob2019) - def test_required_methods(self): + def test_required_methods(self) -> None: """Test the presence of required methods.""" required_methods = ("__init__", "load") @@ -38,7 +37,7 @@ def test_required_methods(self): for method in required_methods: assert method in dir(DatasetLoader_Jakob2019) - def test_load(self): + def test_load(self) -> None: """ Test :func:`colour_datasets.loaders.jakob2019.\ DatasetLoader_Jakob2019.load` method. @@ -59,7 +58,7 @@ class TestBuildJakob2019: definition unit tests methods. """ - def test_build_Jakob2019(self): + def test_build_Jakob2019(self) -> None: """ Test :func:`colour_datasets.loaders.jakob2019.build_Jakob2019` definition. diff --git a/colour_datasets/loaders/tests/test_jiang2013.py b/colour_datasets/loaders/tests/test_jiang2013.py index e7cc64e..4143b46 100644 --- a/colour_datasets/loaders/tests/test_jiang2013.py +++ b/colour_datasets/loaders/tests/test_jiang2013.py @@ -1,6 +1,5 @@ """Define the unit tests for the :mod:`colour_datasets.loaders.jiang2013` module.""" - from colour import SpectralShape from colour_datasets.loaders import DatasetLoader_Jiang2013, build_Jiang2013 @@ -24,7 +23,7 @@ class TestDatasetLoader_Jiang2013: class unit tests methods. """ - def test_required_attributes(self): + def test_required_attributes(self) -> None: """Test the presence of required attributes.""" required_attributes = ("ID",) @@ -32,7 +31,7 @@ def test_required_attributes(self): for attribute in required_attributes: assert attribute in dir(DatasetLoader_Jiang2013) - def test_required_methods(self): + def test_required_methods(self) -> None: """Test the presence of required methods.""" required_methods = ("__init__", "load") @@ -40,7 +39,7 @@ def test_required_methods(self): for method in required_methods: assert method in dir(DatasetLoader_Jiang2013) - def test_load(self): + def test_load(self) -> None: """ Test :func:`colour_datasets.loaders.jiang2013.\ DatasetLoader_Jiang2013.load` method. @@ -86,7 +85,7 @@ class TestBuildJiang2013: definition unit tests methods. """ - def test_build_Jiang2013(self): + def test_build_Jiang2013(self) -> None: """ Test :func:`colour_datasets.loaders.jiang2013.build_Jiang2013` definition. diff --git a/colour_datasets/loaders/tests/test_karge2015.py b/colour_datasets/loaders/tests/test_karge2015.py index cb43de6..079df02 100644 --- a/colour_datasets/loaders/tests/test_karge2015.py +++ b/colour_datasets/loaders/tests/test_karge2015.py @@ -1,6 +1,5 @@ """Define the unit tests for the :mod:`colour_datasets.loaders.karge2015` module.""" - from colour import SpectralShape from colour_datasets.loaders import DatasetLoader_Karge2015, build_Karge2015 @@ -24,7 +23,7 @@ class TestDatasetLoader_Karge2015: class unit tests methods. """ - def test_required_attributes(self): + def test_required_attributes(self) -> None: """Test the presence of required attributes.""" required_attributes = ("ID",) @@ -32,7 +31,7 @@ def test_required_attributes(self): for attribute in required_attributes: assert attribute in dir(DatasetLoader_Karge2015) - def test_required_methods(self): + def test_required_methods(self) -> None: """Test the presence of required methods.""" required_methods = ("__init__", "load") @@ -40,7 +39,7 @@ def test_required_methods(self): for method in required_methods: assert method in dir(DatasetLoader_Karge2015) - def test_load(self): + def test_load(self) -> None: """ Test :func:`colour_datasets.loaders.karge2015.\ DatasetLoader_Karge2015.load` method. @@ -67,7 +66,7 @@ class TestBuildKarge2015: definition unit tests methods. """ - def test_build_Karge2015(self): + def test_build_Karge2015(self) -> None: """ Test :func:`colour_datasets.loaders.karge2015.build_Karge2015` definition. diff --git a/colour_datasets/loaders/tests/test_kuopio.py b/colour_datasets/loaders/tests/test_kuopio.py index 57bc5bf..3f88b78 100644 --- a/colour_datasets/loaders/tests/test_kuopio.py +++ b/colour_datasets/loaders/tests/test_kuopio.py @@ -42,7 +42,7 @@ class TestReadSdsFromMatFileKuopioUniversity: read_sds_from_mat_file_KuopioUniversity` definition unit tests methods. """ - def test_read_sds_from_mat_file_KuopioUniversity(self): + def test_read_sds_from_mat_file_KuopioUniversity(self) -> None: """ Test :func:`colour_datasets.loaders.kuopio.\ read_sds_from_mat_file_KuopioUniversity` definition. @@ -105,7 +105,7 @@ class TestDatasetLoader_KuopioUniversity: DatasetLoader_KuopioUniversity` class unit tests methods. """ - def test_required_attributes(self): + def test_required_attributes(self) -> None: """Test the presence of required attributes.""" required_attributes = ("ID", "METADATA") @@ -123,7 +123,7 @@ def test_required_attributes(self): for attribute in required_attributes: assert attribute in dir(dataset_loader) - def test_required_methods(self): + def test_required_methods(self) -> None: """Test the presence of required methods.""" required_methods = ("__init__", "load") @@ -141,7 +141,7 @@ def test_required_methods(self): for method in required_methods: assert method in dir(dataset_loader) - def test_load(self): + def test_load(self) -> None: """ Test :func:`colour_datasets.loaders.kuopio.\ DatasetLoader_KuopioUniversity.load` method. diff --git a/colour_datasets/loaders/tests/test_labsphere2019.py b/colour_datasets/loaders/tests/test_labsphere2019.py index 18b7d32..ef2c95d 100644 --- a/colour_datasets/loaders/tests/test_labsphere2019.py +++ b/colour_datasets/loaders/tests/test_labsphere2019.py @@ -3,7 +3,6 @@ module. """ - from colour import SpectralShape from colour_datasets.loaders import ( @@ -30,7 +29,7 @@ class TestDatasetLoader_Labsphere2019: DatasetLoader_Labsphere2019` class unit tests methods. """ - def test_required_attributes(self): + def test_required_attributes(self) -> None: """Test the presence of required attributes.""" required_attributes = ("ID",) @@ -38,7 +37,7 @@ def test_required_attributes(self): for attribute in required_attributes: assert attribute in dir(DatasetLoader_Labsphere2019) - def test_required_methods(self): + def test_required_methods(self) -> None: """Test the presence of required methods.""" required_methods = ("__init__", "load") @@ -46,7 +45,7 @@ def test_required_methods(self): for method in required_methods: assert method in dir(DatasetLoader_Labsphere2019) - def test_load(self): + def test_load(self) -> None: """ Test :func:`colour_datasets.loaders.labsphere2019.\ DatasetLoader_Labsphere2019.load` method. @@ -65,7 +64,7 @@ class TestBuildLabsphere2019: definition unit tests methods. """ - def test_build_Labsphere2019(self): + def test_build_Labsphere2019(self) -> None: """ Test :func:`colour_datasets.loaders.labsphere2019.build_Labsphere2019` definition. diff --git a/colour_datasets/loaders/tests/test_luo1997.py b/colour_datasets/loaders/tests/test_luo1997.py index f352fac..251250b 100644 --- a/colour_datasets/loaders/tests/test_luo1997.py +++ b/colour_datasets/loaders/tests/test_luo1997.py @@ -1,6 +1,5 @@ """Define the unit tests for the :mod:`colour_datasets.loaders.luo1997` module.""" - import numpy as np from colour.constants import TOLERANCE_ABSOLUTE_TESTS @@ -25,7 +24,7 @@ class TestDatasetLoader_Luo1997: class unit tests methods. """ - def test_required_attributes(self): + def test_required_attributes(self) -> None: """Test the presence of required attributes.""" required_attributes = ("ID",) @@ -33,7 +32,7 @@ def test_required_attributes(self): for attribute in required_attributes: assert attribute in dir(DatasetLoader_Luo1997) - def test_required_methods(self): + def test_required_methods(self) -> None: """Test the presence of required methods.""" required_methods = ("__init__", "load") @@ -41,7 +40,7 @@ def test_required_methods(self): for method in required_methods: assert method in dir(DatasetLoader_Luo1997) - def test_load(self): + def test_load(self) -> None: """ Test :func:`colour_datasets.loaders.luo1997.DatasetLoader_Luo1997.\ load` method. @@ -176,7 +175,7 @@ class TestBuildLuo1997: definition unit tests methods. """ - def test_build_Luo1997(self): + def test_build_Luo1997(self) -> None: """ Test :func:`colour_datasets.loaders.luo1997.build_Luo1997` definition. diff --git a/colour_datasets/loaders/tests/test_luo1999.py b/colour_datasets/loaders/tests/test_luo1999.py index 93b674c..d71734e 100644 --- a/colour_datasets/loaders/tests/test_luo1999.py +++ b/colour_datasets/loaders/tests/test_luo1999.py @@ -1,6 +1,5 @@ """Define the unit tests for the :mod:`colour_datasets.loaders.luo1999` module.""" - import numpy as np from colour.constants import TOLERANCE_ABSOLUTE_TESTS @@ -25,7 +24,7 @@ class TestDatasetLoader_Luo1999: class unit tests methods. """ - def test_required_attributes(self): + def test_required_attributes(self) -> None: """Test the presence of required attributes.""" required_attributes = ("ID",) @@ -33,7 +32,7 @@ def test_required_attributes(self): for attribute in required_attributes: assert attribute in dir(DatasetLoader_Luo1999) - def test_required_methods(self): + def test_required_methods(self) -> None: """Test the presence of required methods.""" required_methods = ("__init__", "load") @@ -41,7 +40,7 @@ def test_required_methods(self): for method in required_methods: assert method in dir(DatasetLoader_Luo1999) - def test_load(self): + def test_load(self) -> None: """ Test :func:`colour_datasets.loaders.luo1999.DatasetLoader_Luo1999.\ load` method. @@ -159,7 +158,7 @@ class TestBuildLuo1999: definition unit tests methods. """ - def test_build_Luo1999(self): + def test_build_Luo1999(self) -> None: """ Test :func:`colour_datasets.loaders.luo1999.build_Luo1999` definition. diff --git a/colour_datasets/loaders/tests/test_solomotav2023.py b/colour_datasets/loaders/tests/test_solomotav2023.py index af7d72b..9080f1e 100644 --- a/colour_datasets/loaders/tests/test_solomotav2023.py +++ b/colour_datasets/loaders/tests/test_solomotav2023.py @@ -1,6 +1,5 @@ """Define the unit tests for the :mod:`colour_datasets.loaders.solomotav2023` module.""" - from colour import SpectralShape from colour_datasets.loaders import ( @@ -27,7 +26,7 @@ class TestDatasetLoader_Solomotav2023: class unit tests methods. """ - def test_required_attributes(self): + def test_required_attributes(self) -> None: """Test the presence of required attributes.""" required_attributes = ("ID",) @@ -35,7 +34,7 @@ def test_required_attributes(self): for attribute in required_attributes: assert attribute in dir(DatasetLoader_Solomotav2023) - def test_required_methods(self): + def test_required_methods(self) -> None: """Test the presence of required methods.""" required_methods = ("__init__", "load") @@ -43,7 +42,7 @@ def test_required_methods(self): for method in required_methods: assert method in dir(DatasetLoader_Solomotav2023) - def test_load(self): + def test_load(self) -> None: """ Test :func:`colour_datasets.loaders.solomotav2023.\ DatasetLoader_Solomotav2023.load` method. @@ -63,7 +62,7 @@ class TestBuildSolomotav2023: definition unit tests methods. """ - def test_build_Solomotav2023(self): + def test_build_Solomotav2023(self) -> None: """ Test :func:`colour_datasets.loaders.solomotav2023.build_Solomotav2023` definition. diff --git a/colour_datasets/loaders/tests/test_winquist2022.py b/colour_datasets/loaders/tests/test_winquist2022.py index 155e752..1181981 100644 --- a/colour_datasets/loaders/tests/test_winquist2022.py +++ b/colour_datasets/loaders/tests/test_winquist2022.py @@ -3,7 +3,6 @@ module. """ - import numpy as np from colour.constants import TOLERANCE_ABSOLUTE_TESTS @@ -32,7 +31,7 @@ class TestDatasetLoader_Winquist2022: class unit tests methods. """ - def test_required_attributes(self): + def test_required_attributes(self) -> None: """Test the presence of required attributes.""" required_attributes = ("ID",) @@ -40,7 +39,7 @@ def test_required_attributes(self): for attribute in required_attributes: assert attribute in dir(DatasetLoader_Winquist2022) - def test_required_methods(self): + def test_required_methods(self) -> None: """Test the presence of required methods.""" required_methods = ("__init__", "load") @@ -48,7 +47,7 @@ def test_required_methods(self): for method in required_methods: assert method in dir(DatasetLoader_Winquist2022) - def test_load(self): + def test_load(self) -> None: """ Test :func:`colour_datasets.loaders.winquist2022.DatasetLoader_Winquist2022.\ @@ -71,7 +70,7 @@ class TestBuildWinquist2022: definition unit tests methods. """ - def test_build_Winquist2022(self): + def test_build_Winquist2022(self) -> None: """ Test :func:`colour_datasets.loaders.winquist2022.build_Winquist2022` definition. diff --git a/colour_datasets/loaders/tests/test_xrite2016.py b/colour_datasets/loaders/tests/test_xrite2016.py index 4043f5e..7e68b8b 100644 --- a/colour_datasets/loaders/tests/test_xrite2016.py +++ b/colour_datasets/loaders/tests/test_xrite2016.py @@ -1,6 +1,5 @@ """Define the unit tests for the :mod:`colour_datasets.loaders.xrite2016` module.""" - from colour.characterisation import ColourChecker from colour_datasets.loaders import DatasetLoader_XRite2016, build_XRite2016 @@ -24,7 +23,7 @@ class TestDatasetLoader_XRite2016: class unit tests methods. """ - def test_required_attributes(self): + def test_required_attributes(self) -> None: """Test the presence of required attributes.""" required_attributes = ("ID",) @@ -32,7 +31,7 @@ def test_required_attributes(self): for attribute in required_attributes: assert attribute in dir(DatasetLoader_XRite2016) - def test_required_methods(self): + def test_required_methods(self) -> None: """Test the presence of required methods.""" required_methods = ("__init__", "load") @@ -40,7 +39,7 @@ def test_required_methods(self): for method in required_methods: assert method in dir(DatasetLoader_XRite2016) - def test_load(self): + def test_load(self) -> None: """ Test :func:`colour_datasets.loaders.xrite2016.\ DatasetLoader_XRite2016.load` method. @@ -67,7 +66,7 @@ class TestBuildXRite2016: definition unit tests methods. """ - def test_build_XRite2016(self): + def test_build_XRite2016(self) -> None: """ Test :func:`colour_datasets.loaders.xrite2016.build_XRite2016` definition. diff --git a/colour_datasets/loaders/tests/test_zhao2009.py b/colour_datasets/loaders/tests/test_zhao2009.py index 43fcb14..5b5ee11 100644 --- a/colour_datasets/loaders/tests/test_zhao2009.py +++ b/colour_datasets/loaders/tests/test_zhao2009.py @@ -1,6 +1,5 @@ """Define the unit tests for the :mod:`colour_datasets.loaders.zhao2009` module.""" - from colour import SpectralShape from colour_datasets.loaders import DatasetLoader_Zhao2009, build_Zhao2009 @@ -24,7 +23,7 @@ class TestDatasetLoader_Zhao2009: class unit tests methods. """ - def test_required_attributes(self): + def test_required_attributes(self) -> None: """Test the presence of required attributes.""" required_attributes = ("ID",) @@ -32,7 +31,7 @@ def test_required_attributes(self): for attribute in required_attributes: assert attribute in dir(DatasetLoader_Zhao2009) - def test_required_methods(self): + def test_required_methods(self) -> None: """Test the presence of required methods.""" required_methods = ("__init__", "load") @@ -40,7 +39,7 @@ def test_required_methods(self): for method in required_methods: assert method in dir(DatasetLoader_Zhao2009) - def test_load(self): + def test_load(self) -> None: """ Test :func:`colour_datasets.loaders.zhao2009.\ DatasetLoader_Zhao2009.load` method. @@ -72,7 +71,7 @@ class TestBuildZhao2009: definition unit tests methods. """ - def test_build_Zhao2009(self): + def test_build_Zhao2009(self) -> None: """ Test :func:`colour_datasets.loaders.zhao2009.build_Zhao2009` definition. diff --git a/colour_datasets/loaders/winquist2022.py b/colour_datasets/loaders/winquist2022.py index c92f202..72cea53 100644 --- a/colour_datasets/loaders/winquist2022.py +++ b/colour_datasets/loaders/winquist2022.py @@ -20,8 +20,10 @@ import glob import os +import typing -from colour.hints import Dict +if typing.TYPE_CHECKING: + from colour.hints import Dict from colour_datasets.loaders import AbstractDatasetLoader from colour_datasets.loaders.dyer2017 import MultiSpectralDistributions_AMPAS diff --git a/colour_datasets/loaders/xrite2016.py b/colour_datasets/loaders/xrite2016.py index fa63f6e..2b3429d 100644 --- a/colour_datasets/loaders/xrite2016.py +++ b/colour_datasets/loaders/xrite2016.py @@ -20,11 +20,14 @@ import codecs import os +import typing import numpy as np from colour import CCS_ILLUMINANTS, Lab_to_XYZ, XYZ_to_xyY from colour.characterisation import ColourChecker -from colour.hints import Dict + +if typing.TYPE_CHECKING: + from colour.hints import Dict from colour_datasets.loaders import AbstractDatasetLoader from colour_datasets.records import datasets @@ -110,7 +113,7 @@ def load(self) -> Dict[str, ColourChecker]: illuminant = CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"]["ICC D50"] self._content = {} - for key, filename in zip(keys, filenames): + for key, filename in zip(keys, filenames, strict=False): directory = os.path.splitext(filename)[0] path = os.path.join(self.record.repository, "dataset", directory, filename) @@ -142,10 +145,10 @@ def load(self) -> Dict[str, ColourChecker]: np.reshape(np.array(samples_data, dtype=object), (i, j, 2)), [1, 0, 2], ) - keys, values = zip(*np.reshape(samples, (-1, 2))) + keys, values = zip(*np.reshape(samples, (-1, 2)), strict=False) values = XYZ_to_xyY(Lab_to_XYZ(values, illuminant)) self._content[key] = ColourChecker( - key, dict(zip(keys, values)), illuminant, j, i + key, dict(zip(keys, values, strict=False)), illuminant, j, i ) return self._content diff --git a/colour_datasets/loaders/zhao2009.py b/colour_datasets/loaders/zhao2009.py index faf1d01..ae7622a 100644 --- a/colour_datasets/loaders/zhao2009.py +++ b/colour_datasets/loaders/zhao2009.py @@ -18,10 +18,13 @@ from __future__ import annotations import os +import typing import numpy as np from colour.characterisation import RGB_CameraSensitivities -from colour.hints import Dict + +if typing.TYPE_CHECKING: + from colour.hints import Dict from colour_datasets.loaders import AbstractDatasetLoader from colour_datasets.records import datasets diff --git a/colour_datasets/records/__init__.py b/colour_datasets/records/__init__.py index 5d2a283..d29f471 100644 --- a/colour_datasets/records/__init__.py +++ b/colour_datasets/records/__init__.py @@ -1,5 +1,6 @@ -from __future__ import annotations +# isort: skip_file +from __future__ import annotations from .configuration import Configuration, sandbox, use_sandbox from .zenodo import Community, Record diff --git a/colour_datasets/records/configuration.py b/colour_datasets/records/configuration.py index 30a6032..b068366 100644 --- a/colour_datasets/records/configuration.py +++ b/colour_datasets/records/configuration.py @@ -9,13 +9,17 @@ import functools import os +import typing -from colour.hints import Any, Callable, Dict from colour.utilities import Structure from colour.utilities.documentation import ( DocstringDict, is_documentation_building, ) +from typing_extensions import Self + +if typing.TYPE_CHECKING: + from colour.hints import Any, Callable, Dict __author__ = "Colour Developers" __copyright__ = "Copyright 2019 Colour Developers" @@ -75,7 +79,7 @@ def use_sandbox( state: bool = True, api_url: str = "https://sandbox.zenodo.org/api", community: str = "colour-science-datasets", -): +) -> None: """ Modify the *Colour - Datasets* configuration to use *Zenodo* sandbox. @@ -120,7 +124,7 @@ def __init__( self._api_url = api_url self._community = community - def __enter__(self) -> sandbox: + def __enter__(self) -> Self: """ Set the configuration to the *Zenodo* sandbox upon entering the context manager. @@ -130,7 +134,7 @@ def __enter__(self) -> sandbox: return self - def __exit__(self, *args: Any): + def __exit__(self, *args: Any) -> None: """Restore the configuration upon exiting the context manager.""" use_sandbox(False) diff --git a/colour_datasets/records/tests/test_configuration.py b/colour_datasets/records/tests/test_configuration.py index 53d160c..a950710 100644 --- a/colour_datasets/records/tests/test_configuration.py +++ b/colour_datasets/records/tests/test_configuration.py @@ -24,12 +24,12 @@ class TestUseSandbox: definition unit tests methods. """ - def teardown_method(self): + def teardown_method(self) -> None: """After tests actions.""" use_sandbox(False) - def test_use_sandbox(self): + def test_use_sandbox(self) -> None: """ Test :func:`colour_datasets.records.configuration.use_sandbox` definition. @@ -47,7 +47,7 @@ class TestSandbox: definition unit tests methods. """ - def test_sandbox(self): + def test_sandbox(self) -> None: """ Test :func:`colour_datasets.records.configuration.sandbox` definition. diff --git a/colour_datasets/records/tests/test_zenodo.py b/colour_datasets/records/tests/test_zenodo.py index 16fbd1a..b7e74e3 100644 --- a/colour_datasets/records/tests/test_zenodo.py +++ b/colour_datasets/records/tests/test_zenodo.py @@ -29,7 +29,7 @@ class TestRecord(unittest.TestCase): methods. """ - def setUp(self): + def setUp(self) -> None: """Initialise the common tests attributes.""" self._data = json_open("https://zenodo.org/api/records/3245883") @@ -37,7 +37,7 @@ def setUp(self): self._record = Record(self._data, self._configuration) - def test_required_attributes(self): + def test_required_attributes(self) -> None: """Test the presence of required attributes.""" required_attributes = ( @@ -51,7 +51,7 @@ def test_required_attributes(self): for attribute in required_attributes: self.assertIn(attribute, dir(Record)) - def test_required_methods(self): + def test_required_methods(self) -> None: """Test the presence of required methods.""" required_methods = ( @@ -67,7 +67,7 @@ def test_required_methods(self): for method in required_methods: self.assertIn(method, dir(Record)) - def test_configuration(self): + def test_configuration(self) -> None: """ Test :func:colour_datasets.records.zenodo.Record.configuration` property. @@ -75,12 +75,12 @@ def test_configuration(self): self.assertEqual(self._record.configuration, self._configuration) - def test_data(self): + def test_data(self) -> None: """Test :func:colour_datasets.records.zenodo.Record.data` property.""" self.assertEqual(self._record.data, self._data) - def test_repository(self): + def test_repository(self) -> None: """ Test :func:colour_datasets.records.zenodo.Record.repository` property. @@ -91,12 +91,12 @@ def test_repository(self): os.path.join(self._configuration.repository, "3245883"), ) - def test_id(self): + def test_id(self) -> None: """Test :func:colour_datasets.records.zenodo.Record.id` property.""" self.assertEqual(self._record.id, "3245883") - def test_title(self): + def test_title(self) -> None: """ Test :func:colour_datasets.records.zenodo.Record.title` property. @@ -107,7 +107,7 @@ def test_title(self): "Camera Spectral Sensitivity Database - Jiang et al. (2013)", ) - def test__init__(self): + def test__init__(self) -> None: """ Test :func:`colour_datasets.records.zenodo.Record.__init__` method. """ @@ -119,7 +119,7 @@ def test__init__(self): "Camera Spectral Sensitivity Database - Jiang et al. (2013)", ) - def test__str__(self): + def test__str__(self) -> None: """ Test :func:`colour_datasets.records.zenodo.Record.__str__` method. """ @@ -166,13 +166,13 @@ def test__str__(self): )[1:], ) - def test__repr__(self): + def test__repr__(self) -> None: """ Test :func:`colour_datasets.records.zenodo.Record.__repr__` method. """ self.assertIsInstance( - eval( # noqa: PGH001, S307 + eval( # noqa: S307 repr(self._record), {}, {"Record": Record, "Configuration": Configuration}, @@ -180,7 +180,7 @@ def test__repr__(self): Record, ) - def test_from_id(self): + def test_from_id(self) -> None: """Test :func:`colour_datasets.records.zenodo.Record.from_id` method.""" record = Record.from_id("3245883") @@ -191,7 +191,7 @@ def test_from_id(self): "Camera Spectral Sensitivity Database - Jiang et al. (2013)", ) - def test_synced(self): + def test_synced(self) -> None: """Test :func:`colour_datasets.records.zenodo.Record.synced` method.""" self._record.pull() @@ -199,14 +199,14 @@ def test_synced(self): self._record.remove() self.assertFalse(self._record.synced()) - def test_pull(self): + def test_pull(self) -> None: """Test :func:`colour_datasets.records.zenodo.Record.pull` method.""" self._record.remove() self._record.pull() self.assertTrue(self._record.synced()) - def test_remove(self): + def test_remove(self) -> None: """Test :func:`colour_datasets.records.zenodo.Record.remove` method.""" self._record.pull() @@ -220,7 +220,7 @@ class TestCommunity(unittest.TestCase): methods. """ - def setUp(self): + def setUp(self) -> None: """Initialise the common tests attributes.""" community_data = json_open( @@ -236,7 +236,7 @@ def setUp(self): self._community = Community(self._data, self._configuration) - def test_required_attributes(self): + def test_required_attributes(self) -> None: """Test the presence of required attributes.""" required_attributes = ( @@ -249,7 +249,7 @@ def test_required_attributes(self): for attribute in required_attributes: self.assertIn(attribute, dir(Community)) - def test_required_methods(self): + def test_required_methods(self) -> None: """Test the presence of required methods.""" required_methods = ( @@ -268,7 +268,7 @@ def test_required_methods(self): for method in required_methods: self.assertIn(method, dir(Community)) - def test_configuration(self): + def test_configuration(self) -> None: """ Test :func:colour_datasets.records.zenodo.Community.configuration` property. @@ -276,14 +276,14 @@ def test_configuration(self): self.assertEqual(self._community.configuration, self._configuration) - def test_data(self): + def test_data(self) -> None: """ Test :func:colour_datasets.records.zenodo.Community.data` property. """ self.assertEqual(self._community.data, self._data) - def test_repository(self): + def test_repository(self) -> None: """ Test :func:colour_datasets.records.zenodo.Community.repository` property. @@ -291,14 +291,14 @@ def test_repository(self): self.assertEqual(self._community.repository, self._configuration.repository) - def test_records(self): + def test_records(self) -> None: """ Test :func:colour_datasets.records.zenodo.Community.records` property. """ self.assertIn("3245883", list(self._community.records)) - def test__init__(self): + def test__init__(self) -> None: """ Test :func:`colour_datasets.records.zenodo.Community.__init__` method. """ @@ -310,7 +310,7 @@ def test__init__(self): "Camera Spectral Sensitivity Database - Jiang et al. (2013)", ) - def test__str__(self): + def test__str__(self) -> None: """ Test :func:`colour_datasets.records.zenodo.Community.__str__` method. """ @@ -339,13 +339,13 @@ def test__str__(self): )[1:], ) - def test__repr__(self): + def test__repr__(self) -> None: """ Test :func:`colour_datasets.records.zenodo.Community.__repr__` method. """ self.assertIsInstance( - eval( # noqa: PGH001, S307 + eval( # noqa: S307 repr(self._community), {}, {"Community": Community, "Configuration": Configuration}, @@ -353,7 +353,7 @@ def test__repr__(self): Community, ) - def test__getitem__(self): + def test__getitem__(self) -> None: """ Test :func:`colour_datasets.records.zenodo.Community.__getitem__` method. @@ -361,7 +361,7 @@ def test__getitem__(self): self.assertIs(self._community["3245883"], self._community.records["3245883"]) - def test__iter__(self): + def test__iter__(self) -> None: """ Test :func:`colour_datasets.records.zenodo.Community.__iter__` method. @@ -369,7 +369,7 @@ def test__iter__(self): self.assertListEqual(list(self._community), list(self._community.records)) - def test__len__(self): + def test__len__(self) -> None: """ Test :func:`colour_datasets.records.zenodo.Community.__getitem__` method. @@ -377,7 +377,7 @@ def test__len__(self): self.assertEqual(len(self._community), len(self._community.records)) - def test_from_id(self): + def test_from_id(self) -> None: """ Test :func:`colour_datasets.records.zenodo.Community.from_id` method. """ @@ -390,7 +390,7 @@ def test_from_id(self): "Camera Spectral Sensitivity Database - Jiang et al. (2013)", ) - def test_synced(self): + def test_synced(self) -> None: """ Test :func:`colour_datasets.records.zenodo.Community.synced` method. """ @@ -400,7 +400,7 @@ def test_synced(self): self._community.remove() self.assertFalse(self._community.synced()) - def test_pull(self): + def test_pull(self) -> None: """ Test :func:`colour_datasets.records.zenodo.Community.pull` method. """ @@ -409,7 +409,7 @@ def test_pull(self): self._community.pull() self.assertTrue(self._community.synced()) - def test_remove(self): + def test_remove(self) -> None: """ Test :func:`colour_datasets.records.zenodo.Community.remove` method. """ diff --git a/colour_datasets/records/zenodo.py b/colour_datasets/records/zenodo.py index bcf01ce..3c1621a 100644 --- a/colour_datasets/records/zenodo.py +++ b/colour_datasets/records/zenodo.py @@ -18,6 +18,7 @@ import stat import tempfile import textwrap +import typing import urllib import urllib.error from collections.abc import Mapping @@ -25,18 +26,20 @@ from pprint import pformat import setuptools.archive_util -from colour.hints import ( - Any, - Callable, - Dict, - Generator, - List, -) from colour.utilities import optional, warning from colour_datasets.records import Configuration from colour_datasets.utilities import json_open, url_download +if typing.TYPE_CHECKING: + from colour.hints import ( + Any, + Callable, + Dict, + Generator, + List, + ) + __author__ = "Colour Developers" __copyright__ = "Copyright 2019 Colour Developers" __license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause" @@ -202,7 +205,7 @@ def strip_html(text: str) -> str: ] ) - representation = "\n".join( + return "\n".join( [ f'{metadata["title"]} - {metadata["version"]}', f'{"=" * (len(self.title) + 3 + len(metadata["version"]))}', @@ -225,8 +228,6 @@ def strip_html(text: str) -> str: ] ) - return representation - def __repr__(self) -> str: """ Return an evaluable string representation of the *Zenodo* record. @@ -331,7 +332,7 @@ def synced(self) -> bool: ] ) - def pull(self, use_urls_txt_file: bool = True, retries: int = 3): + def pull(self, use_urls_txt_file: bool = True, retries: int = 3) -> None: """ Pull the *Zenodo* record data to the local repository. @@ -392,7 +393,7 @@ def urls_download(urls: Dict) -> None: try: if use_urls_txt_file and urls_txt: urls = {} - urls_txt_file = tempfile.NamedTemporaryFile(delete=False).name + urls_txt_file = tempfile.NamedTemporaryFile(delete=False).name # noqa: SIM115 url_download( urls_txt["links"]["self"], urls_txt_file, @@ -414,10 +415,13 @@ def urls_download(urls: Dict) -> None: urls_download(urls) else: - raise ValueError( # noqa: TRY301 + msg = ( f'"{self._configuration.urls_txt_file}" file was not ' f"found in record data!" ) + raise ValueError( # noqa: TRY301 + msg + ) except (urllib.error.URLError, ValueError) as error: warning( f"An error occurred using urls from " @@ -472,7 +476,7 @@ def urls_download(urls: Dict) -> None: with open(os.path.join(self.repository, "record.json"), "w") as record_json: json.dump(self.data, record_json, indent=4, sort_keys=True) - def remove(self): + def remove(self) -> None: """ Remove the *Zenodo* record data local repository. @@ -634,7 +638,7 @@ def __str__(self) -> str: synced = len([dataset for dataset in self.values() if dataset.synced()]) - representation = "\n".join( + return "\n".join( [ f"{self._configuration.community}", f'{"=" * len(self._configuration.community)}', @@ -650,8 +654,6 @@ def __str__(self) -> str: ] ) - return representation - def __repr__(self) -> str: """ Return an evaluable string representation of the *Zenodo* community. @@ -809,7 +811,8 @@ def from_id( os.path.exists(records_json_filename), ] ): - raise RuntimeError("Local files were not found, aborting!") from error + msg = "Local files were not found, aborting!" + raise RuntimeError(msg) from error with open(community_json_filename) as json_file: community_data = json.loads(json_file.read()) @@ -850,7 +853,7 @@ def synced(self) -> bool: return all(record.synced() for record in self._records.values()) - def pull(self, use_urls_txt_file: bool = True, retries: int = 3): + def pull(self, use_urls_txt_file: bool = True, retries: int = 3) -> None: """ Pull the *Zenodo* community data to the local repository. @@ -882,7 +885,7 @@ def pull(self, use_urls_txt_file: bool = True, retries: int = 3): for record in self._records.values(): record.pull(use_urls_txt_file, retries) - def remove(self): + def remove(self) -> None: """ Remove the *Zenodo* community data local repository. @@ -905,7 +908,7 @@ def _remove_readonly( function: Callable, path: str, excinfo: Any, # noqa: ARG001 -): +) -> None: """ Error handler for :func:`shutil.rmtree` definition that removes read-only files. diff --git a/colour_datasets/utilities/__init__.py b/colour_datasets/utilities/__init__.py index 735cfb3..59f38e6 100644 --- a/colour_datasets/utilities/__init__.py +++ b/colour_datasets/utilities/__init__.py @@ -1,3 +1,5 @@ +# isort: skip_file + from .common import ( suppress_stdout, hash_md5, diff --git a/colour_datasets/utilities/common.py b/colour_datasets/utilities/common.py index dc7b0ae..a87d40a 100644 --- a/colour_datasets/utilities/common.py +++ b/colour_datasets/utilities/common.py @@ -15,13 +15,17 @@ import shutil import socket import sys +import typing import urllib.error import urllib.request import setuptools.archive_util from cachetools import TTLCache, cached -from colour.hints import Any, Callable, Dict from tqdm import tqdm +from typing_extensions import Self + +if typing.TYPE_CHECKING: + from colour.hints import Any, Callable, Dict __author__ = "Colour Developers" __copyright__ = "Copyright 2019 Colour Developers" @@ -44,15 +48,15 @@ class suppress_stdout: """A context manager and decorator temporarily suppressing standard output.""" - def __enter__(self) -> suppress_stdout: + def __enter__(self) -> Self: """Redirect the standard output upon entering the context manager.""" self._stdout = sys.stdout - sys.stdout = open(os.devnull, "w") # noqa: SIM115 + sys.stdout = open(os.devnull, "w") return self - def __exit__(self, *args: Any): + def __exit__(self, *args: Any) -> None: """Restore the standard output upon exiting the context manager.""" sys.stdout.close() @@ -77,7 +81,7 @@ def update_to( chunks_count: int = 1, chunk_size: int = 1, total_size: int | None = None, - ): + ) -> None: """ Report the progress of an action. @@ -127,7 +131,9 @@ def hash_md5(filename: str, chunk_size: int = 2**16) -> str: return md5.hexdigest() -def url_download(url: str, filename: str, md5: str | None = None, retries: int = 3): +def url_download( + url: str, filename: str, md5: str | None = None, retries: int = 3 +) -> None: """ Download given url and saves its content at given file. @@ -172,13 +178,16 @@ def url_download(url: str, filename: str, md5: str | None = None, retries: int = socket.setdefaulttimeout(timeout) if md5 is not None and md5.lower() != hash_md5(filename): - raise ValueError( # noqa: TRY301 + msg = ( f'"MD5" hash of "{filename}" file does not match the ' f"expected hash!" ) + raise ValueError( # noqa: TRY301 + msg + ) attempt = retries - except (urllib.error.URLError, OSError, ValueError): + except (urllib.error.URLError, OSError, ValueError): # noqa: PERF203 attempt += 1 print( # noqa: T201 f'An error occurred while downloading "{filename}" file ' @@ -229,7 +238,7 @@ def json_open(url: str, retries: int = 3) -> Dict: request = urllib.request.Request(url) # noqa: S310 with urllib.request.urlopen(request) as response: # noqa: S310 return json.loads(response.read()) - except (urllib.error.URLError, ValueError): + except (urllib.error.URLError, ValueError): # noqa: PERF203 attempt += 1 print( # noqa: T201 f'An error occurred while opening "{url}" url during attempt ' @@ -280,15 +289,15 @@ def unpack_gzipfile( os.makedirs(extraction_directory) try: - with gzip.open(filename) as gzip_file, open( - extraction_path, "wb" - ) as output_file: + with ( + gzip.open(filename) as gzip_file, + open(extraction_path, "wb") as output_file, + ): shutil.copyfileobj(gzip_file, output_file) except Exception as error: print(error) # noqa: T201 - raise setuptools.archive_util.UnrecognizedFormat( - f'{filename} is not a "GZIP" file!' - ) from error + msg = f'{filename} is not a "GZIP" file!' + raise setuptools.archive_util.UnrecognizedFormat(msg) from error return True diff --git a/colour_datasets/utilities/spreadsheet.py b/colour_datasets/utilities/spreadsheet.py index a5c87fd..25d7ebe 100644 --- a/colour_datasets/utilities/spreadsheet.py +++ b/colour_datasets/utilities/spreadsheet.py @@ -13,11 +13,14 @@ from __future__ import annotations import re +import typing import xlrd -from colour.hints import Dict, List from colour.utilities import CanonicalMapping, attest +if typing.TYPE_CHECKING: + from colour.hints import Dict, List + __author__ = "Colour Developers, Openpyxl Developers" __copyright__ = "Copyright 2019 Colour Developers" __copyright__ += ", " @@ -234,7 +237,7 @@ def cell_range_values(sheet: xlrd.sheet.Sheet, cell_range: str) -> List[str]: row_out = row_to_index(groups["row_out"]) for row in range(row_in, row_out + 1, 1): - table.append( + table.append( # noqa: PERF401 sheet.row_values(row, start_colx=column_in, end_colx=column_out + 1) ) diff --git a/colour_datasets/utilities/tests/test_common.py b/colour_datasets/utilities/tests/test_common.py index 669f8f6..98dfaf0 100644 --- a/colour_datasets/utilities/tests/test_common.py +++ b/colour_datasets/utilities/tests/test_common.py @@ -35,7 +35,7 @@ class TestHashMd5: tests methods. """ - def test_hash_md5(self): + def test_hash_md5(self) -> None: """Test :func:`colour_datasets.utilities.common.hash_md5` definition.""" dataset = build_Labsphere2019() @@ -63,17 +63,17 @@ class TestUrlDownload: unit tests methods. """ - def setup_method(self): + def setup_method(self) -> None: """Initialise the common tests attributes.""" - self._temporary_file = tempfile.NamedTemporaryFile(delete=False).name + self._temporary_file = tempfile.NamedTemporaryFile(delete=False).name # noqa: SIM115 - def teardown_method(self): + def teardown_method(self) -> None: """After tests actions.""" os.remove(self._temporary_file) - def test_url_download(self): + def test_url_download(self) -> None: """Test :func:`colour_datasets.utilities.common.url_download` definition.""" dataset = build_Labsphere2019() @@ -118,7 +118,7 @@ class TestJsonOpen: unit tests methods. """ - def test_json_open(self): + def test_json_open(self) -> None: """Test :func:`colour_datasets.utilities.common.json_open` definition.""" data = json_open("https://zenodo.org/api/records/3245883") @@ -134,17 +134,17 @@ class TestUnpackGzipfile: unit tests methods. """ - def setup_method(self): + def setup_method(self) -> None: """Initialise the common tests attributes.""" self._temporary_directory = tempfile.mkdtemp() - def teardown_method(self): + def teardown_method(self) -> None: """After tests actions.""" shutil.rmtree(self._temporary_directory) - def test_unpack_gzipfile(self): + def test_unpack_gzipfile(self) -> None: """ Test :func:`colour_datasets.utilities.common.unpack_gzipfile` definition. diff --git a/colour_datasets/utilities/tests/test_spreadsheet.py b/colour_datasets/utilities/tests/test_spreadsheet.py index b13ae38..0145e1c 100644 --- a/colour_datasets/utilities/tests/test_spreadsheet.py +++ b/colour_datasets/utilities/tests/test_spreadsheet.py @@ -38,7 +38,7 @@ class TestRowToIndex: definition unit tests methods. """ - def test_row_to_index(self): + def test_row_to_index(self) -> None: """ Test :func:`colour_datasets.utilities.spreadsheet.row_to_index` definition. @@ -59,7 +59,7 @@ class TestIndexToRow: definition unit tests methods. """ - def test_index_to_row(self): + def test_index_to_row(self) -> None: """ Test :func:`colour_datasets.utilities.spreadsheet.index_to_row` definition. @@ -78,7 +78,7 @@ class TestColumnToIndex: definition unit tests methods. """ - def test_column_to_index(self): + def test_column_to_index(self) -> None: """ Test :func:`colour_datasets.utilities.spreadsheet.column_to_index` definition. @@ -99,7 +99,7 @@ class TestIndexToColumn: definition unit tests methods. """ - def test_index_to_column(self): + def test_index_to_column(self) -> None: """ Test :func:`colour_datasets.utilities.spreadsheet.index_to_column` definition. @@ -118,7 +118,7 @@ class TestCellRangeValues: definition unit tests methods. """ - def test_cell_range_values(self): + def test_cell_range_values(self) -> None: """ Test :func:`colour_datasets.utilities.spreadsheet.cell_range_values` definition. diff --git a/docs/conf.py b/docs/conf.py index 8964aa4..7e5a527 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -9,7 +9,7 @@ sys.path.append(str(Path(__file__).parent.parent)) -import colour_datasets as package # noqa: E402 +import colour_datasets as package basename = re.sub("_(\\w)", lambda x: x.group(1).upper(), package.__name__.title()) diff --git a/docs/index.rst b/docs/index.rst index d164367..83a9c88 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -156,7 +156,8 @@ The *Colour Developers* can be reached via different means: - `Facebook `__ - `Github Discussions `__ - `Gitter `__ -- `Twitter `__ +- `X `__ +- `Bluesky `__ About ----- diff --git a/docs/requirements.txt b/docs/requirements.txt index da90e31..1086549 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -6,41 +6,41 @@ babel==2.16.0 beautifulsoup4==4.12.3 biblib-simple==0.1.2 cachetools==5.5.0 -certifi==2024.8.30 +certifi==2024.12.14 charset-normalizer==3.4.0 -colorama==0.4.6 ; sys_platform == 'win32' or platform_system == 'Windows' +colorama==0.4.6 ; sys_platform == 'win32' colour-science==0.4.6 -contourpy==1.3.0 +contourpy==1.3.1 cycler==0.12.1 docutils==0.21.2 -fonttools==4.54.1 +fonttools==4.55.3 idna==3.10 -imageio==2.35.1 +imageio==2.36.1 imagesize==1.4.1 -jinja2==3.1.4 +jinja2==3.1.5 kiwisolver==1.4.7 latexcodec==3.0.0 -markupsafe==3.0.1 -matplotlib==3.9.2 -numpy==2.1.2 +markupsafe==3.0.2 +matplotlib==3.10.0 +numpy==2.2.1 opencv-python==4.10.0.84 -packaging==24.1 -pillow==10.4.0 +packaging==24.2 +pillow==11.0.0 pybtex==0.24.0 pybtex-docutils==1.0.3 -pydata-sphinx-theme==0.15.4 +pydata-sphinx-theme==0.16.1 pygments==2.18.0 -pyparsing==3.1.4 +pyparsing==3.2.0 python-dateutil==2.9.0.post0 pyyaml==6.0.2 requests==2.32.3 restructuredtext-lint==1.4.0 scipy==1.14.1 -setuptools==75.1.0 ; python_full_version >= '3.12' -six==1.16.0 +setuptools==75.6.0 ; python_full_version >= '3.12' +six==1.17.0 snowballstemmer==2.2.0 soupsieve==2.6 -sphinx==8.1.2 +sphinx==8.1.3 sphinxcontrib-applehelp==2.0.0 sphinxcontrib-bibtex==2.6.3 sphinxcontrib-devhelp==2.0.0 @@ -48,8 +48,8 @@ sphinxcontrib-htmlhelp==2.1.0 sphinxcontrib-jsmath==1.0.1 sphinxcontrib-qthelp==2.0.0 sphinxcontrib-serializinghtml==2.0.0 -tomli==2.0.2 ; python_full_version < '3.11' -tqdm==4.66.5 +tomli==2.2.1 ; python_full_version < '3.11' +tqdm==4.67.1 typing-extensions==4.12.2 -urllib3==2.2.3 +urllib3==2.3.0 xlrd==1.2.0 diff --git a/pyproject.toml b/pyproject.toml index facfa47..a6a0bde 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,7 +46,7 @@ classifiers = [ ] dependencies = [ "cachetools", - "colour-science>=0.4.4", + "colour-science>=0.4.5", "imageio>=2,<3", "numpy>=1.24,<3", "opencv-python>=4,<5", @@ -100,19 +100,15 @@ build-backend = "hatchling.build" packages = [ "colour_datasets" ] [tool.codespell] -ignore-words-list = "exitance,seperately" +ignore-words-list = "assertIn,exitance,seperately,socio-economic" skip = "BIBLIOGRAPHY.bib,CONTRIBUTORS.rst" -[tool.flynt] -line_length=999 - [tool.isort] ensure_newline_before_comments = true force_grid_wrap = 0 include_trailing_comma = true line_length = 88 multi_line_output = 3 -skip_glob = ["colour_datasets/**/__init__.py"] split_on_trailing_comma = true use_parentheses = true @@ -129,91 +125,53 @@ reportUnusedExpression = false addopts = "--durations=5" [tool.ruff] -target-version = "py39" +target-version = "py310" line-length = 88 -select = [ - "A", # flake8-builtins - "ARG", # flake8-unused-arguments - # "ANN", # flake8-annotations - "B", # flake8-bugbear - # "BLE", # flake8-blind-except - "C4", # flake8-comprehensions - # "C90", # mccabe - # "COM", # flake8-commas - "DTZ", # flake8-datetimez - "D", # pydocstyle - "E", # pydocstyle - # "ERA", # eradicate - # "EM", # flake8-errmsg - "EXE", # flake8-executable - "F", # flake8 - # "FBT", # flake8-boolean-trap - "G", # flake8-logging-format - "I", # isort - "ICN", # flake8-import-conventions - "INP", # flake8-no-pep420 - "ISC", # flake8-implicit-str-concat - "N", # pep8-naming - # "PD", # pandas-vet - "PIE", # flake8-pie - "PGH", # pygrep-hooks - "PL", # pylint - # "PT", # flake8-pytest-style - # "PTH", # flake8-use-pathlib [Enable] - "Q", # flake8-quotes - "RET", # flake8-return - "RUF", # Ruff - "S", # flake8-bandit - "SIM", # flake8-simplify - "T10", # flake8-debugger - "T20", # flake8-print - # "TCH", # flake8-type-checking - "TID", # flake8-tidy-imports - "TRY", # tryceratops - "UP", # pyupgrade - "W", # pydocstyle - "YTT" # flake8-2020 -] +select = ["ALL"] ignore = [ - "B008", - "B905", - "D104", - "D200", - "D202", - "D205", - "D301", - "D400", - "I001", - "N801", - "N802", - "N803", - "N806", - "N813", - "N815", - "N816", - "PGH003", - "PIE804", - "PLE0605", - "PLR0911", - "PLR0912", - "PLR0913", - "PLR0915", - "PLR2004", - "RET504", - "RET505", - "RET506", - "RET507", - "RET508", - "TRY003", - "TRY300", + "C", # Pylint - Convention + "C90", # mccabe + "COM", # flake8-commas + "ERA", # eradicate + "FBT", # flake8-boolean-trap + "FIX", # flake8-fixme + "PT", # flake8-pytest-style + "PTH", # flake8-use-pathlib [Enable] + "TD", # flake8-todos + "ANN401", # Dynamically typed expressions (typing.Any) are disallowed in `**kwargs` + "D200", # One-line docstring should fit on one line + "D202", # No blank lines allowed after function docstring + "D205", # 1 blank line required between summary line and description + "D301", # Use `r"""` if any backslashes in a docstring + "D400", # First line should end with a period + "I001", # Import block is un-sorted or un-formatted + "N801", # Class name `.*` should use CapWords convention + "N802", # Function name `.*` should be lowercase + "N803", # Argument name `.*` should be lowercase + "N806", # Variable `.*` in function should be lowercase + "N813", # Camelcase `.*` imported as lowercase `.*` + "N815", # Variable `.*` in class scope should not be mixedCase + "N816", # Variable `.*` in global scope should not be mixedCase + "NPY002", # Replace legacy `np.random.random` call with `np.random.Generator` + "PGH003", # Use specific rule codes when ignoring type issues + "PLR0912", # Too many branches + "PLR0913", # Too many arguments in function definition + "PLR0915", # Too many statements + "PLR2004", # Magic value used in comparison, consider replacing `.*` with a constant variable + "PYI036", # Star-args in `.*` should be annotated with `object` + "PYI051", # `Literal[".*"]` is redundant in a union with `str` + "PYI056", # Calling `.append()` on `__all__` may not be supported by all type checkers (use `+=` instead) + "RUF022", # [*] `__all__` is not sorted + "TRY003", # Avoid specifying long messages outside the exception class + "UP038", # Use `X | Y` in `isinstance` call instead of `(X, Y)` ] typing-modules = ["colour.hints"] -fixable = ["B", "C", "E", "F", "PIE", "RUF", "SIM", "UP", "W"] [tool.ruff.pydocstyle] convention = "numpy" [tool.ruff.per-file-ignores] +"__init__.py" = ["D104"] "colour_datasets/examples/*" = ["INP", "T201", "T203"] "docs/*" = ["INP"] "tasks.py" = ["INP"] diff --git a/requirements.txt b/requirements.txt index 58ca8da..38aef7a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,34 +2,34 @@ # uv export --no-hashes --all-extras accessible-pygments==0.0.5 alabaster==1.0.0 -anyio==4.6.0 -appnope==0.1.4 ; platform_system == 'Darwin' +anyio==4.7.0 +appnope==0.1.4 ; sys_platform == 'darwin' argon2-cffi==23.1.0 argon2-cffi-bindings==21.2.0 arrow==1.3.0 -asttokens==2.4.1 +asttokens==3.0.0 async-lru==2.0.4 -attrs==24.2.0 +attrs==24.3.0 babel==2.16.0 backports-tarfile==1.2.0 ; python_full_version < '3.12' beautifulsoup4==4.12.3 biblib-simple==0.1.2 -bleach==6.1.0 +bleach==6.2.0 cachetools==5.5.0 -certifi==2024.8.30 +certifi==2024.12.14 cffi==1.17.1 cfgv==3.4.0 charset-normalizer==3.4.0 -click==8.1.7 -colorama==0.4.6 ; sys_platform == 'win32' or platform_system == 'Windows' +click==8.1.8 +colorama==0.4.6 ; sys_platform == 'win32' colour-science==0.4.6 comm==0.2.2 -contourpy==1.3.0 -coverage==7.6.2 +contourpy==1.3.1 +coverage==7.6.9 coveralls==4.0.1 -cryptography==43.0.1 ; sys_platform == 'linux' +cryptography==44.0.0 ; sys_platform == 'linux' cycler==0.12.1 -debugpy==1.8.7 +debugpy==1.8.11 decorator==5.1.1 defusedxml==0.7.1 distlib==0.3.9 @@ -38,34 +38,34 @@ docutils==0.21.2 exceptiongroup==1.2.2 ; python_full_version < '3.11' execnet==2.1.1 executing==2.1.0 -fastjsonschema==2.20.0 +fastjsonschema==2.21.1 filelock==3.16.1 -fonttools==4.54.1 +fonttools==4.55.3 fqdn==1.5.1 h11==0.14.0 -hatch==1.12.0 -hatchling==1.25.0 -httpcore==1.0.6 -httpx==0.27.2 +hatch==1.14.0 +hatchling==1.27.0 +httpcore==1.0.7 +httpx==0.28.1 hyperlink==21.0.0 -identify==2.6.1 +identify==2.6.3 idna==3.10 -imageio==2.35.1 +imageio==2.36.1 imagesize==1.4.1 -importlib-metadata==8.5.0 +importlib-metadata==8.5.0 ; python_full_version < '3.12' iniconfig==2.0.0 invoke==2.2.0 ipykernel==6.29.5 -ipython==8.28.0 +ipython==8.31.0 ipywidgets==8.1.5 isoduration==20.11.0 jaraco-classes==3.4.0 jaraco-context==6.0.1 jaraco-functools==4.1.0 -jedi==0.19.1 +jedi==0.19.2 jeepney==0.8.0 ; sys_platform == 'linux' -jinja2==3.1.4 -json5==0.9.25 +jinja2==3.1.5 +json5==0.10.0 jsonpointer==3.0.0 jsonschema==4.23.0 jsonschema-specifications==2024.10.1 @@ -73,65 +73,65 @@ jupyter==1.1.1 jupyter-client==8.6.3 jupyter-console==6.6.3 jupyter-core==5.7.2 -jupyter-events==0.10.0 +jupyter-events==0.11.0 jupyter-lsp==2.2.5 -jupyter-server==2.14.2 +jupyter-server==2.15.0 jupyter-server-terminals==0.5.3 -jupyterlab==4.2.5 +jupyterlab==4.3.4 jupyterlab-pygments==0.3.0 jupyterlab-server==2.27.3 jupyterlab-widgets==3.0.13 -keyring==25.4.1 +keyring==25.5.0 kiwisolver==1.4.7 latexcodec==3.0.0 markdown-it-py==3.0.0 -markupsafe==3.0.1 -matplotlib==3.9.2 +markupsafe==3.0.2 +matplotlib==3.10.0 matplotlib-inline==0.1.7 mdurl==0.1.2 mistune==3.0.2 more-itertools==10.5.0 -nbclient==0.10.0 +nbclient==0.10.2 nbconvert==7.16.4 nbformat==5.10.4 nest-asyncio==1.6.0 -nh3==0.2.18 +nh3==0.2.20 nodeenv==1.9.1 -notebook==7.2.2 +notebook==7.3.2 notebook-shim==0.2.4 -numpy==2.1.2 +numpy==2.2.1 opencv-python==4.10.0.84 overrides==7.7.0 -packaging==24.1 +packaging==24.2 pandocfilters==1.5.1 parso==0.8.4 pathspec==0.12.1 pexpect==4.9.0 -pillow==10.4.0 -pkginfo==1.10.0 +pillow==11.0.0 +pkginfo==1.12.0 platformdirs==4.3.6 pluggy==1.5.0 pre-commit==4.0.1 -prometheus-client==0.21.0 +prometheus-client==0.21.1 prompt-toolkit==3.0.48 -psutil==6.0.0 +psutil==6.1.1 ptyprocess==0.7.0 pure-eval==0.2.3 pybtex==0.24.0 pybtex-docutils==1.0.3 pycparser==2.22 -pydata-sphinx-theme==0.15.4 +pydata-sphinx-theme==0.16.1 pygments==2.18.0 -pyparsing==3.1.4 -pyright==1.1.384 -pytest==8.3.3 -pytest-cov==5.0.0 +pyparsing==3.2.0 +pyright==1.1.391 +pytest==8.3.4 +pytest-cov==6.0.0 pytest-xdist==3.6.1 python-dateutil==2.9.0.post0 -python-json-logger==2.0.7 -pywin32==307 ; platform_python_implementation != 'PyPy' and sys_platform == 'win32' +python-json-logger==3.2.1 +pywin32==308 ; platform_python_implementation != 'PyPy' and sys_platform == 'win32' pywin32-ctypes==0.2.3 ; sys_platform == 'win32' -pywinpty==2.0.13 ; os_name == 'nt' +pywinpty==2.0.14 ; os_name == 'nt' and sys_platform != 'darwin' and sys_platform != 'linux' pyyaml==6.0.2 pyzmq==26.2.0 readme-renderer==44.0 @@ -142,18 +142,18 @@ restructuredtext-lint==1.4.0 rfc3339-validator==0.1.4 rfc3986==2.0.0 rfc3986-validator==0.1.1 -rich==13.9.2 -rpds-py==0.20.0 +rich==13.9.4 +rpds-py==0.22.3 scipy==1.14.1 secretstorage==3.3.3 ; sys_platform == 'linux' send2trash==1.8.3 -setuptools==75.1.0 +setuptools==75.6.0 shellingham==1.5.4 -six==1.16.0 +six==1.17.0 sniffio==1.3.1 snowballstemmer==2.2.0 soupsieve==2.6 -sphinx==8.1.2 +sphinx==8.1.3 sphinxcontrib-applehelp==2.0.0 sphinxcontrib-bibtex==2.6.3 sphinxcontrib-devhelp==2.0.0 @@ -163,28 +163,28 @@ sphinxcontrib-qthelp==2.0.0 sphinxcontrib-serializinghtml==2.0.0 stack-data==0.6.3 terminado==0.18.1 -tinycss2==1.3.0 +tinycss2==1.4.0 toml==0.10.2 -tomli==2.0.2 ; python_full_version <= '3.11' +tomli==2.2.1 ; python_full_version <= '3.11' tomli-w==1.1.0 tomlkit==0.13.2 -tornado==6.4.1 -tqdm==4.66.5 +tornado==6.4.2 +tqdm==4.67.1 traitlets==5.14.3 -trove-classifiers==2024.10.12 -twine==5.1.1 -types-python-dateutil==2.9.0.20241003 +trove-classifiers==2024.10.21.16 +twine==6.0.1 +types-python-dateutil==2.9.0.20241206 typing-extensions==4.12.2 uri-template==1.3.0 -urllib3==2.2.3 +urllib3==2.3.0 userpath==1.9.2 -uv==0.4.20 -virtualenv==20.26.6 +uv==0.5.11 +virtualenv==20.28.0 wcwidth==0.2.13 -webcolors==24.8.0 +webcolors==24.11.1 webencodings==0.5.1 websocket-client==1.8.0 widgetsnbextension==4.0.13 xlrd==1.2.0 -zipp==3.20.2 +zipp==3.21.0 ; python_full_version < '3.12' zstandard==0.23.0 diff --git a/tasks.py b/tasks.py index 8442958..ec8e5ba 100644 --- a/tasks.py +++ b/tasks.py @@ -10,6 +10,7 @@ import inspect import os import re +import typing import uuid import biblib.bib @@ -20,7 +21,9 @@ if not hasattr(inspect, "getargspec"): inspect.getargspec = inspect.getfullargspec # pyright: ignore -from invoke.context import Context +if typing.TYPE_CHECKING: + from invoke.context import Context + from invoke.tasks import task __author__ = "Colour Developers" @@ -72,7 +75,7 @@ def clean( docs: bool = True, bytecode: bool = False, pytest: bool = True, -): +) -> None: """ Clean the project. @@ -112,7 +115,7 @@ def formatting( ctx: Context, asciify: bool = True, bibtex: bool = True, -): +) -> None: """ Convert unicode characters to ASCII and cleanup the *BibTeX* file. @@ -155,7 +158,7 @@ def quality( ctx: Context, pyright: bool = True, rstlint: bool = True, -): +) -> None: """ Check the codebase with *Pyright* and lints various *restructuredText* files with *rst-lint*. @@ -180,7 +183,7 @@ def quality( @task -def precommit(ctx: Context): +def precommit(ctx: Context) -> None: """ Run the "pre-commit" hooks on the codebase. @@ -195,7 +198,7 @@ def precommit(ctx: Context): @task -def tests(ctx: Context): +def tests(ctx: Context) -> None: """ Run the unit tests with *Pytest*. @@ -216,7 +219,7 @@ def tests(ctx: Context): @task -def examples(ctx: Context): +def examples(ctx: Context) -> None: """ Run the examples. @@ -236,7 +239,7 @@ def examples(ctx: Context): @task(formatting, quality, precommit, tests, examples) -def preflight(ctx: Context): # noqa: ARG001 +def preflight(ctx: Context) -> None: # noqa: ARG001 """ Perform the preflight tasks, i.e., *formatting*, *tests*, *quality*, and *examples*. @@ -251,7 +254,7 @@ def preflight(ctx: Context): # noqa: ARG001 @task -def docs(ctx: Context, html: bool = True, pdf: bool = True): +def docs(ctx: Context, html: bool = True, pdf: bool = True) -> None: """ Build the documentation. @@ -276,7 +279,7 @@ def docs(ctx: Context, html: bool = True, pdf: bool = True): @task -def todo(ctx: Context): +def todo(ctx: Context) -> None: """ Export the TODO items. @@ -293,7 +296,7 @@ def todo(ctx: Context): @task -def requirements(ctx: Context): +def requirements(ctx: Context) -> None: """ Export the *requirements.txt* file. @@ -314,7 +317,7 @@ def requirements(ctx: Context): @task(clean, preflight, docs, todo, requirements) -def build(ctx: Context): +def build(ctx: Context) -> None: """ Build the project and runs dependency tasks, i.e., *docs*, *todo*, and *preflight*. @@ -331,7 +334,7 @@ def build(ctx: Context): @task -def virtualise(ctx: Context, tests: bool = True): +def virtualise(ctx: Context, tests: bool = True) -> None: """ Create a virtual environment for the project build. @@ -349,9 +352,7 @@ def virtualise(ctx: Context, tests: bool = True): ctx.run(f"mv {PYPI_ARCHIVE_NAME}-{APPLICATION_VERSION} {unique_name}") ctx.run(f"rm -rf {unique_name}/{PYTHON_PACKAGE_NAME}/resources") ctx.run( - "ln -s ../../../{0}/resources {1}/{0}".format( - PYTHON_PACKAGE_NAME, unique_name - ) + f"ln -s ../../../{PYTHON_PACKAGE_NAME}/resources {unique_name}/{PYTHON_PACKAGE_NAME}" # noqa: E501 ) with ctx.cd(unique_name): @@ -371,7 +372,7 @@ def virtualise(ctx: Context, tests: bool = True): @task -def tag(ctx: Context): +def tag(ctx: Context) -> None: """ Tag the repository according to defined version using *git-flow*. @@ -385,7 +386,8 @@ def tag(ctx: Context): result = ctx.run("git rev-parse --abbrev-ref HEAD", hide="both") if result.stdout.strip() != "develop": # pyright: ignore - raise RuntimeError("Are you still on a feature or master branch?") + msg = "Are you still on a feature or master branch?" + raise RuntimeError(msg) with open(os.path.join(PYTHON_PACKAGE_NAME, "__init__.py")) as file_handle: file_content = file_handle.read() @@ -405,7 +407,7 @@ def tag(ctx: Context): 1 ) - version = ".".join((major_version, minor_version, change_version)) + version = f"{major_version}.{minor_version}.{change_version}" result = ctx.run("git ls-remote --tags upstream", hide="both") remote_tags = result.stdout.strip().split("\n") # pyright: ignore @@ -414,17 +416,18 @@ def tag(ctx: Context): tags.add(remote_tag.split("refs/tags/")[1].replace("refs/tags/", "^{}")) version_tags = sorted(tags) if f"v{version}" in version_tags: - raise RuntimeError( + msg = ( f'A "{PYTHON_PACKAGE_NAME}" "v{version}" tag already exists in ' f"remote repository!" ) + raise RuntimeError(msg) ctx.run(f"git flow release start v{version}") ctx.run(f"git flow release finish v{version}") @task(build) -def release(ctx: Context): +def release(ctx: Context) -> None: """ Release the project to *Pypi* with *Twine*. @@ -441,7 +444,7 @@ def release(ctx: Context): @task -def sha256(ctx: Context): +def sha256(ctx: Context) -> None: """ Compute the project *Pypi* package *sha256* with *OpenSSL*. diff --git a/utilities/export_todo.py b/utilities/export_todo.py index acc86e0..e24b4de 100755 --- a/utilities/export_todo.py +++ b/utilities/export_todo.py @@ -93,7 +93,7 @@ def extract_todo_items(root_directory: str) -> dict: return todo_items -def export_todo_items(todo_items: dict, file_path: str): +def export_todo_items(todo_items: dict, file_path: str) -> None: """ Export TODO items to given file. diff --git a/utilities/unicode_to_ascii.py b/utilities/unicode_to_ascii.py index 0fb9fea..d5c5e11 100755 --- a/utilities/unicode_to_ascii.py +++ b/utilities/unicode_to_ascii.py @@ -28,7 +28,7 @@ } -def unicode_to_ascii(root_directory: str): +def unicode_to_ascii(root_directory: str) -> None: """ Recursively convert from unicode to ASCII *.py*, *.bib* and *.rst* files in given directory.