diff --git a/.github/workflows/continuous-integration-static-type-checking.yml b/.github/workflows/continuous-integration-static-type-checking.yml new file mode 100644 index 00000000..aa5c27b4 --- /dev/null +++ b/.github/workflows/continuous-integration-static-type-checking.yml @@ -0,0 +1,35 @@ +name: Continuous Integration - Static Type Checking + +on: [push, pull_request] + +jobs: + continuous-integration-static-type-checking: + name: ${{ matrix.os }} - Python ${{ matrix.python-version }} + strategy: + matrix: + os: [macOS-latest] + python-version: [3.11] + fail-fast: false + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v1 + - name: Environment Variables + run: | + echo "CI_PACKAGE=opencolorio_config_aces" >> $GITHUB_ENV + shell: bash + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + - name: Install Dependencies (macOS) + if: matrix.os == 'macOS-latest' + run: | + brew install graphviz + export GRAPHVIZ_DIR="/usr/local/Cellar/graphviz/" + pip install pygraphviz --global-option=build_ext --global-option="-I$GRAPHVIZ_DIR/include" --global-option="-L$GRAPHVIZ_DIR/lib" + - name: Install Package Dependencies + run: | + pip install -r requirements.txt + - name: Static Type Checking + run: | + pyright --skipunannotated \ No newline at end of file diff --git a/README.rst b/README.rst index 6de00b8b..dff11a95 100644 --- a/README.rst +++ b/README.rst @@ -146,6 +146,7 @@ Development Dependencies - `invoke `__ - `pre-commit `__ - `pydata-sphinx-theme `__ +- `pyright `__ - `pytest `__ - `pytest-cov `__ - `restructuredtext-lint `__ diff --git a/docs/installation.rst b/docs/installation.rst index b3918ff7..13cb4f49 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -94,6 +94,7 @@ Development Dependencies - `invoke `__ - `pre-commit `__ - `pydata-sphinx-theme `__ +- `pyright `__ - `pytest `__ - `pytest-cov `__ - `restructuredtext-lint `__ diff --git a/opencolorio_config_aces/__init__.py b/opencolorio_config_aces/__init__.py index 48b9f54d..489845ce 100644 --- a/opencolorio_config_aces/__init__.py +++ b/opencolorio_config_aces/__init__.py @@ -108,7 +108,6 @@ "print_aces_taxonomy", "unclassify_ctl_transforms", "version_aces_dev", - "version_config_mapping_file", ] __all__ += [ "DescriptionStyle", diff --git a/opencolorio_config_aces/clf/discover/classify.py b/opencolorio_config_aces/clf/discover/classify.py index 8becb938..848e40c5 100644 --- a/opencolorio_config_aces/clf/discover/classify.py +++ b/opencolorio_config_aces/clf/discover/classify.py @@ -14,12 +14,15 @@ - :func:`opencolorio_config_aces.print_clf_taxonomy` """ +from __future__ import annotations + import itertools import logging import os import xml.etree.ElementTree from collections import defaultdict -from collections.abc import Mapping +from collections.abc import Mapping, Sequence +from typing import Any, Callable, cast from opencolorio_config_aces.config.reference.discover.classify import ( ACESTransformID, @@ -64,86 +67,66 @@ logger = logging.getLogger(__name__) -URN_CLF = "urn:aswf:ocio:transformId:1.0" +URN_CLF: str = "urn:aswf:ocio:transformId:1.0" """ *CLF* Uniform Resource Name (*URN*). - -URN_CLF : unicode """ -SEPARATOR_VERSION_CLF = "." +SEPARATOR_VERSION_CLF: str = "." """ *CLFtransformID* separator used to tokenize the *VERSION* parts of the *CLFtransformID*. urn:aswf:ocio:transformId:1.0:OCIO:ACES:AP0_to_AP1-Gamma2pnt2:1.0 |---| |---| - -SEPARATOR_VERSION_CLF : unicode """ -SEPARATOR_ID_CLF = ":" +SEPARATOR_ID_CLF: str = ":" """ *CLFtransformID* separator used to tokenize the *ID* part of the *CLFtransformID*. urn:aswf:ocio:transformId:1.0:OCIO:ACES:AP0_to_AP1-Gamma2pnt2:1.0 |-------------URN-----------|:|----------------ID---------------| - -SEPARATOR_ID_CLF : unicode """ -EXTENSION_CLF = ".clf" +EXTENSION_CLF: str = ".clf" """ *CLF* transform extension. - -EXTENSION_CLF : unicode """ -NAMESPACE_CLF = "OCIO" +NAMESPACE_CLF: str = "OCIO" """ Namespace for the *OCIO* *CLF* transforms. - -NAMESPACE_CLF : unicode """ -TRANSFORM_TYPES_CLF = ["", "Input", "Utility"] +TRANSFORM_TYPES_CLF: list = ["", "Input", "Utility"] """ *CLF* transform types. - -TRANSFORM_TYPES_CLF : list """ -TRANSFORM_FAMILIES_CLF = {"input": "Input", "utility": "Utility"} +TRANSFORM_FAMILIES_CLF: dict = {"input": "Input", "utility": "Utility"} """ *CLF* transform families mapping the *CLF* transform directories to family names. - -TRANSFORM_FAMILIES_CLF : dict """ -TRANSFORM_GENUS_DEFAULT_CLF = "undefined" +TRANSFORM_GENUS_DEFAULT_CLF: str = "undefined" """ *CLF* transform default genus, i.e. *undefined*. - -TRANSFORM_GENUS_DEFAULT_CLF : unicode """ -TRANSFORM_FILTERERS_DEFAULT_CLF = [] +TRANSFORM_FILTERERS_DEFAULT_CLF: list = [] """ Default list of *CLF* transform filterers. - -TRANSFORM_FILTERERS_DEFAULT_CLF : list """ -PATTERNS_DESCRIPTION_CLF = {} +PATTERNS_DESCRIPTION_CLF: dict = {} """ *CLF* transform description substitution patterns. - -PATTERNS_DESCRIPTION_CLF : dict """ -ROOT_TRANSFORMS_CLF = os.path.normpath( +ROOT_TRANSFORMS_CLF: str = os.path.normpath( os.environ.get( "OPENCOLORIO_CONFIG_ACES__CLF_TRANSFORMS_ROOT", os.path.join(os.path.dirname(__file__), "..", "transforms"), @@ -154,26 +137,26 @@ sub-module repository. It can be defined by setting the `OPENCOLORIO_CONFIG_ACES__CLF_TRANSFORMS_ROOT` environment variable with the local 'transforms/clf' directory. - -ROOT_TRANSFORMS_CLF : unicode """ -def clf_transform_relative_path(path, root_directory=ROOT_TRANSFORMS_CLF): +def clf_transform_relative_path( + path: str, root_directory: str = ROOT_TRANSFORMS_CLF +) -> str: """ Return the relative path from given *CLF* transform to the *CLF* transforms root directory. Parameters ---------- - path : unicode + path *CLF* transform absolute path. - root_directory : unicode, optional + root_directory *CLF* transforms root directory. Returns ------- - unicode + :class:`str` *CLF* transform relative path. """ @@ -187,7 +170,7 @@ class CLFTransformID: Parameters ---------- - clf_transform_id : unicode + clf_transform_id *CLFtransformID*, e.g. *urn:aswf:ocio:transformId:v1.0:ACES.OCIO.AP0_to_AP1-Gamma2pnt2.c1.v1*. @@ -210,30 +193,30 @@ class CLFTransformID: __repr__ """ - def __init__(self, clf_transform_id): - self._clf_transform_id = clf_transform_id + def __init__(self, clf_transform_id: str) -> None: + self._clf_transform_id: str = clf_transform_id - self._urn = None - self._type = None - self._namespace = None - self._name = None - self._major_version = None - self._minor_version = None - self._patch_version = None - self._source = None - self._target = None + self._urn: str | None = None + self._type: str | None = None + self._namespace: str | None = None + self._name: str | None = None + self._major_version: str | None = None + self._minor_version: str | None = None + self._patch_version: str | None = None + self._source: str | None = None + self._target: str | None = None self._parse() @property - def clf_transform_id(self): + def clf_transform_id(self) -> str | None: """ Getter property for the *CLFtransformID*, e.g. *urn:aswf:ocio:transformId:v1.0:ACES.OCIO.AP0_to_AP1-Gamma2pnt2.c1.v1*. Returns ------- - unicode + :class:`str` or None *CLFtransformID*. Notes @@ -244,14 +227,14 @@ def clf_transform_id(self): return self._clf_transform_id @property - def urn(self): + def urn(self) -> str | None: """ Getter property for the *CLFtransformID* Uniform Resource Name (*URN*), e.g. *urn:aswf:ocio:transformId:v1.0*. Returns ------- - unicode + :class:`str` or None *CLFtransformID* Uniform Resource Name (*URN*). Notes @@ -262,13 +245,13 @@ def urn(self): return self._urn @property - def type(self): # noqa: A003 + def type(self) -> str | None: # noqa: A003 """ Getter property for the *CLFtransformID* type, e.g. *ACES*. Returns ------- - unicode + :class:`str` or None *CLFtransformID* type. Notes @@ -279,13 +262,13 @@ def type(self): # noqa: A003 return self._type @property - def namespace(self): + def namespace(self) -> str | None: """ Getter property for the *CLFtransformID* namespace, e.g. *OCIO*. Returns ------- - unicode + :class:`str` or None *CLFtransformID* namespace. Notes @@ -296,14 +279,14 @@ def namespace(self): return self._namespace @property - def name(self): + def name(self) -> str | None: """ Getter property for the *CLFtransformID* name, e.g. *AP0_to_AP1-Gamma2pnt2*. Returns ------- - unicode + :class:`str` or None *CLFtransformID* name. Notes @@ -314,13 +297,13 @@ def name(self): return self._name @property - def major_version(self): + def major_version(self) -> str | None: """ Getter property for the *CLFtransformID* major version number, e.g. *c1*. Returns ------- - unicode + :class:`str` or None *CLFtransformID* major version number. Notes @@ -331,13 +314,13 @@ def major_version(self): return self._major_version @property - def minor_version(self): + def minor_version(self) -> str | None: """ Getter property for the *CLFtransformID* minor version number, e.g. *v1*. Returns ------- - unicode + :class:`str` or None *CLFtransformID* minor version number. Notes @@ -348,13 +331,13 @@ def minor_version(self): return self._minor_version @property - def patch_version(self): + def patch_version(self) -> str | None: """ Getter property for the *CLFtransformID* patch version number. Returns ------- - unicode + :class:`str` or None *CLFtransformID* patch version number. Notes @@ -365,13 +348,13 @@ def patch_version(self): return self._patch_version @property - def source(self): + def source(self) -> str | None: """ Getter property for the *CLFtransformID* source colourspace. Returns ------- - unicode + :class:`str` or None *CLFtransformID* source colourspace. Notes @@ -382,13 +365,13 @@ def source(self): return self._source @property - def target(self): + def target(self) -> str | None: """ Getter property for the *CLFtransformID* target colourspace. Returns ------- - unicode + :class:`str` or None *CLFtransformID* target colourspace. Notes @@ -398,31 +381,31 @@ def target(self): return self._target - def __str__(self): + def __str__(self) -> str: """ Return a formatted string representation of the *CLFtransformID*. Returns ------- - unicode + :class:`str` Formatted string representation. """ return f"{self.__class__.__name__}('{self._clf_transform_id}')" - def __repr__(self): + def __repr__(self) -> str: """ Return an evaluable string representation of the *CLFtransformID*. Returns ------- - unicode + :class:`str` Evaluable string representation. """ return str(self) - def _parse(self): + def _parse(self) -> None: """Parse the *CLFtransformID*.""" if self._clf_transform_id is None: @@ -472,13 +455,13 @@ class CLFTransform: Parameters ---------- - path : unicode + path *CLF* transform path. - family : unicode, optional + family *CLF* transform family, e.g. *aces* - genus : unicode, optional + genus *CLF* transform genus, e.g. *undefined* - siblings : array_like, optional + siblings *CLF* transform siblings, e.g. inverse transform. Attributes @@ -502,34 +485,40 @@ class CLFTransform: __ne__ """ - def __init__(self, path, family=None, genus=None, siblings=None): + def __init__( + self, + path: str, + family: str | None = None, + genus: str | None = None, + siblings: Sequence | None = None, + ) -> None: if siblings is None: siblings = [] - self._path = os.path.abspath(os.path.normpath(path)) + self._path: str = os.path.abspath(os.path.normpath(path)) - self._code = None - self._clf_transform_id = None - self._user_name = None - self._description = "" - self._input_descriptor = "" - self._output_descriptor = "" - self._information = {} + self._code: str | None = None + self._clf_transform_id: CLFTransformID | None = None + self._user_name: str | None = None + self._description: str | None = "" + self._input_descriptor: str | None = "" + self._output_descriptor: str | None = "" + self._information: dict = {} - self._family = family - self._genus = genus - self._siblings = siblings + self._family: str | None = family + self._genus: str | None = genus + self._siblings: Sequence | None = siblings self._parse() @property - def path(self): + def path(self) -> str | None: """ Getter property for the *CLF* transform path. Returns ------- - unicode + :class:`str` or None *CLF* transform path. Notes @@ -540,14 +529,14 @@ def path(self): return self._path @property - def code(self): + def code(self) -> str | None: """ Getter property for the *CLF* transform code, i.e. the *CLF* transform file content. Returns ------- - unicode + :class:`str` or None *CLF* transform code. Notes @@ -560,13 +549,13 @@ def code(self): return self._code @property - def clf_transform_id(self): + def clf_transform_id(self) -> CLFTransformID | None: """ Getter property for the *CLF* transform *CLFtransformID*. Returns ------- - unicode + :class:`CLFTransformID` *CLF* transform *CLFtransformID*. Notes @@ -577,13 +566,13 @@ def clf_transform_id(self): return self._clf_transform_id @property - def user_name(self): + def user_name(self) -> str | None: """ Getter property for the *CLF* transform user name. Returns ------- - unicode + :class:`str` or None *CLF* transform user name. Notes @@ -594,14 +583,14 @@ def user_name(self): return self._user_name @property - def description(self): + def description(self) -> str | None: """ Getter property for the *CLF* transform description extracted from parsing the file content header. Returns ------- - unicode + :class:`str` or None *CLF* transform description. Notes @@ -612,14 +601,14 @@ def description(self): return self._description @property - def input_descriptor(self): + def input_descriptor(self) -> str | None: """ Getter property for the *CLF* transform input descriptor extracted from parsing the file content header. Returns ------- - unicode + :class:`str` or None *CLF* transform input descriptor. Notes @@ -630,14 +619,14 @@ def input_descriptor(self): return self._input_descriptor @property - def output_descriptor(self): + def output_descriptor(self) -> str | None: """ Getter property for the *CLF* transform output descriptor extracted from parsing the file content header. Returns ------- - unicode + :class:`str` or None *CLF* transform output descriptor. Notes @@ -648,14 +637,14 @@ def output_descriptor(self): return self._output_descriptor @property - def information(self): + def information(self) -> dict: """ Getter property for the *CLF* transform information extracted from parsing the file content header. Returns ------- - dict + :class:`dict` *CLF* transform information. Notes @@ -666,7 +655,7 @@ def information(self): return self._information @property - def family(self): + def family(self) -> str | None: """ Getter property for the *CLF* transform family, e.g. *aces*, a value in :attr:`opencolorio_config_aces.clf.reference.\ @@ -674,7 +663,7 @@ def family(self): Returns ------- - unicode + :class:`str` or None *CLF* transform family. Notes @@ -685,13 +674,13 @@ def family(self): return self._family @property - def genus(self): + def genus(self) -> str | None: """ Getter property for the *CLF* transform genus, e.g. *undefined*. Returns ------- - unicode + :class:`str` or None *CLF* transform genus. Notes @@ -702,14 +691,14 @@ def genus(self): return self._genus @property - def siblings(self): + def siblings(self) -> Sequence | None: """ Getter property for the *CLF* transform siblings, e.g. inverse transform. Returns ------- - unicode + :class:`Sequence` or None *CLF* transform siblings. Notes @@ -719,7 +708,7 @@ def siblings(self): return self._siblings - def __getattr__(self, item): + def __getattr__(self, item: str) -> Any: """ Reimplement the :meth:`object.__getattr__` so that unsuccessful attribute lookup on :class:`opencolorio_config_aces.clf.reference.\ @@ -729,12 +718,12 @@ def __getattr__(self, item): Parameters ---------- - item : unicode + item Attribute to lookup the value of. Returns ------- - object + :class:`object` Attribute value. """ @@ -742,13 +731,13 @@ def __getattr__(self, item): return getattr(clf_transform_id, item) - def __str__(self): + def __str__(self) -> str: """ Return a formatted string representation of the *CLF* transform. Returns ------- - unicode + :class:`str` Formatted string representation. """ @@ -757,30 +746,30 @@ def __str__(self): f"'{clf_transform_relative_path(self._path)}')" ) - def __repr__(self): + def __repr__(self) -> str: """ Return an evaluable representation of the *CLF* transform. Returns ------- - unicode + :class:`str` Evaluable string representation. """ return str(self) - def __eq__(self, other): + def __eq__(self, other: Any) -> bool: """ Return whether the *CLF* transform is equal to given other object. Parameters ---------- - other : object + other Object to test whether it is equal to the *CLF* transform. Returns ------- - bool + :class:`bool` Is given object equal to *CLF* transform. """ @@ -789,25 +778,25 @@ def __eq__(self, other): else: return self._path == other.path - def __ne__(self, other): + def __ne__(self, other: Any) -> bool: """ Return whether the *CLF* transform is not equal to given other object. Parameters ---------- - other : object + other Object to test whether it is not equal to the *CLF* transform. Returns ------- - bool + :class:`bool` Is given object not equal to *CLF* transform. """ return not (self == other) - def _parse(self): + def _parse(self) -> None: """Parse the *CLF* transform.""" tree = xml.etree.ElementTree.parse(self._path) # noqa: S314 @@ -855,9 +844,9 @@ class CLFTransformPair: Parameters ---------- - forward_transform : CLFTransform + forward_transform *CLF* transform forward transform. - inverse_transform : CLFTransform + inverse_transform *CLF* transform inverse transform. Attributes @@ -873,18 +862,20 @@ class CLFTransformPair: __ne__ """ - def __init__(self, forward_transform, inverse_transform): + def __init__( + self, forward_transform: CLFTransform, inverse_transform: CLFTransform + ) -> None: self._forward_transform = forward_transform self._inverse_transform = inverse_transform @property - def forward_transform(self): + def forward_transform(self) -> CLFTransform: """ Getter property for the *CLF* transform pair forward transform. Returns ------- - unicode + :class:`CLFTransform` *CLF* transform pair forward transform. Notes @@ -895,13 +886,13 @@ def forward_transform(self): return self._forward_transform @property - def inverse_transform(self): + def inverse_transform(self) -> CLFTransform: """ Getter property for the *CLF* transform pair inverse transform. Returns ------- - unicode + :class:`CLFTransform` *CLF* transform pair inverse transform. Notes @@ -911,13 +902,13 @@ def inverse_transform(self): return self._inverse_transform - def __str__(self): + def __str__(self) -> str: """ Return a formatted string representation of the *CLF* transform pair. Returns ------- - unicode + :class:`str` Formatted string representation. """ @@ -927,31 +918,31 @@ def __str__(self): f"{self._inverse_transform!s}')" ) - def __repr__(self): + def __repr__(self) -> str: """ Return an evaluable string representation of the *CLF* transform pair. Returns ------- - unicode + :class:`str` Evaluable string representation. """ return str(self) - def __eq__(self, other): + def __eq__(self, other: Any) -> bool: """ Return whether the *CLF* transform pair is equal to given other object. Parameters ---------- - other : object + other Object to test whether it is equal to the *CLF* transform pair. Returns ------- - bool + :class:`bool` Is given object equal to *CLF* transform pair. """ @@ -962,49 +953,44 @@ def __eq__(self, other): self._inverse_transform == other._inverse_transform ) - def __ne__(self, other): + def __ne__(self, other: Any) -> bool: """ Return whether the *CLF* transform pair is not equal to given other object. Parameters ---------- - other : object + other Object to test whether it is not equal to the *CLF* transform pair. Returns ------- - bool + :class:`bool` Is given object not equal to *CLF* transform pair. """ return not (self == other) -def find_clf_transform_pairs(clf_transforms): +def find_clf_transform_pairs( + clf_transforms: Sequence[str], +) -> defaultdict[str, dict[str, list[str]]]: """ Find the pairs in given list of *CLF* transform paths. Parameters ---------- - clf_transforms : array_like + clf_transforms *CLF* transform paths to find the pairs from. Returns ------- - dict - .. math:: - - \\{``basename_1'': \\{ - ``forward\\_transform'': ``forward\\_transform_1.clf'', - ``inverse\\_transform'': ``inverse\\_transform_1.clf''\\}, - \\ldots, 'basename_n': \\{ - ``forward\\_transform'': ``forward\\_transform_n.clf'', - ``inverse\\_transform'': ``inverse\\_transform_n.clf''\\}\\} + :class:`defaultdict` + Pairs of *CLF* transform paths. """ - def stem(path): + def stem(path: str) -> str: """Return the stem of given path.""" return os.path.splitext(os.path.basename(path))[0] @@ -1034,29 +1020,26 @@ def stem(path): clf_transform_pairs[basename]["inverse_transform"].siblings.append( clf_transform_pairs[basename]["forward_transform"] ) + return clf_transform_pairs -def discover_clf_transforms(root_directory=ROOT_TRANSFORMS_CLF): +def discover_clf_transforms( + root_directory: str = ROOT_TRANSFORMS_CLF, +) -> defaultdict[str, list[str]]: """ Discover the *CLF* transform paths in given root directory: The given directory is traversed and the `*.clf` files are collected. Parameters ---------- - root_directory : unicode + root_directory Root directory to traverse to find the *CLF* transforms. Returns ------- - dict - .. math:: - - \\{``directory_1'': - \\left[``transform_a.clf'', ``transform_b.clf''\\right],\\\\ - \\ldots,\\\\ - ``directory_n'': - \\left[``transform_c.clf'', ``transform_d.clf''\\right]\\} + :class:`defaultdict` + Discovered *CLF* transform paths. Examples -------- @@ -1092,32 +1075,27 @@ def discover_clf_transforms(root_directory=ROOT_TRANSFORMS_CLF): return clf_transforms -def classify_clf_transforms(unclassified_clf_transforms): +TypeClassifiedCLFTransforms = dict[ + str, dict[str, dict[str, CLFTransform | CLFTransformPair]] +] + + +def classify_clf_transforms( + unclassified_clf_transforms: defaultdict[str, list[str]] +) -> TypeClassifiedCLFTransforms: """ Classify given *CLF* transforms. Parameters ---------- - unclassified_clf_transforms : dict + unclassified_clf_transforms Unclassified *CLF* transforms as returned by :func:`opencolorio_config_aces.discover_clf_transforms` definition. Returns ------- - dict - .. math:: - - \\{``family_1'': \\{``genus_1'': \\{\\}_{CLF_1}, - \\ldots, - ``family_n'': \\{``genus_2'':\\{\\}_{CLF_2}\\}\\} - - where - - .. math:: - - \\{\\}_{CLF_n}=\\{``basename_n'': CLFTransform_n, - \\ldots, - ``basename_{n + 1}'': CLFTransform_{n + 1}\\} + :class:`dict` + Classified *CLF* transforms. Examples -------- @@ -1162,7 +1140,9 @@ def classify_clf_transforms(unclassified_clf_transforms): ).items(): if len(pairs) == 1: clf_transform = CLFTransform( - list(pairs.values())[0], family, genus + cast(str, next(iter(pairs.values()))), + family, + genus, ) logger.debug( @@ -1175,10 +1155,10 @@ def classify_clf_transforms(unclassified_clf_transforms): elif len(pairs) == 2: forward_clf_transform = CLFTransform( - pairs["forward_transform"], family, genus + cast(str, pairs["forward_transform"]), family, genus ) inverse_clf_transform = CLFTransform( - pairs["inverse_transform"], family, genus + cast(str, pairs["inverse_transform"]), family, genus ) clf_transform = CLFTransformPair( @@ -1196,22 +1176,25 @@ def classify_clf_transforms(unclassified_clf_transforms): return vivified_to_dict(classified_clf_transforms) -def unclassify_clf_transforms(classified_clf_transforms): +TypeUnclassifiedCLFTransforms = list[CLFTransform] + + +def unclassify_clf_transforms( + classified_clf_transforms: TypeClassifiedCLFTransforms, +) -> TypeUnclassifiedCLFTransforms: """ - Unclassifie given *CLF* transforms. + Unclassify given *CLF* transforms. Parameters ---------- - classified_clf_transforms : dict + classified_clf_transforms Classified *CLF* transforms as returned by :func:`opencolorio_config_aces.classify_clf_transforms` definition. Returns ------- - list - .. math:: - - \\left[CLFTransform_1, \\ldots, CLFTransform_n\\right] + :class:`list` + Unclassified *CLF* transforms. Examples -------- @@ -1240,28 +1223,30 @@ def unclassify_clf_transforms(classified_clf_transforms): return unclassified_clf_transforms -def filter_clf_transforms(clf_transforms, filterers=None): +def filter_clf_transforms( + clf_transforms: TypeClassifiedCLFTransforms + | TypeUnclassifiedCLFTransforms, + filterers: Sequence[Callable] | None = None, +) -> list[CLFTransform]: """ Filter given *CLF* transforms with given filterers. Parameters ---------- - clf_transforms : dict or list + clf_transforms *CLF* transforms as returned by :func:`opencolorio_config_aces.classify_clf_transforms` or :func:`opencolorio_config_aces.unclassify_clf_transforms` definitions. - filterers : array_like, optional + filterers List of callables used to filter the *CLF* transforms, each callable takes a *CLF* transform as argument and returns whether to include or exclude the *CLF* transform as a bool. Returns ------- - list - .. math:: - - \\left[CLFTransform_1, \\ldots, CLFTransform_n\\right] + :class:`list` + Filtered *CLF* transforms. Warnings -------- @@ -1299,7 +1284,7 @@ def filter_clf_transforms(clf_transforms, filterers=None): return filtered_clf_transforms -def print_clf_taxonomy(): +def print_clf_taxonomy() -> None: """ Print the *builtins* *CLF* taxonomy: @@ -1329,24 +1314,33 @@ def print_clf_taxonomy(): clf_transform.source, clf_transform.target, ) - logger.info( - '\t\tCLFtransformID : "%s"', - clf_transform.clf_transform_id.clf_transform_id, - ) + if clf_transform.clf_transform_id is not None: + logger.info( + '\t\tCLFtransformID : "%s"', + clf_transform.clf_transform_id.clf_transform_id, + ) elif isinstance(clf_transform, CLFTransformPair): logger.info( '\t\t"%s" <--> "%s"', clf_transform.forward_transform.source, clf_transform.forward_transform.target, ) - logger.info( - '\t\tACEStransformID : "%s"', - clf_transform.forward_transform.clf_transform_id.clf_transform_id, - ) - logger.info( - '\t\tACEStransformID : "%s"', - clf_transform.inverse_transform.clf_transform_id.clf_transform_id, - ) + if ( + clf_transform.forward_transform.clf_transform_id + is not None + ): + logger.info( + '\t\tACEStransformID : "%s"', + clf_transform.forward_transform.clf_transform_id.clf_transform_id, + ) + if ( + clf_transform.inverse_transform.clf_transform_id + is not None + ): + logger.info( + '\t\tACEStransformID : "%s"', + clf_transform.inverse_transform.clf_transform_id.clf_transform_id, + ) if __name__ == "__main__": diff --git a/opencolorio_config_aces/config/cg/generate/config.py b/opencolorio_config_aces/config/cg/generate/config.py index a81e94a6..282ba009 100644 --- a/opencolorio_config_aces/config/cg/generate/config.py +++ b/opencolorio_config_aces/config/cg/generate/config.py @@ -975,7 +975,7 @@ def yield_from_config_mapping(): logger.info("Implicit transforms: %s.", implicit_transforms) - def implicit_filterer(transform): + def implicit_transform_filterer(transform): """Return whether given transform is an implicit transform.""" return transform.get("name") in implicit_transforms @@ -1003,20 +1003,23 @@ def multi_filters(array, filterers): return filtered - colorspace_filterers = [implicit_filterer, transform_filterer] + colorspace_filterers = [implicit_transform_filterer, transform_filterer] data.colorspaces = multi_filters(data.colorspaces, colorspace_filterers) logger.info( 'Filtered "Colorspace" transforms: %s.', [a["name"] for a in data.colorspaces], ) - look_filterers = [implicit_filterer, transform_filterer] + look_filterers = [implicit_transform_filterer, transform_filterer] data.looks = multi_filters(data.looks, look_filterers) logger.info( 'Filtered "Look" transforms: %s ', [a["name"] for a in data.looks] ) - view_transform_filterers = [implicit_filterer, transform_filterer] + view_transform_filterers = [ + implicit_transform_filterer, + transform_filterer, + ] data.view_transforms = multi_filters( data.view_transforms, view_transform_filterers ) @@ -1030,8 +1033,8 @@ def multi_filters(array, filterers): a["name"] for a in data.colorspaces if a.get("family") == "Display" ] - def implicit_filterer(transform): - """Return whether given transform is an implicit transform.""" + def implicit_view_filterer(transform): + """Return whether given transform is an implicit view.""" return all( [ @@ -1052,14 +1055,14 @@ def view_filterer(transform): return False - shared_view_filterers = [implicit_filterer, view_filterer] + shared_view_filterers = [implicit_view_filterer, view_filterer] data.shared_views = multi_filters(data.shared_views, shared_view_filterers) logger.info( 'Filtered shared "View(s)": %s.', [a["view"] for a in data.shared_views], ) - view_filterers = [implicit_filterer, view_filterer] + view_filterers = [implicit_view_filterer, view_filterer] data.views = multi_filters(data.views, view_filterers) logger.info('Filtered "View(s)": %s.', [a["view"] for a in data.views]) diff --git a/opencolorio_config_aces/config/generation/common.py b/opencolorio_config_aces/config/generation/common.py index 3ba58861..565f22d9 100644 --- a/opencolorio_config_aces/config/generation/common.py +++ b/opencolorio_config_aces/config/generation/common.py @@ -18,7 +18,6 @@ from collections.abc import Mapping from dataclasses import asdict, dataclass, field from semver import Version -from typing import Union from opencolorio_config_aces.utilities import required from opencolorio_config_aces.config.generation import ( @@ -132,19 +131,19 @@ class ConfigData: description: str = ( 'An "OpenColorIO" config generated by "OpenColorIO-Config-ACES".' ) - search_path: Union[list] = field(default_factory=list) - roles: Union[dict] = field(default_factory=dict) - colorspaces: Union[list] = field(default_factory=list) - named_transforms: Union[list] = field(default_factory=list) - view_transforms: Union[list] = field(default_factory=list) - looks: Union[list] = field(default_factory=list) - shared_views: Union[list] = field(default_factory=list) - views: Union[list] = field(default_factory=list) - active_displays: Union[list] = field(default_factory=list) - active_views: Union[list] = field(default_factory=list) - file_rules: Union[list] = field(default_factory=list) - viewing_rules: Union[list] = field(default_factory=list) - inactive_colorspaces: Union[list] = field(default_factory=list) + search_path: list = field(default_factory=list) + roles: dict = field(default_factory=dict) + colorspaces: list = field(default_factory=list) + named_transforms: list = field(default_factory=list) + view_transforms: list = field(default_factory=list) + looks: list = field(default_factory=list) + shared_views: list = field(default_factory=list) + views: list = field(default_factory=list) + active_displays: list = field(default_factory=list) + active_views: list = field(default_factory=list) + file_rules: list = field(default_factory=list) + viewing_rules: list = field(default_factory=list) + inactive_colorspaces: list = field(default_factory=list) default_view_transform: str = field(default_factory=str) @@ -470,7 +469,7 @@ def generate_config(data, config_name=None, validate=True, base_config=None): **{"name": "Utility - Raw", "family": "Utility", "is_data": True} ) - _red_cdl_transform = ocio.CDLTransform() + _red_cdl_transform = ocio.CDLTransform() # pyright: ignore _red_cdl_transform.setSlope([0, 0, 0]) _red_cdl_transform.setOffset([1, 0, 0]) look_1 = look_factory("Look - Red", forward_transform=_red_cdl_transform) @@ -482,7 +481,7 @@ def generate_config(data, config_name=None, validate=True, base_config=None): "offset": [0, 1, 0], }, } - _gain_cdl_transform = ocio.CDLTransform() + _gain_cdl_transform = ocio.CDLTransform() # pyright: ignore _gain_cdl_transform.setSlope([0.5, 0.5, 0.5]) look_3 = { "name": "Look - Quarter Blue", @@ -505,7 +504,7 @@ def generate_config(data, config_name=None, validate=True, base_config=None): } data = ConfigData( - roles={ocio.ROLE_SCENE_LINEAR: "Gamut - sRGB"}, + roles={ocio.ROLE_SCENE_LINEAR: "Gamut - sRGB"}, # pyright: ignore colorspaces=[ colorspace_1, colorspace_2, @@ -679,8 +678,8 @@ def generate_config(data, config_name=None, validate=True, base_config=None): roles={ "aces_interchange": "ACES - ACES2065-1", "cie_xyz_d65_interchange": "CIE-XYZ D65", - ocio.ROLE_DEFAULT: "ACES - ACES2065-1", - ocio.ROLE_SCENE_LINEAR: colorspace_2["name"], + ocio.ROLE_DEFAULT: "ACES - ACES2065-1", # pyright: ignore + ocio.ROLE_SCENE_LINEAR: colorspace_2["name"], # pyright: ignore }, colorspaces=[ colorspace_1, diff --git a/opencolorio_config_aces/config/generation/factories.py b/opencolorio_config_aces/config/generation/factories.py index 4daffb77..f0c4c3f5 100644 --- a/opencolorio_config_aces/config/generation/factories.py +++ b/opencolorio_config_aces/config/generation/factories.py @@ -53,7 +53,7 @@ BUILTIN_TRANSFORMS = DocstringDict( { builtin: PROFILE_VERSION_DEFAULT - for builtin in ocio.BuiltinTransformRegistry() + for builtin in ocio.BuiltinTransformRegistry() # pyright: ignore } ) BUILTIN_TRANSFORMS.__doc__ = """ diff --git a/opencolorio_config_aces/utilities/common.py b/opencolorio_config_aces/utilities/common.py index ecb7ee1f..26340bc2 100644 --- a/opencolorio_config_aces/utilities/common.py +++ b/opencolorio_config_aces/utilities/common.py @@ -68,7 +68,7 @@ def get(self, key, default=None): pass -PrettyPrinter._dispatch = _dispatch() +PrettyPrinter._dispatch = _dispatch() # pyright: ignore ROOT_BUILD_DEFAULT = (Path(__file__) / ".." / ".." / ".." / "build").resolve() diff --git a/pyproject.toml b/pyproject.toml index caec615d..3342417f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,6 +39,7 @@ flynt = "*" invoke = "*" pre-commit = "*" pydata-sphinx-theme = "*" +pyright = "*" pytest = "*" pytest-cov = "*" restructuredtext-lint = "*" @@ -65,6 +66,16 @@ exclude = ''' [tool.flynt] line_length=999 +[tool.pyright] +reportMissingImports = false +reportMissingModuleSource = false +reportUnboundVariable = false +reportUnnecessaryCast = true +reportUnnecessaryTypeIgnoreComment = true +reportUnsupportedDunderAll = false +reportUnusedExpression = false +exclude = ["**/aces-dev"] + [tool.mypy] plugins = "numpy.typing.mypy_plugin" ignore_missing_imports = true @@ -132,6 +143,7 @@ ignore = [ "N813", "N815", "N816", + "PGH003", "PIE804", "PLE0605", "PLR0912", diff --git a/requirements.txt b/requirements.txt index 77429655..d95090d8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -47,6 +47,7 @@ pre-commit==3.3.2 ; python_version >= "3.9" and python_version < "3.12" pycparser==2.21 ; python_version >= "3.9" and python_version < "3.12" and sys_platform == "linux" pydata-sphinx-theme==0.13.3 ; python_version >= "3.9" and python_version < "3.12" pygments==2.15.1 ; python_version >= "3.9" and python_version < "3.12" +pyright==1.1.331 ; python_version >= "3.9" and python_version < "3.12" pytest-cov==4.1.0 ; python_version >= "3.9" and python_version < "3.12" pytest==7.3.1 ; python_version >= "3.9" and python_version < "3.12" pywin32-ctypes==0.2.0 ; python_version >= "3.9" and python_version < "3.12" and sys_platform == "win32" diff --git a/tasks.py b/tasks.py index 7d2b8909..e61dd8e1 100644 --- a/tasks.py +++ b/tasks.py @@ -9,6 +9,7 @@ import os +import contextlib import requests from invoke.exceptions import Failure from pathlib import Path @@ -27,10 +28,10 @@ import inspect if not hasattr(inspect, "getargspec"): - inspect.getargspec = inspect.getfullargspec + inspect.getargspec = inspect.getfullargspec # pyright: ignore -from invoke import Context, task -import contextlib +from invoke.tasks import task +from invoke.context import Context __author__ = "OpenColorIO Contributors" __copyright__ = "Copyright Contributors to the OpenColorIO Project." @@ -48,6 +49,7 @@ "ORG", "CONTAINER", "clean", + "quality", "precommit", "tests", "preflight", @@ -140,6 +142,28 @@ def clean( ctx.run(f"rm -rf {pattern}") +@task +def quality( + ctx: Context, + pyright: bool = True, +): + """ + Check the codebase with *Pyright* and lints various *restructuredText* + files with *rst-lint*. + + Parameters + ---------- + ctx + Context. + pyright + Whether to check the codebase with *Pyright*. + """ + + if pyright: + message_box('Checking codebase with "Pyright"...') + ctx.run("pyright --skipunannotated --level warning") + + @task def precommit(ctx: Context): """ @@ -176,7 +200,7 @@ def tests(ctx: Context): ) -@task(precommit, tests) +@task(quality, precommit, tests) def preflight(ctx: Context): # noqa: ARG001 """ Perform the preflight tasks, i.e. *formatting* and *quality*.