diff --git a/.gitignore b/.gitignore index 138bb48..9c42ef2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,24 @@ +# Common Files *.egg-info *.pyc *.pyo .DS_Store .coverage* -.idea +uv.lock + +# Common Directories +.fleet/ +.idea/ +.ipynb_checkpoints/ +.python-version +.vs/ +.vscode/ +.sandbox/ +build/ +dist/ +docs/_build/ +docs/generated/ +node_modules/ +references/ + __pycache__ -poetry.lock diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b527df6..3cd27a3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,19 +1,47 @@ repos: -- repo: https://github.com/ikamensh/flynt/ - rev: '1.0.1' + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: "v5.0.0" hooks: - - id: flynt -- repo: https://github.com/charliermarsh/ruff-pre-commit - rev: 'v0.0.285' + - id: check-added-large-files + - id: check-case-conflict + - id: check-merge-conflict + - id: check-symlinks + - id: check-yaml + - id: debug-statements + - id: end-of-file-fixer + - id: mixed-line-ending + - id: name-tests-test + args: ["--pytest-test-first"] + - id: requirements-txt-fixer + - id: trailing-whitespace + - repo: https://github.com/codespell-project/codespell + rev: v2.3.0 hooks: - - id: ruff -- repo: https://github.com/psf/black - rev: 23.7.0 + - id: codespell + args: ["--ignore-words-list=socio-economic"] + exclude: "CONTRIBUTORS.rst" + - repo: https://github.com/PyCQA/isort + rev: "5.13.2" hooks: - - id: black - language_version: python3.9 -- repo: https://github.com/keewis/blackdoc - rev: v0.3.8 + - id: isort + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: "v0.8.2" hooks: - - id: blackdoc - language_version: python3.9 + - id: ruff-format + - id: ruff + args: [--fix] + - repo: https://github.com/adamchainz/blacken-docs + rev: 1.19.1 + hooks: + - id: blacken-docs + language_version: python3.10 + - repo: https://github.com/pre-commit/mirrors-prettier + rev: "v4.0.0-alpha.8" + hooks: + - id: prettier + - repo: https://github.com/pre-commit/pygrep-hooks + rev: "v1.10.0" + hooks: + - id: rst-backticks + - id: rst-directive-colons + - id: rst-inline-touching-normal diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 67ab8e5..58ec1de 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -8,23 +8,22 @@ In the interest of fostering an open and welcoming environment, we as contributo Examples of behavior that contributes to creating a positive environment include: -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members Examples of unacceptable behavior by participants include: -* The use of sexualized language or imagery and unwelcome sexual attention or advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others’ private information, such as a physical or electronic address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a professional setting +- The use of sexualized language or imagery and unwelcome sexual attention or advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others’ private information, such as a physical or electronic address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities - Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. @@ -35,7 +34,6 @@ This Code of Conduct applies within all project spaces, and it also applies when ## Enforcement - Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting Thomas Mansencal and Michael Mauderer via email at thomas@colour-science.org and michael@colour-science.org respectively. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project’s leadership. @@ -46,6 +44,5 @@ This Code of Conduct is adapted from the Contributor Covenant, version 1.4, avai For answers to common questions about this code of conduct, see [https://www.contributor-covenant.org/faq][faq]. - [homepage]: https://www.contributor-covenant.org/version/1/4/code-of-conduct.html -[faq]: https://www.contributor-covenant.org/faq \ No newline at end of file +[faq]: https://www.contributor-covenant.org/faq diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index dcb47a4..bb84d24 100644 --- a/CONTRIBUTORS.rst +++ b/CONTRIBUTORS.rst @@ -4,10 +4,10 @@ Contributors Colour - Dash ------------- -- **Thomas Mansencal**, *Technology Supervisor @ Wētā FX* +- **Thomas Mansencal**, *Principal Pipeline Programmer @ Epic Games* Project coordination, writing. - + About ----- diff --git a/README.rst b/README.rst index e150ce7..19b6819 100644 --- a/README.rst +++ b/README.rst @@ -53,7 +53,8 @@ The *Colour Developers* can be reached via different means: - `Email `__ - `Facebook `__ - `Gitter `__ -- `Twitter `__ +- `X `__ +- `Bluesky `__ About ----- diff --git a/app.py b/app.py index 4242c0a..c1472e0 100644 --- a/app.py +++ b/app.py @@ -3,11 +3,10 @@ =========== """ -import dash import os -from colour.hints import Optional -from flask import Flask +import dash +from flask import Flask __author__ = "Colour Developers" __copyright__ = "Copyright 2018 Colour Developers" @@ -20,10 +19,8 @@ __major_version__ = "0" __minor_version__ = "2" -__change_version__ = "7" -__version__ = ".".join( - (__major_version__, __minor_version__, __change_version__) -) +__change_version__ = "8" +__version__ = f"{__major_version__}.{__minor_version__}.{__change_version__}" __all__ = ["SERVER", "SERVER_URL", "APP"] @@ -32,16 +29,16 @@ *Flask* server hosting the *Dash* app. """ -SERVER_URL: Optional[str] = os.environ.get("COLOUR_DASH_SERVER") +SERVER_URL: str | None = os.environ.get("COLOUR_DASH_SERVER") """ Server url used to construct permanent links for the individual apps. """ APP: dash.Dash = dash.Dash( __application_name__, - external_scripts=os.environ.get("COLOUR_DASH_JS", "").split(","), - external_stylesheets=os.environ.get("COLOUR_DASH_CSS", "").split(","), - server=SERVER, # pyright: ignore + external_scripts=os.environ.get("COLOUR_DASH_JS", "").split(","), # pyright: ignore + external_stylesheets=os.environ.get("COLOUR_DASH_CSS", "").split(","), # pyright: ignore + server=SERVER, ) """ *Dash* app. diff --git a/apps/common.py b/apps/common.py index 47d3757..214e385 100644 --- a/apps/common.py +++ b/apps/common.py @@ -3,15 +3,21 @@ ====== """ +from __future__ import annotations + +import typing from io import StringIO + from colour.adaptation import CHROMATIC_ADAPTATION_TRANSFORMS from colour.colorimetry import CCS_ILLUMINANTS + +if typing.TYPE_CHECKING: + from colour.hints import ArrayLike, Dict, Iterable, List, NDArrayFloat + from colour.io import LUTOperatorMatrix, write_LUT_SonySPImtx from colour.models import RGB_COLOURSPACES from colour.utilities import as_float_array -from colour.hints import ArrayLike, Dict, Iterable, List - __author__ = "Colour Developers" __copyright__ = "Copyright 2018 Colour Developers" __license__ = "BSD-3-Clause - https://opensource.org/licenses/BSD-3-Clause" @@ -50,9 +56,7 @@ OPTIONS_ILLUMINANTS: List[Dict] = [ {"label": key, "value": key} - for key in sorted( - CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"].keys() - ) + for key in sorted(CCS_ILLUMINANTS["CIE 1931 2 Degree Standard Observer"].keys()) ] """ *CIE 1931 2 Degree Standard Observer* illuminant options for a @@ -69,9 +73,7 @@ selected true xpos 0 ypos 0 -}}"""[ - 1: -] +}}"""[1:] """ *The Foundry Nuke* *ColorMatrix* node template. """ @@ -79,7 +81,7 @@ def nuke_format_matrix(M: ArrayLike, decimals: int = 10) -> str: """ - Format given matrix for usage in *The Foundry Nuke*, i.e. *TCL* code for + Format given matrix for usage in *The Foundry Nuke*, i.e., *TCL* code for a *ColorMatrix* node. Parameters @@ -129,7 +131,9 @@ def spimtx_format_matrix(M: ArrayLike, decimals: int = 10) -> str: string = StringIO() write_LUT_SonySPImtx( - LUTOperatorMatrix(M), string, decimals # pyright: ignore + LUTOperatorMatrix(M), + string, # pyright: ignore + decimals, ) return string.getvalue() @@ -151,15 +155,13 @@ def spimtx_format_matrix(M: ArrayLike, decimals: int = 10) -> str: name: Linear {input_colourspace} to Linear {output_colourspace} children: - ! {{matrix: {matrix}}} -"""[ - 1: -] +"""[1:] """ *OpenColorIO* *ColorSpace* template. """ -def matrix_3x3_to_4x4(M): +def matrix_3x3_to_4x4(M: ArrayLike) -> NDArrayFloat: """ Convert given 3x3 matrix :math:`M` to a raveled 4x4 matrix. diff --git a/apps/rgb_colourspace_chromatically_adapted_primaries.py b/apps/rgb_colourspace_chromatically_adapted_primaries.py index be72d2b..65127d4 100644 --- a/apps/rgb_colourspace_chromatically_adapted_primaries.py +++ b/apps/rgb_colourspace_chromatically_adapted_primaries.py @@ -3,17 +3,19 @@ =========================================================== """ +from __future__ import annotations + import sys import urllib.parse from contextlib import suppress -from dash.dcc import Dropdown, Link, Location, Markdown, Slider -from dash.dependencies import Input, Output -from dash.html import A, Button, Code, Div, H3, H5, Li, Pre, Ul from urllib.parse import parse_qs, urlencode, urlparse from colour.colorimetry import CCS_ILLUMINANTS from colour.models import RGB_COLOURSPACES, chromatically_adapted_primaries from colour.utilities import numpy_print_options +from dash.dcc import Dropdown, Link, Location, Markdown, Slider +from dash.dependencies import Input, Output +from dash.html import H3, H5, A, Button, Code, Div, Li, Pre, Ul from app import APP, SERVER_URL from apps.common import ( @@ -48,7 +50,7 @@ APP_PATH: str = f"/apps/{__name__.split('.')[-1]}" """ -App path, i.e. app url. +App path, i.e., app url. """ APP_DESCRIPTION: str = ( @@ -67,7 +69,7 @@ """ -def _uid(id_): +def _uid(id_: str) -> str: """ Generate a unique id for given id by appending the application *UID*. """ @@ -78,9 +80,9 @@ def _uid(id_): STATE_DEFAULT = { "colourspace": OPTIONS_RGB_COLOURSPACE[0]["value"], "illuminant": OPTIONS_ILLUMINANTS[0]["value"], - "chromatic_adaptation_transform": OPTIONS_CHROMATIC_ADAPTATION_TRANSFORM[ - 0 - ]["value"], + "chromatic_adaptation_transform": OPTIONS_CHROMATIC_ADAPTATION_TRANSFORM[0][ + "value" + ], "formatter": "str", "decimals": 10, } @@ -118,9 +120,7 @@ def _uid(id_): Dropdown( id=_uid("chromatic-adaptation-transform"), options=OPTIONS_CHROMATIC_ADAPTATION_TRANSFORM, - value=STATE_DEFAULT[ - "chromatic_adaptation_transform" - ], + value=STATE_DEFAULT["chromatic_adaptation_transform"], clearable=False, className="app-widget", ), @@ -208,16 +208,14 @@ def _uid(id_): className="row", ) """ -App layout, i.e. :class:`Div` class instance. +App layout, i.e., :class:`Div` class instance. LAYOUT : Div """ @APP.callback( - Output( - component_id=_uid("primaries-output"), component_property="children" - ), + Output(component_id=_uid("primaries-output"), component_property="children"), [ Input(_uid("colourspace"), "value"), Input(_uid("illuminant"), "value"), @@ -317,7 +315,7 @@ def value_from_query(value: str) -> str: return STATE_DEFAULT[value.replace("-", "_")] - state = ( + return ( value_from_query("colourspace"), value_from_query("illuminant"), value_from_query("chromatic-adaptation-transform"), @@ -325,8 +323,6 @@ def value_from_query(value: str) -> str: int(value_from_query("decimals")), ) - return state - @APP.callback( Output(_uid("url"), "search"), diff --git a/apps/rgb_colourspace_transformation_matrix.py b/apps/rgb_colourspace_transformation_matrix.py index d86cc50..e573362 100644 --- a/apps/rgb_colourspace_transformation_matrix.py +++ b/apps/rgb_colourspace_transformation_matrix.py @@ -3,17 +3,19 @@ ================================================= """ -import urllib.parse +from __future__ import annotations + import re import sys +import urllib.parse from contextlib import suppress -from dash.dcc import Dropdown, Location, Link, Markdown, Slider -from dash.dependencies import Input, Output -from dash.html import A, Button, Code, Div, H3, H5, Li, Pre, Ul from urllib.parse import parse_qs, urlencode, urlparse from colour.models import RGB_COLOURSPACES, matrix_RGB_to_RGB from colour.utilities import numpy_print_options +from dash.dcc import Dropdown, Link, Location, Markdown, Slider +from dash.dependencies import Input, Output +from dash.html import H3, H5, A, Button, Code, Div, Li, Pre, Ul from app import APP, SERVER_URL from apps.common import ( @@ -52,7 +54,7 @@ APP_PATH: str = f"/apps/{__name__.split('.')[-1]}" """ -App path, i.e. app url. +App path, i.e., app url. """ APP_DESCRIPTION: str = ( @@ -71,7 +73,7 @@ """ -def _uid(id_): +def _uid(id_: str) -> str: """ Generate a unique id for given id by appending the application *UID*. """ @@ -82,9 +84,9 @@ def _uid(id_): STATE_DEFAULT = { "input_colourspace": OPTIONS_RGB_COLOURSPACE[0]["value"], "output_colourspace": OPTIONS_RGB_COLOURSPACE[0]["value"], - "chromatic_adaptation_transform": OPTIONS_CHROMATIC_ADAPTATION_TRANSFORM[ - 0 - ]["value"], + "chromatic_adaptation_transform": OPTIONS_CHROMATIC_ADAPTATION_TRANSFORM[0][ + "value" + ], "formatter": "str", "decimals": 10, } @@ -125,9 +127,7 @@ def _uid(id_): *OPTIONS_CHROMATIC_ADAPTATION_TRANSFORM, {"label": "None", "value": "None"}, ], - value=STATE_DEFAULT[ - "chromatic_adaptation_transform" - ], + value=STATE_DEFAULT["chromatic_adaptation_transform"], clearable=False, className="app-widget", ), @@ -223,7 +223,7 @@ def _uid(id_): className="row", ) """ -App layout, i.e. :class:`Div` class instance. +App layout, i.e., :class:`Div` class instance. LAYOUT : Div """ @@ -303,8 +303,7 @@ def slugify(string: str) -> str: pattern = r"\(|\)" string = re.sub(pattern, "", string) pattern = r"\s-\s|\s|-|\.|/" - string = re.sub(pattern, "_", string) - return string + return re.sub(pattern, "_", string) M_f = TEMPLATE_NUKE_NODE_COLORMATRIX.format( name=( @@ -375,7 +374,7 @@ def value_from_query(value: str) -> str: return STATE_DEFAULT[value.replace("-", "_")] - state = ( + return ( value_from_query("input-colourspace"), value_from_query("output-colourspace"), value_from_query("chromatic-adaptation-transform"), @@ -383,8 +382,6 @@ def value_from_query(value: str) -> str: int(value_from_query("decimals")), ) - return state - @APP.callback( Output(_uid("url"), "search"), diff --git a/index.py b/index.py index b9454de..ca9635d 100644 --- a/index.py +++ b/index.py @@ -3,13 +3,19 @@ ===== """ -import dash -from dash.dependencies import Input, Output +from __future__ import annotations + +import typing + +if typing.TYPE_CHECKING: + import dash + from dash.dcc import Link, Location, Markdown -from dash.html import A, Div, H3, P +from dash.dependencies import Input, Output +from dash.html import H3, A, Div, P -import apps.rgb_colourspace_transformation_matrix as app_1 import apps.rgb_colourspace_chromatically_adapted_primaries as app_2 +import apps.rgb_colourspace_transformation_matrix as app_1 from app import APP, SERVER # noqa: F401 __author__ = "Colour Developers" @@ -25,7 +31,7 @@ @APP.callback(Output("apps", "children"), [Input("url", "pathname")]) -def load_app(app: dash.Dash): +def load_app(app: dash.Dash) -> Div: """ Load given app into the appropriate :class:`Div` class instance. @@ -42,50 +48,51 @@ def load_app(app: dash.Dash): if app == app_1.APP_PATH: return app_1.LAYOUT - elif app == app_2.APP_PATH: + + if app == app_2.APP_PATH: return app_2.LAYOUT - else: - return Div( - [ - P( - [ - "Various colour science ", - A( - "Dash", - href="https://dash.plot.ly/", - target="_blank", - ), - " apps built on top of \n", - A( - "Colour", - href="https://github.com/colour-science/colour", - target="_blank", - ), - ".", - ] - ), - H3( - [ - Link( - app_1.APP_NAME, - href=app_1.APP_PATH, - className="app-link", - ) - ] - ), - Markdown(app_1.APP_DESCRIPTION.replace("This app c", "C")), - H3( - [ - Link( - app_2.APP_NAME, - href=app_2.APP_PATH, - className="app-link", - ) - ] - ), - Markdown(app_2.APP_DESCRIPTION.replace("This app c", "C")), - ] - ) + + return Div( + [ + P( + [ + "Various colour science ", + A( + "Dash", + href="https://dash.plot.ly/", + target="_blank", + ), + " apps built on top of \n", + A( + "Colour", + href="https://github.com/colour-science/colour", + target="_blank", + ), + ".", + ] + ), + H3( + [ + Link( + app_1.APP_NAME, + href=app_1.APP_PATH, + className="app-link", + ) + ] + ), + Markdown(app_1.APP_DESCRIPTION.replace("This app c", "C")), + H3( + [ + Link( + app_2.APP_NAME, + href=app_2.APP_PATH, + className="app-link", + ) + ] + ), + Markdown(app_2.APP_DESCRIPTION.replace("This app c", "C")), + ] + ) if __name__ == "__main__": diff --git a/pyproject.toml b/pyproject.toml index f08165e..0cf30f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,13 +1,16 @@ -[tool.poetry] +[project] name = "colour-dash" -version = "0.2.7" +version = "0.2.8" description = "Various colour science Dash apps built on top of Colour" -license = "BSD-3-Clause" -authors = [ "Colour Developers " ] -maintainers = [ "Colour Developers " ] -readme = 'README.rst' -repository = "https://github.com/colour-science/colour-dash" -homepage = "https://www.colour-science.org/" +readme = "README.rst" +requires-python = ">=3.10,<3.14" +authors = [ + { name = "Colour Developers", email = "colour-developers@colour-science.org" }, +] +maintainers = [ + { name = "Colour Developers", email = "colour-developers@colour-science.org" } +] +license = { text = "BSD-3-Clause" } keywords = [ "color", "color-science", @@ -37,47 +40,50 @@ classifiers = [ "Topic :: Scientific/Engineering", "Topic :: Software Development" ] +dependencies = [ + "colour-science>=0.4.6", + "dash", + "dash-renderer", + "gunicorn", + "imageio>=2,<3", + "numpy>=1.24,<3", + "plotly", + "scipy>=1.10,<2", + "typing-extensions>=4,<5", +] -[tool.poetry.dependencies] -python = ">= 3.9, < 3.12" -colour-science = ">= 0.4.3" -imageio = ">= 2, < 3" -numpy = ">= 1.22, < 2" -scipy = ">= 1.8, < 2" -dash = "*" -dash-renderer = "*" -gunicorn = "*" -plotly = "*" +[tool.uv.sources] +colour-science = { git = "https://github.com/colour-science/colour", branch = "develop" } -[tool.poetry.group.dev.dependencies] -black = "*" -blackdoc = "*" -coverage = "!= 6.3" -coveralls = "*" -flynt = "*" -invoke = "*" -jupyter = "*" -pre-commit = "*" -pyright = "*" -pytest = "*" -pytest-cov = "*" -pytest-xdist = "*" -ruff = "*" -toml = "*" -twine = "*" +[project.urls] +Homepage = "https://www.colour-science.org" +Repository = "https://github.com/colour-science/colour-dash" +Issues = "https://github.com/colour-science/colour-dash/issues" +Changelog = "https://github.com/colour-science/colour-dash/releases" -[tool.black] -line-length = 79 -exclude = ''' -/( - \.git - | build - | dist -)/ -''' +[tool.uv] +dev-dependencies = [ + "coverage", + "coveralls", + "invoke", + "pre-commit", + "pyright", + "toml", + "twine", +] + +[tool.codespell] +ignore-words-list = "socio-economic" +skip = "BIBLIOGRAPHY.bib,CONTRIBUTORS.rst,*.ipynb" -[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 +split_on_trailing_comma = true +use_parentheses = true [tool.pyright] reportMissingImports = false @@ -89,97 +95,58 @@ reportUnsupportedDunderAll = false reportUnusedExpression = false [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"] "docs/*" = ["INP"] "app.py" = ["INP"] "index.py" = ["INP"] "setup.py" = ["INP"] "tasks.py" = ["INP"] -[build-system] -requires = [ "poetry_core>=1.0.0" ] -build-backend = "poetry.core.masonry.api" +[tool.ruff.format] +docstring-code-format = true diff --git a/requirements.txt b/requirements.txt index d1c5981..86b1a3e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,168 +1,75 @@ -ansi2html==1.8.0 ; python_version >= "3.9" and python_version < "3.12" -anyio==4.0.0 ; python_version >= "3.9" and python_version < "3.12" -appnope==0.1.3 ; python_version >= "3.9" and python_version < "3.12" and (platform_system == "Darwin" or sys_platform == "darwin") -argon2-cffi-bindings==21.2.0 ; python_version >= "3.9" and python_version < "3.12" -argon2-cffi==23.1.0 ; python_version >= "3.9" and python_version < "3.12" -arrow==1.2.3 ; python_version >= "3.9" and python_version < "3.12" -astor==0.8.1 ; python_version >= "3.9" and python_version < "3.12" -asttokens==2.3.0 ; python_version >= "3.9" and python_version < "3.12" -async-lru==2.0.4 ; python_version >= "3.9" and python_version < "3.12" -attrs==23.1.0 ; python_version >= "3.9" and python_version < "3.12" -babel==2.12.1 ; python_version >= "3.9" and python_version < "3.12" -backcall==0.2.0 ; python_version >= "3.9" and python_version < "3.12" -beautifulsoup4==4.12.2 ; python_version >= "3.9" and python_version < "3.12" -black==23.7.0 ; python_version >= "3.9" and python_version < "3.12" -blackdoc==0.3.8 ; python_version >= "3.9" and python_version < "3.12" -bleach==6.0.0 ; python_version >= "3.9" and python_version < "3.12" -certifi==2023.7.22 ; python_version >= "3.9" and python_version < "3.12" -cffi==1.15.1 ; python_version >= "3.9" and python_version < "3.12" -cfgv==3.4.0 ; python_version >= "3.9" and python_version < "3.12" -charset-normalizer==3.2.0 ; python_version >= "3.9" and python_version < "3.12" -click==8.1.7 ; python_version >= "3.9" and python_version < "3.12" -colorama==0.4.6 ; python_version >= "3.9" and python_version < "3.12" and (sys_platform == "win32" or platform_system == "Windows") -colour-science==0.4.3 ; python_version >= "3.9" and python_version < "3.12" -comm==0.1.4 ; python_version >= "3.9" and python_version < "3.12" -coverage==6.5.0 ; python_version >= "3.9" and python_version < "3.12" -coverage[toml]==6.5.0 ; python_version >= "3.9" and python_version < "3.12" -coveralls==3.3.1 ; python_version >= "3.9" and python_version < "3.12" -cryptography==41.0.3 ; python_version >= "3.9" and python_version < "3.12" and sys_platform == "linux" -dash-core-components==2.0.0 ; python_version >= "3.9" and python_version < "3.12" -dash-html-components==2.0.0 ; python_version >= "3.9" and python_version < "3.12" -dash-renderer==1.9.1 ; python_version >= "3.9" and python_version < "3.12" -dash-table==5.0.0 ; python_version >= "3.9" and python_version < "3.12" -dash==2.13.0 ; python_version >= "3.9" and python_version < "3.12" -debugpy==1.6.7.post1 ; python_version >= "3.9" and python_version < "3.12" -decorator==5.1.1 ; python_version >= "3.9" and python_version < "3.12" -defusedxml==0.7.1 ; python_version >= "3.9" and python_version < "3.12" -distlib==0.3.7 ; python_version >= "3.9" and python_version < "3.12" -docopt==0.6.2 ; python_version >= "3.9" and python_version < "3.12" -docutils==0.20.1 ; python_version >= "3.9" and python_version < "3.12" -exceptiongroup==1.1.3 ; python_version >= "3.9" and python_version < "3.11" -execnet==2.0.2 ; python_version >= "3.9" and python_version < "3.12" -executing==1.2.0 ; python_version >= "3.9" and python_version < "3.12" -fastjsonschema==2.18.0 ; python_version >= "3.9" and python_version < "3.12" -filelock==3.12.3 ; python_version >= "3.9" and python_version < "3.12" -flask==2.2.5 ; python_version >= "3.9" and python_version < "3.12" -flynt==1.0.1 ; python_version >= "3.9" and python_version < "3.12" -fqdn==1.5.1 ; python_version >= "3.9" and python_version < "3.12" -gunicorn==21.2.0 ; python_version >= "3.9" and python_version < "3.12" -identify==2.5.27 ; python_version >= "3.9" and python_version < "3.12" -idna==3.4 ; python_version >= "3.9" and python_version < "3.12" -imageio==2.31.2 ; python_version >= "3.9" and python_version < "3.12" -importlib-metadata==6.8.0 ; python_version >= "3.9" and python_version < "3.12" -iniconfig==2.0.0 ; python_version >= "3.9" and python_version < "3.12" -invoke==2.2.0 ; python_version >= "3.9" and python_version < "3.12" -ipykernel==6.25.1 ; python_version >= "3.9" and python_version < "3.12" -ipython-genutils==0.2.0 ; python_version >= "3.9" and python_version < "3.12" -ipython==8.15.0 ; python_version >= "3.9" and python_version < "3.12" -ipywidgets==8.1.0 ; python_version >= "3.9" and python_version < "3.12" -isoduration==20.11.0 ; python_version >= "3.9" and python_version < "3.12" -itsdangerous==2.1.2 ; python_version >= "3.9" and python_version < "3.12" -jaraco-classes==3.3.0 ; python_version >= "3.9" and python_version < "3.12" -jedi==0.19.0 ; python_version >= "3.9" and python_version < "3.12" -jeepney==0.8.0 ; python_version >= "3.9" and python_version < "3.12" and sys_platform == "linux" -jinja2==3.1.2 ; python_version >= "3.9" and python_version < "3.12" -json5==0.9.14 ; python_version >= "3.9" and python_version < "3.12" -jsonpointer==2.4 ; python_version >= "3.9" and python_version < "3.12" -jsonschema-specifications==2023.7.1 ; python_version >= "3.9" and python_version < "3.12" -jsonschema==4.19.0 ; python_version >= "3.9" and python_version < "3.12" -jsonschema[format-nongpl]==4.19.0 ; python_version >= "3.9" and python_version < "3.12" -jupyter-client==8.3.1 ; python_version >= "3.9" and python_version < "3.12" -jupyter-console==6.6.3 ; python_version >= "3.9" and python_version < "3.12" -jupyter-core==5.3.1 ; python_version >= "3.9" and python_version < "3.12" -jupyter-events==0.7.0 ; python_version >= "3.9" and python_version < "3.12" -jupyter-lsp==2.2.0 ; python_version >= "3.9" and python_version < "3.12" -jupyter-server-terminals==0.4.4 ; python_version >= "3.9" and python_version < "3.12" -jupyter-server==2.7.3 ; python_version >= "3.9" and python_version < "3.12" -jupyter==1.0.0 ; python_version >= "3.9" and python_version < "3.12" -jupyterlab-pygments==0.2.2 ; python_version >= "3.9" and python_version < "3.12" -jupyterlab-server==2.24.0 ; python_version >= "3.9" and python_version < "3.12" -jupyterlab-widgets==3.0.8 ; python_version >= "3.9" and python_version < "3.12" -jupyterlab==4.0.5 ; python_version >= "3.9" and python_version < "3.12" -keyring==24.2.0 ; python_version >= "3.9" and python_version < "3.12" -markdown-it-py==3.0.0 ; python_version >= "3.9" and python_version < "3.12" -markupsafe==2.1.3 ; python_version >= "3.9" and python_version < "3.12" -matplotlib-inline==0.1.6 ; python_version >= "3.9" and python_version < "3.12" -mdurl==0.1.2 ; python_version >= "3.9" and python_version < "3.12" -mistune==3.0.1 ; python_version >= "3.9" and python_version < "3.12" -more-itertools==10.1.0 ; python_version >= "3.9" and python_version < "3.12" -mypy-extensions==1.0.0 ; python_version >= "3.9" and python_version < "3.12" -nbclient==0.8.0 ; python_version >= "3.9" and python_version < "3.12" -nbconvert==7.8.0 ; python_version >= "3.9" and python_version < "3.12" -nbformat==5.9.2 ; python_version >= "3.9" and python_version < "3.12" -nest-asyncio==1.5.7 ; python_version >= "3.9" and python_version < "3.12" -nodeenv==1.8.0 ; python_version >= "3.9" and python_version < "3.12" -notebook-shim==0.2.3 ; python_version >= "3.9" and python_version < "3.12" -notebook==7.0.3 ; python_version >= "3.9" and python_version < "3.12" -numpy==1.25.2 ; python_version >= "3.9" and python_version < "3.12" -overrides==7.4.0 ; python_version >= "3.9" and python_version < "3.12" -packaging==23.1 ; python_version >= "3.9" and python_version < "3.12" -pandocfilters==1.5.0 ; python_version >= "3.9" and python_version < "3.12" -parso==0.8.3 ; python_version >= "3.9" and python_version < "3.12" -pathspec==0.11.2 ; python_version >= "3.9" and python_version < "3.12" -pexpect==4.8.0 ; python_version >= "3.9" and python_version < "3.12" and sys_platform != "win32" -pickleshare==0.7.5 ; python_version >= "3.9" and python_version < "3.12" -pillow==10.0.0 ; python_version >= "3.9" and python_version < "3.12" -pkginfo==1.9.6 ; python_version >= "3.9" and python_version < "3.12" -platformdirs==3.10.0 ; python_version >= "3.9" and python_version < "3.12" -plotly==5.16.1 ; python_version >= "3.9" and python_version < "3.12" -pluggy==1.3.0 ; python_version >= "3.9" and python_version < "3.12" -pre-commit==3.4.0 ; python_version >= "3.9" and python_version < "3.12" -prometheus-client==0.17.1 ; python_version >= "3.9" and python_version < "3.12" -prompt-toolkit==3.0.39 ; python_version >= "3.9" and python_version < "3.12" -psutil==5.9.5 ; python_version >= "3.9" and python_version < "3.12" -ptyprocess==0.7.0 ; python_version >= "3.9" and python_version < "3.12" and (sys_platform != "win32" or os_name != "nt") -pure-eval==0.2.2 ; python_version >= "3.9" and python_version < "3.12" -pycparser==2.21 ; python_version >= "3.9" and python_version < "3.12" -pygments==2.16.1 ; python_version >= "3.9" and python_version < "3.12" -pyright==1.1.325 ; python_version >= "3.9" and python_version < "3.12" -pytest-cov==4.1.0 ; python_version >= "3.9" and python_version < "3.12" -pytest-xdist==3.3.1 ; python_version >= "3.9" and python_version < "3.12" -pytest==7.4.1 ; python_version >= "3.9" and python_version < "3.12" -python-dateutil==2.8.2 ; python_version >= "3.9" and python_version < "3.12" -python-json-logger==2.0.7 ; python_version >= "3.9" and python_version < "3.12" -pywin32-ctypes==0.2.2 ; python_version >= "3.9" and python_version < "3.12" and sys_platform == "win32" -pywin32==306 ; sys_platform == "win32" and platform_python_implementation != "PyPy" and python_version >= "3.9" and python_version < "3.12" -pywinpty==2.0.11 ; python_version >= "3.9" and python_version < "3.12" and os_name == "nt" -pyyaml==6.0.1 ; python_version >= "3.9" and python_version < "3.12" -pyzmq==25.1.1 ; python_version >= "3.9" and python_version < "3.12" -qtconsole==5.4.4 ; python_version >= "3.9" and python_version < "3.12" -qtpy==2.4.0 ; python_version >= "3.9" and python_version < "3.12" -readme-renderer==41.0 ; python_version >= "3.9" and python_version < "3.12" -referencing==0.30.2 ; python_version >= "3.9" and python_version < "3.12" -requests-toolbelt==1.0.0 ; python_version >= "3.9" and python_version < "3.12" -requests==2.31.0 ; python_version >= "3.9" and python_version < "3.12" -retrying==1.3.4 ; python_version >= "3.9" and python_version < "3.12" -rfc3339-validator==0.1.4 ; python_version >= "3.9" and python_version < "3.12" -rfc3986-validator==0.1.1 ; python_version >= "3.9" and python_version < "3.12" -rfc3986==2.0.0 ; python_version >= "3.9" and python_version < "3.12" -rich==13.5.2 ; python_version >= "3.9" and python_version < "3.12" -rpds-py==0.10.0 ; python_version >= "3.9" and python_version < "3.12" -ruff==0.0.287 ; python_version >= "3.9" and python_version < "3.12" -scipy==1.11.2 ; python_version >= "3.9" and python_version < "3.12" -secretstorage==3.3.3 ; python_version >= "3.9" and python_version < "3.12" and sys_platform == "linux" -send2trash==1.8.2 ; python_version >= "3.9" and python_version < "3.12" -setuptools==68.1.2 ; python_version >= "3.9" and python_version < "3.12" -six==1.16.0 ; python_version >= "3.9" and python_version < "3.12" -sniffio==1.3.0 ; python_version >= "3.9" and python_version < "3.12" -soupsieve==2.5 ; python_version >= "3.9" and python_version < "3.12" -stack-data==0.6.2 ; python_version >= "3.9" and python_version < "3.12" -tenacity==8.2.3 ; python_version >= "3.9" and python_version < "3.12" -terminado==0.17.1 ; python_version >= "3.9" and python_version < "3.12" -tinycss2==1.2.1 ; python_version >= "3.9" and python_version < "3.12" -toml==0.10.2 ; python_version >= "3.9" and python_version < "3.12" -tomli==2.0.1 ; python_version >= "3.9" and python_version < "3.12" -tornado==6.3.3 ; python_version >= "3.9" and python_version < "3.12" -traitlets==5.9.0 ; python_version >= "3.9" and python_version < "3.12" -twine==4.0.2 ; python_version >= "3.9" and python_version < "3.12" -typing-extensions==4.7.1 ; python_version >= "3.9" and python_version < "3.12" -uri-template==1.3.0 ; python_version >= "3.9" and python_version < "3.12" -urllib3==2.0.4 ; python_version >= "3.9" and python_version < "3.12" -virtualenv==20.24.4 ; python_version >= "3.9" and python_version < "3.12" -wcwidth==0.2.6 ; python_version >= "3.9" and python_version < "3.12" -webcolors==1.13 ; python_version >= "3.9" and python_version < "3.12" -webencodings==0.5.1 ; python_version >= "3.9" and python_version < "3.12" -websocket-client==1.6.2 ; python_version >= "3.9" and python_version < "3.12" -werkzeug==2.2.3 ; python_version >= "3.9" and python_version < "3.12" -widgetsnbextension==4.0.8 ; python_version >= "3.9" and python_version < "3.12" -zipp==3.16.2 ; python_version >= "3.9" and python_version < "3.12" +# This file was autogenerated by uv via the following command: +# uv export --no-hashes --all-extras +backports-tarfile==1.2.0 ; python_full_version < '3.12' and platform_machine != 'ppc64le' and platform_machine != 's390x' +blinker==1.9.0 +certifi==2024.12.14 +cffi==1.17.1 ; platform_machine != 'ppc64le' and platform_machine != 's390x' and platform_python_implementation != 'PyPy' and sys_platform == 'linux' +cfgv==3.4.0 +charset-normalizer==3.4.0 +click==8.1.8 +colorama==0.4.6 ; platform_system == 'Windows' +colour-science @ git+https://github.com/colour-science/colour@9c00f804798f70aa6f71476f2d5fa8b4b355bca1 +coverage==7.6.9 +coveralls==4.0.1 +cryptography==44.0.0 ; platform_machine != 'ppc64le' and platform_machine != 's390x' and sys_platform == 'linux' +dash==2.18.2 +dash-core-components==2.0.0 +dash-html-components==2.0.0 +dash-renderer==1.9.1 +dash-table==5.0.0 +distlib==0.3.9 +docopt==0.6.2 +docutils==0.21.2 +filelock==3.16.1 +flask==3.0.3 +gunicorn==23.0.0 +identify==2.6.3 +idna==3.10 +imageio==2.36.1 +importlib-metadata==8.5.0 +invoke==2.2.0 +itsdangerous==2.2.0 +jaraco-classes==3.4.0 ; platform_machine != 'ppc64le' and platform_machine != 's390x' +jaraco-context==6.0.1 ; platform_machine != 'ppc64le' and platform_machine != 's390x' +jaraco-functools==4.1.0 ; platform_machine != 'ppc64le' and platform_machine != 's390x' +jeepney==0.8.0 ; platform_machine != 'ppc64le' and platform_machine != 's390x' and sys_platform == 'linux' +jinja2==3.1.5 +keyring==25.5.0 ; platform_machine != 'ppc64le' and platform_machine != 's390x' +markdown-it-py==3.0.0 +markupsafe==3.0.2 +mdurl==0.1.2 +more-itertools==10.5.0 ; platform_machine != 'ppc64le' and platform_machine != 's390x' +nest-asyncio==1.6.0 +nh3==0.2.20 +nodeenv==1.9.1 +numpy==2.2.1 +packaging==24.2 +pillow==11.0.0 +pkginfo==1.12.0 +platformdirs==4.3.6 +plotly==5.24.1 +pre-commit==4.0.1 +pycparser==2.22 ; platform_machine != 'ppc64le' and platform_machine != 's390x' and platform_python_implementation != 'PyPy' and sys_platform == 'linux' +pygments==2.18.0 +pyright==1.1.391 +pywin32-ctypes==0.2.3 ; platform_machine != 'ppc64le' and platform_machine != 's390x' and sys_platform == 'win32' +pyyaml==6.0.2 +readme-renderer==44.0 +requests==2.32.3 +requests-toolbelt==1.0.0 +retrying==1.3.4 +rfc3986==2.0.0 +rich==13.9.4 +scipy==1.14.1 +secretstorage==3.3.3 ; platform_machine != 'ppc64le' and platform_machine != 's390x' and sys_platform == 'linux' +setuptools==75.6.0 +six==1.17.0 +tenacity==9.0.0 +toml==0.10.2 +tomli==2.2.1 ; python_full_version <= '3.11' +twine==6.0.1 +typing-extensions==4.12.2 +urllib3==2.3.0 +virtualenv==20.28.0 +werkzeug==3.0.6 +zipp==3.21.0 diff --git a/tasks.py b/tasks.py index 93206d1..71b5612 100644 --- a/tasks.py +++ b/tasks.py @@ -3,21 +3,25 @@ ============== """ +from __future__ import annotations + import contextlib +import inspect import platform -from invoke.exceptions import Failure +import typing from colour.utilities import message_box +from invoke.exceptions import Failure import app -import inspect - if not hasattr(inspect, "getargspec"): inspect.getargspec = inspect.getfullargspec # pyright: ignore +if typing.TYPE_CHECKING: + from invoke.context import Context + from invoke.tasks import task -from invoke.context import Context __author__ = "Colour Developers" __copyright__ = "Copyright 2018 Colour Developers" @@ -48,14 +52,14 @@ @task -def clean(ctx: Context, bytecode: bool = False): +def clean(ctx: Context, bytecode: bool = False) -> None: """ Clean the project. Parameters ---------- bytecode : bool, optional - Whether to clean the bytecode files, e.g. *.pyc* files. + Whether to clean the bytecode files, e.g., *.pyc* files. """ message_box("Cleaning project...") @@ -74,7 +78,7 @@ def clean(ctx: Context, bytecode: bool = False): def quality( ctx: Context, pyright: bool = True, -): +) -> None: """ Check the codebase with *Pyright*. @@ -92,7 +96,7 @@ def quality( @task -def precommit(ctx: Context): +def precommit(ctx: Context) -> None: """ Run the "pre-commit" hooks on the codebase. @@ -107,7 +111,7 @@ def precommit(ctx: Context): @task -def requirements(ctx: Context): +def requirements(ctx: Context) -> None: """ Export the *requirements.txt* file. @@ -118,16 +122,11 @@ def requirements(ctx: Context): """ message_box('Exporting "requirements.txt" file...') - ctx.run( - "poetry export -f requirements.txt " - "--without-hashes " - "--with dev " - "--output requirements.txt" - ) + ctx.run('uv export --no-hashes --all-extras | grep -v "-e \\." > requirements.txt') @task(requirements) -def docker_build(ctx: Context): +def docker_build(ctx: Context) -> None: """ Build the *docker* image. @@ -149,7 +148,7 @@ def docker_build(ctx: Context): @task -def docker_remove(ctx: Context): +def docker_remove(ctx: Context) -> None: """ Stop and remove the *docker* container. @@ -169,7 +168,7 @@ def docker_remove(ctx: Context): @task(docker_remove, docker_build) -def docker_run(ctx: Context): +def docker_run(ctx: Context) -> None: """ Run the *docker* container. @@ -195,7 +194,7 @@ def docker_run(ctx: Context): @task(clean, quality, precommit, docker_run) -def docker_push(ctx: Context): +def docker_push(ctx: Context) -> None: """ Push the *docker* container.