diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | setup.cfg | 20 | ||||
-rwxr-xr-x | setup.py | 3 | ||||
-rwxr-xr-x | tests/__init__.py | 2 | ||||
-rwxr-xr-x | tests/test_angles.py | 4 | ||||
-rwxr-xr-x | tests/test_builtin.py | 4 | ||||
-rwxr-xr-x | tests/test_colors.py | 30 | ||||
-rwxr-xr-x | tests/test_decoders.py | 57 | ||||
-rwxr-xr-x | thcolor/__init__.py | 8 | ||||
-rw-r--r-- | thcolor/angles.py | 78 | ||||
-rw-r--r-- | thcolor/builtin.py | 108 | ||||
-rw-r--r-- | thcolor/colors.py | 343 | ||||
-rw-r--r-- | thcolor/decoders.py | 212 | ||||
-rwxr-xr-x | thcolor/errors.py | 28 | ||||
-rw-r--r-- | thcolor/utils.py | 14 | ||||
-rwxr-xr-x | thcolor/version.py | 2 |
16 files changed, 448 insertions, 467 deletions
@@ -3,6 +3,8 @@ __pycache__ /*.egg-info /dist /.spyproject +/.tool-versions +/.python-version /*.kdev4 /build /docs/_build @@ -3,7 +3,7 @@ name = thcolor version = attr: thcolor.version.version url = https://thcolor.touhey.pro/ project_urls = - Documentation = https://thcolor.touhey.pro/docs/ + Documentation = https://thcolor.touhey.pro/docs/ author = Thomas Touhey author_email = thomas@touhey.fr description = color management module @@ -11,13 +11,13 @@ long_description = file: README.rst keywords = textout, color, parser, css license = MIT classifiers = - Development Status :: 4 - Beta - Natural Language :: English - License :: OSI Approved :: MIT License - Operating System :: OS Independent - Programming Language :: Python :: 3 - Intended Audience :: Developers - Topic :: Text Processing :: Markup :: HTML + Development Status :: 4 - Beta + Natural Language :: English + License :: OSI Approved :: MIT License + Operating System :: OS Independent + Programming Language :: Python :: 3 + Intended Audience :: Developers + Topic :: Text Processing :: Markup :: HTML [options] zip_safe = False @@ -35,12 +35,12 @@ source-dir = docs universal = True [flake8] -ignore = D105,D107,D202,D208,D210,D401,T484,T499,W503 +ignore = D105,D107,D202,W503,T499 exclude = docs/conf.py test.py per-file-ignores = - tests/*:F405,S101,D102,D103 + tests/*:F403,F405,S101,D102,D103,T484 rst-roles = py:class py:attr @@ -3,7 +3,7 @@ # Copyright (C) 2019-2022 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr> # This file is part of the thcolor Python module, which is MIT-licensed. # ***************************************************************************** -""" Setup script for the thcolor Python package and script. """ +"""Setup script for the thcolor Python package and script.""" from setuptools import setup as _setup @@ -16,7 +16,6 @@ except ImportError: pass # Actually, most of the project's data is read from the `setup.cfg` file. - _setup(**kwargs) # End of file. diff --git a/tests/__init__.py b/tests/__init__.py index f8d9bfb..9e1e6db 100755 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -3,7 +3,7 @@ # Copyright (C) 2019-2021 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr> # This file is part of the thcolor project, which is MIT-licensed. # ***************************************************************************** -""" Unit tests for the `thcolor` Python module. """ +"""Unit tests for the `thcolor` Python module.""" # This file is only there to indicate that the folder is a module. # It doesn't actually contain code. diff --git a/tests/test_angles.py b/tests/test_angles.py index 6309c96..9438924 100755 --- a/tests/test_angles.py +++ b/tests/test_angles.py @@ -3,7 +3,7 @@ # Copyright (C) 2019-2021 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr> # This file is part of the thcolor project, which is MIT-licensed. # ***************************************************************************** -""" Unit tests for the thcolor color decoding and management module. """ +"""Unit tests for the thcolor color decoding and management module.""" from math import pi @@ -12,7 +12,7 @@ from thcolor.angles import * # NOQA class TestAngles: - """ Test angle definitions and conversions. """ + """Test angle definitions and conversions.""" @pytest.mark.parametrize('fst,snd', ( (GradiansAngle(.1), GradiansAngle(.2)), diff --git a/tests/test_builtin.py b/tests/test_builtin.py index 16a50f8..a43e724 100755 --- a/tests/test_builtin.py +++ b/tests/test_builtin.py @@ -3,7 +3,7 @@ # Copyright (C) 2019-2021 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr> # This file is part of the thcolor project, which is MIT-licensed. # ***************************************************************************** -""" Unit tests for the thcolor color decoding and management module. """ +"""Unit tests for the thcolor color decoding and management module.""" import pytest from thcolor.angles import * # NOQA @@ -14,7 +14,7 @@ from thcolor.errors import * # NOQA class TestDefaultDecoder: - """ Test the default decoder with all features. """ + """Test the default decoder with all features.""" @pytest.fixture def decoder(self): diff --git a/tests/test_colors.py b/tests/test_colors.py index 505a43d..7d0043d 100755 --- a/tests/test_colors.py +++ b/tests/test_colors.py @@ -3,7 +3,7 @@ # Copyright (C) 2019-2021 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr> # This file is part of the thcolor project, which is MIT-licensed. # ***************************************************************************** -""" Unit tests for the thcolor color decoding and management module. """ +"""Unit tests for the thcolor color decoding and management module.""" import pytest from thcolor.angles import * # NOQA @@ -12,7 +12,7 @@ from thcolor.utils import round_half_up class TestSRGBColors: - """ Test the sRGB color conversions. """ + """Test the sRGB color conversions.""" @pytest.mark.parametrize('args', ( (None, 0, 0), @@ -20,7 +20,7 @@ class TestSRGBColors: (0, 0, None), )) def test_invalid_values(self, args): - """ Try to instanciate the class using invalid values. """ + """Try to instanciate the class using invalid values.""" with pytest.raises(ValueError): SRGBColor(*args) @@ -31,7 +31,7 @@ class TestSRGBColors: ((.1, .2, .3), (26, 51, 77)), )) def test_to_bytes(self, args, bytes_): - """ Try converting to bytes and test the rounding up. """ + """Try converting to bytes and test the rounding up.""" assert SRGBColor(*args).asbytes() == bytes_ @@ -39,7 +39,7 @@ class TestSRGBColors: ((0, .003921, .003925), (0, 1, 1)), )) def test_from_bytes(self, args, bytes_): - """ Try converting from bytes and test the rounding up. """ + """Try converting from bytes and test the rounding up.""" assert SRGBColor(*args) == SRGBColor.frombytes(*bytes_) @@ -64,7 +64,7 @@ class TestSRGBColors: ('6db6ec49efd278cd0bc92d1e5e072d680', (110, 205, 224)), )) def test_netscape_decoding(self, name, bytes_): - """ Try decoding colors using Netscape color parsing. """ + """Try decoding colors using Netscape color parsing.""" assert SRGBColor.fromnetscapecolorname(name).asbytes() == bytes_ @@ -147,7 +147,7 @@ class TestSRGBColors: class TestHSLColors: - """ Test the HSL color conversions. """ + """Test the HSL color conversions.""" @pytest.mark.parametrize('args', ( (0, 0, 0), @@ -158,7 +158,7 @@ class TestHSLColors: (DegreesAngle(0), 0, 1.5), )) def test_invalid_values(self, args): - """ Try to instanciate the class using invalid values. """ + """Try to instanciate the class using invalid values.""" with pytest.raises(ValueError): HSLColor(*args) @@ -192,7 +192,7 @@ class TestHSLColors: class TestHSVColor: - """ Test the HSV color conversions. """ + """Test the HSV color conversions.""" @pytest.mark.parametrize('args,rgb', ( ((DegreesAngle(120), 0, 0), (0, 0, 0)), @@ -206,7 +206,7 @@ class TestHSVColor: class TestHWBColors: - """ Test the HWB color conversions. """ + """Test the HWB color conversions.""" @pytest.mark.parametrize('args,rgb', ( ((DegreesAngle(145), .48, .28), (122, 184, 148)), @@ -226,7 +226,7 @@ class TestHWBColors: class TestCMYKColors: - """ Test the CMYK color conversions. """ + """Test the CMYK color conversions.""" @pytest.mark.parametrize('args,rgb', ( ((0, 0, 0, 0), (255, 255, 255)), @@ -241,7 +241,7 @@ class TestCMYKColors: class TestLABColors: - """ Test the LAB color conversions. """ + """Test the LAB color conversions.""" @pytest.mark.parametrize('args,lch', ( # Examples created using the following online converter: @@ -260,7 +260,7 @@ class TestLABColors: class TestLCHColors: - """ Test the LCH color conversions. """ + """Test the LCH color conversions.""" @pytest.mark.parametrize('args,lab', ( # Examples created using the following online converter: @@ -288,7 +288,7 @@ class TestLCHColors: class TestXYZColors: - """ Test the XYZ color conversions. """ + """Test the XYZ color conversions.""" @pytest.mark.parametrize('args', ( (-.5, 0, 0), @@ -302,7 +302,7 @@ class TestXYZColors: (0, 0, None), )) def test_invalid_values(self, args): - """ Try to instanciate the class using invalid values. """ + """Try to instanciate the class using invalid values.""" with pytest.raises(ValueError): XYZColor(*args) diff --git a/tests/test_decoders.py b/tests/test_decoders.py index 785ba32..e3423aa 100755 --- a/tests/test_decoders.py +++ b/tests/test_decoders.py @@ -3,19 +3,20 @@ # Copyright (C) 2019-2021 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr> # This file is part of the thcolor project, which is MIT-licensed. # ***************************************************************************** -""" Unit tests for the thcolor color decoding and management module. """ +"""Unit tests for the thcolor color decoding and management module.""" from typing import Any import pytest -from thcolor.angles import * # NOQA -from thcolor.colors import * # NOQA -from thcolor.decoders import * # NOQA -from thcolor.errors import * # NOQA + +from thcolor.angles import * +from thcolor.colors import * +from thcolor.decoders import * +from thcolor.errors import * class TestInvalidDecoders: - """ Test the exceptions raised when an invalid decoder is created. """ + """Test the exceptions raised when an invalid decoder is created.""" def test_unknown_data_type(self): with pytest.raises(TypeError, match=r'neither an alias'): @@ -52,7 +53,7 @@ class TestInvalidDecoders: class TestBaseDecoder: - """ Test the base decoder with no options enabled. """ + """Test the base decoder with no options enabled.""" @pytest.fixture def decoder(self): @@ -74,9 +75,9 @@ class TestBaseDecoder: )), )) def test_syntax(self, decoder, test_input, expected): - """ Test basic syntax. + """Test basic syntax. - Tests syntaxes that are always available, with separators. + Tests syntaxes that are always available, with separators. """ result = decoder.decode(test_input) @@ -88,47 +89,47 @@ class TestBaseDecoder: )), )) def test_coersce_into_angles(self, decoder, test_input, expected): - """ Test the decoding while indicating that we want angles. """ + """Test the decoding while indicating that we want angles.""" result = decoder.decode(test_input, prefer_angles=True) assert result == expected def test_existing_variable(self, decoder): - """ Test getting an existing variable. """ + """Test getting an existing variable.""" result = decoder.decode('hello') assert result == (56.78,) def test_non_existing_variable(self, decoder): - """ Test getting a non-existing variable. """ + """Test getting a non-existing variable.""" with pytest.raises(ColorExpressionSyntaxError, match=r'unknown value'): decoder.decode('nonexisting') def test_existing_variable_ncol_format(self, decoder): - """ Test decoding an expression using an NCol-like variable. + """Test decoding an expression using an NCol-like variable. - We want to avoid the NCol subsytem from shadowing - existing variables when NCol support is disabled. + We want to avoid the NCol subsytem from shadowing + existing variables when NCol support is disabled. """ result = decoder.decode('B20') assert result == (12.34,) def test_non_existing_variable_ncol_format(self, decoder): - """ Test decoding an expression using an NCol-like variable. + """Test decoding an expression using an NCol-like variable. - We want to avoid the NCol subsystem intercepting errors - in case of non-existing variables. + We want to avoid the NCol subsystem intercepting errors + in case of non-existing variables. """ with pytest.raises(ColorExpressionSyntaxError, match=r'unknown value'): decoder.decode('Y40') def test_disabled_extended_hex(self, decoder): - """ Test decoding an expression using an extended hex color. + """Test decoding an expression using an extended hex color. - This should be disabled in this state. + This should be disabled in this state. """ with pytest.raises(ColorExpressionSyntaxError, match=r'extended hex'): @@ -136,7 +137,7 @@ class TestBaseDecoder: class TestExtendedHexDecoder: - """ Test base decoder with extended hex support. """ + """Test base decoder with extended hex support.""" @pytest.fixture def decoder(self): @@ -154,7 +155,7 @@ class TestExtendedHexDecoder: class TestNColDecoder: - """ Test base decoder with ncol support. """ + """Test base decoder with ncol support.""" @pytest.fixture def decoder(self): @@ -203,7 +204,7 @@ class TestNColDecoder: ), )) def test_ncol(self, decoder, test_input, expected): - """ Test natural colors. """ + """Test natural colors.""" result = decoder.decode(test_input) assert result == (expected,) @@ -214,7 +215,7 @@ class TestNColDecoder: class TestNetscapeDecoder: - """ Test base decoder with netscape color support. """ + """Test base decoder with netscape color support.""" @pytest.fixture def decoder(self): @@ -232,7 +233,7 @@ class TestNetscapeDecoder: ), )) def test_netscape_colors(self, decoder, test_input, expected): - """ Test decoding using Netscape colors. """ + """Test decoding using Netscape colors.""" result = decoder.decode(test_input, prefer_colors=True) assert result == (expected,) @@ -243,10 +244,10 @@ class TestNetscapeDecoder: ('123.0', SRGBColor.frombytes(18, 48, 0, 1.00)), )) def test_coersce_into_colors(self, decoder, test_input, expected): - """ Test the decoding while indicating that we want colors. + """Test the decoding while indicating that we want colors. - It is expected from the decoder to use Netscape color name - style decoding in this scenario. + It is expected from the decoder to use Netscape color name + style decoding in this scenario. """ result = decoder.decode(test_input, prefer_colors=True) diff --git a/thcolor/__init__.py b/thcolor/__init__.py index 8da42a9..4ee1693 100755 --- a/thcolor/__init__.py +++ b/thcolor/__init__.py @@ -3,12 +3,12 @@ # Copyright (C) 2018-2022 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr> # This file is part of the thcolor project, which is MIT-licensed. # ***************************************************************************** -""" HTML/CSS-like color parsing, mainly for the `[color]` tag. +"""HTML/CSS-like color parsing, mainly for the `[color]` tag. - Defines the `get_color()` function which returns an rgba value. +Defines the `get_color()` function which returns an rgba value. - The functions in this module do not aim at being totally compliant with - the W3C standards, although it is inspired from it. +The functions in this module do not aim at being totally compliant with +the W3C standards, although it is inspired from it. """ # End of file. diff --git a/thcolor/angles.py b/thcolor/angles.py index 3f0ca06..23a0c2e 100644 --- a/thcolor/angles.py +++ b/thcolor/angles.py @@ -3,7 +3,7 @@ # Copyright (C) 2019-2022 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr> # This file is part of the thcolor project, which is MIT-licensed. # ***************************************************************************** -""" Angle representation and conversions. """ +"""Angle representation and conversions.""" from math import pi as _pi from typing import Any as _Any, Optional as _Optional @@ -16,9 +16,9 @@ __all__ = [ class Angle: - """ Abstract class representing an angle within thcolor. + """Abstract class representing an angle within thcolor. - Used for some color representations (most notably hue). + Used for some color representations (most notably hue). """ __slots__ = () @@ -50,7 +50,7 @@ class Angle: ) def asdegrees(self) -> 'DegreesAngle': - """ Get the current angle as a degrees angle. """ + """Get the current angle as a degrees angle.""" try: value = self._value @@ -65,7 +65,7 @@ class Angle: return DegreesAngle((value - ob) / (ot - ob) * (nt - nb) + nb) def asgradians(self) -> 'GradiansAngle': - """ Get the current angle as a gradians angle. """ + """Get the current angle as a gradians angle.""" try: value = self._value @@ -80,7 +80,7 @@ class Angle: return GradiansAngle((value - ob) / (ot - ob) * (nt - nb) + nb) def asradians(self) -> 'RadiansAngle': - """ Get the current angle as a radians angle. """ + """Get the current angle as a radians angle.""" try: value = self._value @@ -95,7 +95,7 @@ class Angle: return RadiansAngle((value - ob) / (ot - ob) * (nt - nb) + nb) def asturns(self) -> 'TurnsAngle': - """ Get the current angle as a turns angle. """ + """Get the current angle as a turns angle.""" try: value = self._value @@ -110,7 +110,7 @@ class Angle: return TurnsAngle((value - ob) / (ot - ob) * (nt - nb) + nb) def asprincipal(self): - """ Get the principal angle. """ + """Get the principal angle.""" cls = self.__class__ value = self._value @@ -124,9 +124,9 @@ class Angle: expr: str, decoder: _Optional[_Any] = None, ) -> 'Angle': - """ Create a color from a string. + """Create a color from a string. - :param expr: The expression to decode. + :param expr: The expression to decode. """ if decoder is None: @@ -146,16 +146,16 @@ class Angle: class DegreesAngle(Angle): - """ An angle expressed in degrees. + """An angle expressed in degrees. - A 270° angle can be created the following way: + A 270° angle can be created the following way: - .. code-block:: python + .. code-block:: python - angle = DegreesAngle(270) + angle = DegreesAngle(270) - :param degrees: Degrees; canonical values are between 0 and 360 - excluded. + :param degrees: Degrees, as canonical values are between 0 and 360 + excluded. """ __slots__ = ('_value') @@ -172,22 +172,22 @@ class DegreesAngle(Angle): @property def degrees(self) -> float: - """ Degrees. """ + """Get the degrees.""" return self._value class GradiansAngle(Angle): - """ An angle expressed in gradians. + """An angle expressed in gradians. - A 565.5 gradians angle can be created the following way: + A 565.5 gradians angle can be created the following way: - .. code-block:: python + .. code-block:: python - angle = GradiansAngle(565.5) + angle = GradiansAngle(565.5) - :param gradians: Gradians; canonical values are between - 0 and 400.0 excluded. + :param gradians: Gradians, as canonical values are between + 0 and 400.0 excluded. """ __slots__ = ('_value') @@ -204,23 +204,23 @@ class GradiansAngle(Angle): @property def gradians(self) -> float: - """ Gradians. """ + """Get the gradians.""" return self._value class RadiansAngle(Angle): - """ An angle expressed in radians. + """An angle expressed in radians. - A π radians angle can be created the following way: + A π radians angle can be created the following way: - .. code-block:: python + .. code-block:: python - from math import pi - angle = RadiansAngle(pi) + from math import pi + angle = RadiansAngle(pi) - :param radians: Radians; canonical are between 0 and 2π - excluded. + :param radians: Radians, as canonical are between 0 and 2π + excluded. """ __slots__ = ('_value') @@ -241,22 +241,22 @@ class RadiansAngle(Angle): @property def radians(self) -> float: - """ Radians. """ + """Get the radians.""" return self._value class TurnsAngle(Angle): - """ An angle expressed in turns. + """An angle expressed in turns. - A 3.5 turns angle can be created the following way: + A 3.5 turns angle can be created the following way: - .. code-block:: python + .. code-block:: python - angle = TurnsAngle(3.5) + angle = TurnsAngle(3.5) - :param turns: Turns; canonical values are between 0 and 1 - excluded. + :param turns: Turns, as canonical values are between 0 and 1 + excluded. """ __slots__ = ('_value') @@ -273,7 +273,7 @@ class TurnsAngle(Angle): @property def turns(self) -> float: - """ Turns. """ + """Get the turns.""" return self._value diff --git a/thcolor/builtin.py b/thcolor/builtin.py index 229a746..0179768 100644 --- a/thcolor/builtin.py +++ b/thcolor/builtin.py @@ -3,7 +3,7 @@ # Copyright (C) 2019-2022 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr> # This file is part of the thcolor project, which is MIT-licensed. # ***************************************************************************** -""" Builtin decoders using the base elements. """ +"""Builtin decoders using the base elements.""" from typing import Union as _Union @@ -29,7 +29,7 @@ _number = _Union[int, float] def _rgb(x): - """ Return an RGB color out of the given 6-digit hexadecimal code. """ + """Return an RGB color out of the given 6-digit hexadecimal code.""" from thcolor.colors import SRGBColor as _SRGBColor @@ -46,9 +46,9 @@ def _rgb(x): class CSS1ColorDecoder(_MetaColorDecoder): - """ Named colors from CSS Level 1. + """Named colors from CSS Level 1. - See `<https://www.w3.org/TR/CSS1/>`_ for more information. + See `<https://www.w3.org/TR/CSS1/>`_ for more information. """ __defaults_to_netscape_color__ = True @@ -73,8 +73,9 @@ class CSS1ColorDecoder(_MetaColorDecoder): transparent = _SRGBColor(0, 0, 0, 0) + @staticmethod def rgb(red: int = 0, green: int = 0, blue: int = 0) -> _Color: - """ Make an RGB color out of the given components. """ + """Make an RGB color out of the given components.""" return _SRGBColor( red=_factor(red, max_=255, clip=True), @@ -85,18 +86,18 @@ class CSS1ColorDecoder(_MetaColorDecoder): class CSS2ColorDecoder(CSS1ColorDecoder): - """ Named colors from CSS Level 2 (Revision 1). + """Named colors from CSS Level 2 (Revision 1). - See `<https://www.w3.org/TR/CSS2/>`_ for more information. + See `<https://www.w3.org/TR/CSS2/>`_ for more information. """ orange = _rgb('#ffa500') class CSS3ColorDecoder(CSS2ColorDecoder): - """ Named colors and functions from CSS Color Module Level 3. + """Named colors and functions from CSS Color Module Level 3. - See `<https://drafts.csswg.org/css-color-3/>`_ for more information. + See `<https://drafts.csswg.org/css-color-3/>`_ for more information. """ darkblue = _rgb('#00008B') @@ -230,13 +231,14 @@ class CSS3ColorDecoder(CSS2ColorDecoder): lightyellow = _rgb('#FFFFE0') ivory = _rgb('#FFFFF0') + @staticmethod def rgb( red: _number = 0, green: _number = 0, blue: _number = 0, alpha: _number = 1.0, ) -> _Color: - """ Make an RGB color out of the given components. """ + """Make an RGB color out of the given components.""" return _SRGBColor( red=_factor(red, max_=255, clip=True), @@ -245,13 +247,14 @@ class CSS3ColorDecoder(CSS2ColorDecoder): alpha=_factor(alpha, clip=True), ) + @staticmethod def hsl( hue: _Angle, saturation: _number, lightness: _number, alpha: _number = 1.0, ) -> _Color: - """ Make an HSL color out of the given components. """ + """Make an HSL color out of the given components.""" return _HSLColor( hue=hue, @@ -265,22 +268,23 @@ class CSS3ColorDecoder(CSS2ColorDecoder): class CSS4ColorDecoder(CSS3ColorDecoder): - """ Named colors and functions from CSS Color Module Level 4. + """Named colors and functions from CSS Color Module Level 4. - See `<https://drafts.csswg.org/css-color/>`_ for more information.. + See `<https://drafts.csswg.org/css-color/>`_ for more information.. """ __extended_hex_support__ = True rebeccapurple = _rgb('#663399') + @staticmethod def hwb( hue: _Angle, whiteness: _number = 0.0, blackness: _number = 0.0, alpha: _number = 1.0, ) -> _Color: - """ Make an HWB color out of the given components. """ + """Make an HWB color out of the given components.""" return _HWBColor( hue=hue, @@ -289,19 +293,21 @@ class CSS4ColorDecoder(CSS3ColorDecoder): alpha=_factor(alpha), ) + @staticmethod def gray(gray: _number, alpha: _number = 1.0) -> _Color: - """ Make a gray-scale color out of the given components. """ + """Make a gray-scale color out of the given components.""" gray = _factor(gray, max_=255) return _SRGBColor(gray, gray, gray, _factor(alpha)) + @staticmethod def lab( light: _number, a: _number, b: _number, alpha: _number = 1.0, ) -> _Color: - """ Make an LAB color out of the given components. """ + """Make an LAB color out of the given components.""" return _LABColor( lightness=max(_factor(light), 0.0), @@ -309,16 +315,17 @@ class CSS4ColorDecoder(CSS3ColorDecoder): alpha=_factor(alpha), ) + @staticmethod def lch( light: _number, chroma: _number, hue: _Angle, alpha: _number = 1.0, ) -> _Color: - """ Make an LCH color out of the given components. """ + """Make an LCH color out of the given components.""" return _LCHColor( - light=max(_factor(light), 0.0), + lightness=max(_factor(light), 0.0), chroma=max(chroma, 0.0), hue=hue, alpha=_factor(alpha), @@ -328,7 +335,7 @@ class CSS4ColorDecoder(CSS3ColorDecoder): class DefaultColorDecoder(CSS4ColorDecoder): - """ Functions extending the CSS Color Module Level 4 reference. """ + """Functions extending the CSS Color Module Level 4 reference.""" __ncol_support__ = True @@ -348,6 +355,7 @@ class DefaultColorDecoder(CSS4ColorDecoder): hbwa = _alias('hwb', args=('hue', 'blackness', 'whiteness', 'alpha')) device_cmyk = _alias('cmyk') + @staticmethod def cmyk( cyan: _number, magenta: _number = 0.0, @@ -355,7 +363,7 @@ class DefaultColorDecoder(CSS4ColorDecoder): black: _number = 0.0, alpha: _number = 1.0, ) -> _Color: - """ Make a CMYK color out of the given components. """ + """Make a CMYK color out of the given components.""" return _CMYKColor( cyan=_factor(cyan), @@ -365,13 +373,14 @@ class DefaultColorDecoder(CSS4ColorDecoder): alpha=_factor(alpha), ) + @staticmethod def hsv( hue: _Angle, saturation: _number, value: _number, alpha: _number = 1.0, ) -> _Color: - """ Make an HSV color out of the given components. """ + """Make an HSV color out of the given components.""" return _HSVColor( hue=hue, @@ -380,13 +389,14 @@ class DefaultColorDecoder(CSS4ColorDecoder): alpha=_factor(alpha), ) + @staticmethod def xyz( x: _number, y: _number, z: _number, alpha: _number = 1.0, ) -> _Color: - """ Make an XYZ color out of the given components. """ + """Make an XYZ color out of the given components.""" return _XYZColor( x=_factor(x), @@ -395,13 +405,14 @@ class DefaultColorDecoder(CSS4ColorDecoder): alpha=_factor(alpha), ) + @staticmethod def yiq( y: _number, i: _number, q: _number, alpha: _number = 1.0, ) -> _Color: - """ Make a YIQ color out of the given components. """ + """Make a YIQ color out of the given components.""" return _YIQColor( y=_factor(y), @@ -410,13 +421,14 @@ class DefaultColorDecoder(CSS4ColorDecoder): alpha=_factor(alpha), ) + @staticmethod def yuv( y: _number, u: _number, v: _number, alpha: _number = 1.0, ) -> _Color: - """ Make a YUV color out of the given components. """ + """Make a YUV color out of the given components.""" return _YUVColor( y=_factor(y), @@ -430,62 +442,69 @@ class DefaultColorDecoder(CSS4ColorDecoder): # --- @_fallback(_rgb('#ff0000')) + @staticmethod def red(color: _Color) -> int: - """ Get the red channel value from an RGB color. """ + """Get the red channel value from an RGB color.""" r, g, b, _ = color.assrgb() return r @_fallback(_rgb('#00ff00')) + @staticmethod def green(color: _Color) -> int: - """ Get the green channel value from an RGB color. """ + """Get the green channel value from an RGB color.""" r, g, b, _ = color.assrgb() return g @_fallback(_rgb('#0000ff')) + @staticmethod def blue(color: _Color) -> int: - """ Get the blue channel value from an RGB color. """ + """Get the blue channel value from an RGB color.""" - r, g, b, _ = color.ascolor().assrgb() + r, g, b, _ = color.assrgb() return b # --- # Manage the lightness and saturation for HSL colors. # --- + @staticmethod def darker(by: float, color: _Color) -> _Color: - """ Make the color darker by a given factor. + """Make the color darker by a given factor. - This is accomplished by calling - :py:meth:`thcolor.colors.Color.darker`. + This is accomplished by calling + :py:meth:`thcolor.colors.Color.darker`. """ return color.darker(by) + @staticmethod def lighter(by: float, color: _Color) -> _Color: - """ Make the color lighter by a given factor. + """Make the color lighter by a given factor. - This is accomplished by calling - :py:meth:`thcolor.colors.Color.lighter`. + This is accomplished by calling + :py:meth:`thcolor.colors.Color.lighter`. """ return color.lighter(by) + @staticmethod def desaturate(by: float, color: _Color) -> _Color: - """ Desaturate the color by a given factor. + """Desaturate the color by a given factor. - This is accomplished by calling - :py:meth:`thcolor.colors.Color.desaturate`. + This is accomplished by calling + :py:meth:`thcolor.colors.Color.desaturate`. """ return color.desaturate(by) + @staticmethod def saturate(by: float, color: _Color) -> _Color: - """ Saturate the color by a given factor. + """Saturate the color by a given factor. - This is accomplished by calling - :py:meth:`thcolor.colors.Color.saturate`. + This is accomplished by calling + :py:meth:`thcolor.colors.Color.saturate`. """ return color.saturate(by) @@ -494,12 +513,13 @@ class DefaultColorDecoder(CSS4ColorDecoder): # Others. # --- + @staticmethod def ncol(color: _Color) -> _Color: - """ Return a natural color (NCol). + """Return a natural color (NCol). - This method is actually compatibility with w3color.js. - NCols are managed directly without the function, so - the function only needs to return the color. + This method is actually compatibility with w3color.js. + NCols are managed directly without the function, so + the function only needs to return the color. """ return color diff --git a/thcolor/colors.py b/thcolor/colors.py index 2be1985..cfc9240 100644 --- a/thcolor/colors.py +++ b/thcolor/colors.py @@ -3,7 +3,7 @@ # Copyright (C) 2019-2022 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr> # This file is part of the thcolor project, which is MIT-licensed. # ***************************************************************************** -""" Color representations and conversions. """ +"""Color representations and conversions.""" from math import ( atan2 as _atan2, ceil as _ceil, cos as _cos, sin as _sin, sqrt as _sqrt, @@ -26,9 +26,9 @@ __all__ = [ class Color: - """ Class representing a color within thcolor. + """Class representing a color within thcolor. - :param alpha: Value for :py:attr:`alpha`. + :param alpha: Value for :py:attr:`alpha`. """ __slots__ = ('_alpha') @@ -68,10 +68,10 @@ class Color: @property def alpha(self) -> float: - """ The alpha component value. + """Get the alpha component value. - Represented as a float varying between 0.0 (invisible) - and 1.0 (opaque). + Represented as a float varying between 0.0 (invisible) + and 1.0 (opaque). """ return self._alpha @@ -82,9 +82,9 @@ class Color: expr: str, decoder: _Optional[_Any] = None, ) -> 'Color': - """ Create a color from a string. + """Create a color from a string. - :param expr: The expression to decode. + :param expr: The expression to decode. """ if decoder is None: @@ -107,52 +107,52 @@ class Color: # --- def assrgb(self) -> 'SRGBColor': - """ Get an SRGBColor out of the current object. """ + """Get an SRGBColor out of the current object.""" raise NotImplementedError def ashsl(self) -> 'HSLColor': - """ Get an HSLColor out of the current object. """ + """Get an HSLColor out of the current object.""" return self.assrgb().ashsl() def ashsv(self) -> 'HSVColor': - """ Get an HSVColor out of the current object. """ + """Get an HSVColor out of the current object.""" return self.assrgb().ashsv() def ashwb(self) -> 'HWBColor': - """ Get an HWBColor out of the current object. """ + """Get an HWBColor out of the current object.""" return self.assrgb().ashwb() def ascmyk(self) -> 'CMYKColor': - """ Get a CMYKColor out of the current object. """ + """Get a CMYKColor out of the current object.""" return self.assrgb().ascmyk() def aslab(self) -> 'LABColor': - """ Get a LABColor out of the current object. """ + """Get a LABColor out of the current object.""" raise NotImplementedError def aslch(self) -> 'LCHColor': - """ Get a LCHColor out of the current object. """ + """Get a LCHColor out of the current object.""" raise NotImplementedError def asxyz(self) -> 'XYZColor': - """ Get an XYZColor out of the current object. """ + """Get an XYZColor out of the current object.""" raise NotImplementedError def asyiq(self) -> 'YIQColor': - """ Get an YIQColor out of the current object. """ + """Get an YIQColor out of the current object.""" return self.assrgb().asyiq() def asyuv(self) -> 'YUVColor': - """ Get an YUVColor out of the current object. """ + """Get an YUVColor out of the current object.""" return self.assrgb().asyuv() @@ -161,25 +161,24 @@ class Color: # --- def replace(self, **properties) -> 'Color': - """ Get the color with the given properties replaced. + """Get the color with the given properties replaced. - For changing the alpha on an RGB color: + For changing the alpha on an RGB color: - .. code-block:: python + .. code-block:: python - >>> SRGBColor(.1, .2, .3).replace(alpha=.5) - ... SRGBColor(red=0.1, green=0.2, blue=0.3, alpha=0.5) + >>> SRGBColor(.1, .2, .3).replace(alpha=.5) + ... SRGBColor(red=0.1, green=0.2, blue=0.3, alpha=0.5) - For changing the lightness on an HSL color: + For changing the lightness on an HSL color: - .. code-block:: pycon + .. code-block:: pycon - >>> HSLColor(DegreesAngle(270), .5, 1).replace(lightness=.2) - ... HSLColor(hue=DegreesAngle(degrees=270.0), saturation=0.5, - ... lightness=0.2, alpha=1.0) + >>> HSLColor(DegreesAngle(270), .5, 1).replace(lightness=.2) + ... HSLColor(hue=DegreesAngle(degrees=270.0), saturation=0.5, + ... lightness=0.2, alpha=1.0) - :param properties: Properties to change from the original - color. + :param properties: Properties to change from the original color. """ params = { @@ -199,53 +198,53 @@ class Color: return type(self)(**params) def darker(self, by: float = 0.1) -> 'Color': - """ Get a darker version of the given color. + """Get a darker version of the given color. - :param by: Percentage by which the color should be darker. + :param by: Percentage by which the color should be darker. """ color = self.ashsl() return color.replace(lightness=max(color.lightness - by, 0.0)) def lighter(self, by: float = 0.1) -> 'Color': - """ Get a lighter version of the given color. + """Get a lighter version of the given color. - :param by: Percentage by which the color should be lighter. + :param by: Percentage by which the color should be lighter. """ color = self.ashsl() return color.replace(lightness=min(color.lightness + by, 1.0)) def desaturate(self, by: float = 0.1) -> 'Color': - """ Get a less saturated version of the given color. + """Get a less saturated version of the given color. - :param by: Percentage by which the color should be - desaturated. + :param by: Percentage by which the color should be + desaturated. """ color = self.ashsl() return color.replace(saturation=max(color.saturation - by, 0.0)) def saturate(self, by: float = 0.1) -> 'Color': - """ Get a more saturated version of the given color. + """Get a more saturated version of the given color. - :param by: Percentage by which the color should be - saturated. + :param by: Percentage by which the color should be + saturated. """ color = self.ashsl() return color.replace(saturation=min(color.saturation + by, 1.0)) def css(self) -> _Sequence[str]: - """ Get the CSS color descriptions. + """Get the CSS color descriptions. - Includes older CSS specifications compatibility, - as a sequence of strings. + Includes older CSS specifications compatibility, + as a sequence of strings. - For example: + For example: - >>> SRGBColor.frombytes(18, 52, 86, 0.82).css() - ... ("#123456", "rgba(18, 52, 86, 82%)") + >>> SRGBColor.frombytes(18, 52, 86, 0.82).css() + ... ("#123456", "rgba(18, 52, 86, 82%)") """ def _percent(prop): @@ -261,7 +260,6 @@ class Color: # Start by yelling a #RRGGBB color, compatible with most # web browsers around the world, followed by the rgba() # notation if the alpha value isn't 1.0. - a = _round_half_up(self.alpha, 3) try: @@ -278,7 +276,6 @@ class Color: # Then yield more specific CSS declarations in case # they're supported (which would be neat!). - if isinstance(self, HSLColor): hue, sat, lgt = ( self.hue, self.saturation, self.lightness, @@ -311,12 +308,12 @@ class Color: class SRGBColor(Color): - """ A color expressed using its channel intensities in the sRGB profile. + """A color expressed using its channel intensities in the sRGB profile. - :param red: Value for :py:attr:`red`. - :param green: Value for :py:attr:`green`. - :param blue: Value for :py:attr:`blue`. - :param alpha: Value for :py:attr:`alpha`. + :param red: Value for :py:attr:`red`. + :param green: Value for :py:attr:`green`. + :param blue: Value for :py:attr:`blue`. + :param alpha: Value for :py:attr:`alpha`. """ __slots__ = ('_red', '_green', '_blue') @@ -360,27 +357,27 @@ class SRGBColor(Color): @property def red(self) -> float: - """ The intensity of the red channel. + """Get the intensity of the red channel. - Represented as a float between 0.0 (dark) and 1.0 (light). + Represented as a float between 0.0 (dark) and 1.0 (light). """ return self._red @property def green(self) -> float: - """ The intensity of the green channel. + """Get the intensity of the green channel. - Represented as a float between 0.0 (dark) and 1.0 (light). + Represented as a float between 0.0 (dark) and 1.0 (light). """ return self._green @property def blue(self) -> float: - """ The intensity of the blue channel. + """Get the intensity of the blue channel. - Represented as a float between 0.0 (dark) and 1.0 (light). + Represented as a float between 0.0 (dark) and 1.0 (light). """ return self._blue @@ -393,7 +390,7 @@ class SRGBColor(Color): blue: int, alpha: float = 1.0, ) -> 'SRGBColor': - """ Get an sRGB color from colors using values between 0 and 255. """ + """Get an sRGB color from colors using values between 0 and 255.""" return cls( red=red / 255, @@ -404,7 +401,7 @@ class SRGBColor(Color): @classmethod def fromnetscapecolorname(cls, name: str) -> 'SRGBColor': - """ Get an sRGB color from a Netscape color name. """ + """Get an sRGB color from a Netscape color name.""" name = str(name) if name[0] == '#': @@ -417,7 +414,6 @@ class SRGBColor(Color): # by '0' characters (the 0xFFFF limit is due to how # UTF-16 was managed at the time). # - we truncate our input to 128 characters. - name = name.lower() name = ''.join( c if c in '0123456789abcdef' else '00'[:1 + (ord(c) > 0xFFFF)] @@ -429,7 +425,6 @@ class SRGBColor(Color): # `sz` is the size of the digits slice to take in that zone # (max. 8). # `of` is the offset in the zone of the slice to take. - iv = _ceil(len(name) / 3) of = iv - 8 if iv > 8 else 0 sz = iv - of @@ -437,7 +432,6 @@ class SRGBColor(Color): # Then we isolate the slices using the values calculated # above. `gr` will be an array of 3 or 4 digit strings # (depending on the number of members). - gr = list(map( lambda i: name[i * iv + of:i * iv + iv].ljust(sz, '0'), range(3), @@ -445,18 +439,16 @@ class SRGBColor(Color): # Check how many digits we can skip at the beginning of # each slice. - pre = min(map(lambda x: len(x) - len(x.lstrip('0')), gr)) pre = min(pre, sz - 2) # Then we extract the values. - r, g, b = map(lambda x: int('0' + x[pre:pre + 2], 16), gr) return cls.frombytes(r, g, b) def assrgb(self) -> 'SRGBColor': - """ Get an SRGBColor out of the current object. """ + """Get an SRGBColor out of the current object.""" return SRGBColor( red=self.red, @@ -466,7 +458,7 @@ class SRGBColor(Color): ) def ashsl(self) -> 'HSLColor': - """ Get an HSLColor out of the current object. """ + """Get an HSLColor out of the current object.""" r, g, b = self.red, self.green, self.blue @@ -502,7 +494,7 @@ class SRGBColor(Color): ) def ashsv(self) -> 'HSVColor': - """ Get an HSVColor out of the current object. """ + """Get an HSVColor out of the current object.""" r, g, b = self.red, self.green, self.blue maxc = max(r, g, b) @@ -532,7 +524,7 @@ class SRGBColor(Color): ) def ashwb(self) -> 'HWBColor': - """ Get an HWBColor out of the current object. """ + """Get an HWBColor out of the current object.""" r, g, b = self.red, self.green, self.blue @@ -541,7 +533,7 @@ class SRGBColor(Color): chroma = max_ - min_ if chroma == 0: - hue = 0 + hue = 0. elif r == max_: hue = (g - b) / chroma elif g == max_: @@ -561,7 +553,7 @@ class SRGBColor(Color): ) def ascmyk(self) -> 'CMYKColor': - """ Get a CMYKColor out of the current object. """ + """Get a CMYKColor out of the current object.""" r, g, b, _ = self @@ -582,7 +574,7 @@ class SRGBColor(Color): ) def asyiq(self) -> 'YIQColor': - """ Get an YIQColor out of the current object. """ + """Get an YIQColor out of the current object.""" r, g, b = self.red, self.green, self.blue @@ -594,7 +586,7 @@ class SRGBColor(Color): ) def asbytes(self) -> _Tuple[int, int, int]: - """ Get the red, blue and green bytes. """ + """Get the red, blue and green bytes.""" return ( int(_round_half_up(self.red * 255)), @@ -604,12 +596,12 @@ class SRGBColor(Color): class HSLColor(Color): - """ A color expressed using its hue, saturation and lightness components. + """A color expressed using its hue, saturation and lightness components. - :param hue: Value for :py:attr:`hue`. - :param saturation: Value for :py:attr:`saturation`. - :param lightness: Value for :py:attr:`lightness`. - :param alpha: Value for :py:attr:`alpha`. + :param hue: Value for :py:attr:`hue`. + :param saturation: Value for :py:attr:`saturation`. + :param lightness: Value for :py:attr:`lightness`. + :param alpha: Value for :py:attr:`alpha`. """ __slots__ = ('_hue', '_saturation', '_lightness') @@ -654,26 +646,26 @@ class HSLColor(Color): @property def hue(self) -> _Angle: - """ The hue, as an angle. """ + """Get the hue, as an angle.""" return self._hue @property def saturation(self) -> float: - """ The saturation, between 0.0 and 1.0. """ + """Get the saturation, between 0.0 and 1.0.""" return self._saturation @property def lightness(self) -> float: - """ The lightness, between 0.0 and 1.0. """ + """Get the lightness, between 0.0 and 1.0.""" return self._lightness def assrgb(self) -> 'SRGBColor': - """ Get an SRGBColor out of the current object. """ + """Get an SRGBColor out of the current object.""" - hue, s, lgt = self.hue.asdegrees(), self.saturation, self.lightness + hue_obj, s, lgt = self.hue.asdegrees(), self.saturation, self.lightness if s == 0: # Achromatic color. @@ -696,7 +688,7 @@ class HSLColor(Color): return t1 + (t2 - t1) * (4 - hue) return t1 - hue = (hue.degrees % 360) / 60 + hue = (hue_obj.degrees % 360) / 60 if lgt <= 0.5: t2 = lgt * (s + 1) else: @@ -712,7 +704,7 @@ class HSLColor(Color): ) def ashsl(self) -> 'HSLColor': - """ Get an HSLColor out of the current object. """ + """Get an HSLColor out of the current object.""" return HSLColor( hue=self.hue, @@ -723,12 +715,12 @@ class HSLColor(Color): class HSVColor(Color): - """ A color expressed using its hue, saturation and value components. + """A color expressed using its hue, saturation and value components. - :param hue: Value for :py:attr:`hue`. - :param saturation: Value for :py:attr:`saturation`. - :param value: Value for :py:attr:`value`. - :param alpha: Value for :py:attr:`alpha`. + :param hue: Value for :py:attr:`hue`. + :param saturation: Value for :py:attr:`saturation`. + :param value: Value for :py:attr:`value`. + :param alpha: Value for :py:attr:`alpha`. """ __slots__ = ('_hue', '_saturation', '_value') @@ -752,24 +744,24 @@ class HSVColor(Color): @property def hue(self) -> _Angle: - """ The hue, as an angle. """ + """Get the hue, as an angle.""" return self._hue @property def saturation(self) -> float: - """ The saturation, between 0.0 and 1.0. """ + """Get the saturation, between 0.0 and 1.0.""" return self._saturation @property def value(self) -> float: - """ The value, between 0.0 and 1.0. """ + """Get the value, between 0.0 and 1.0.""" return self._value def assrgb(self) -> 'SRGBColor': - """ Get an SRGBColor out of the current object. """ + """Get an SRGBColor out of the current object.""" hue, saturation, value = ( self.hue.asturns(), @@ -808,7 +800,7 @@ class HSVColor(Color): ) def ashsv(self) -> 'HSVColor': - """ Get an HSVColor out of the current object. """ + """Get an HSVColor out of the current object.""" return HSVColor( hue=self.hue, @@ -819,12 +811,12 @@ class HSVColor(Color): class HWBColor(Color): - """ A color expressed using its hue, whiteness and blackness components. + """A color expressed using its hue, whiteness and blackness components. - :param hue: Value for :py:attr:`hue`. - :param whiteness: Value for :py:attr:`whiteness`. - :param blackness: Value for :py:attr:`blackness`. - :param alpha: Value for :py:attr:`alpha`. + :param hue: Value for :py:attr:`hue`. + :param whiteness: Value for :py:attr:`whiteness`. + :param blackness: Value for :py:attr:`blackness`. + :param alpha: Value for :py:attr:`alpha`. """ __slots__ = ('_hue', '_whiteness', '_blackness') @@ -848,24 +840,24 @@ class HWBColor(Color): @property def hue(self) -> _Angle: - """ The hue, as an angle. """ + """Get the hue, as an angle.""" return self._hue @property def whiteness(self) -> float: - """ The whiteness, as a value between 0.0 and 1.0. """ + """Get the whiteness, as a value between 0.0 and 1.0.""" return self._whiteness @property def blackness(self) -> float: - """ The blackness, as a value between 0.0 and 1.0. """ + """Get the blackness, as a value between 0.0 and 1.0.""" return self._blackness def assrgb(self) -> 'SRGBColor': - """ Get an SRGBColor out of the current object. """ + """Get an SRGBColor out of the current object.""" hue, w, bl = self.hue, self.whiteness, self.blackness @@ -885,7 +877,7 @@ class HWBColor(Color): ) def ashwb(self) -> 'HWBColor': - """ Get an HWBColor out of the current object. """ + """Get an HWBColor out of the current object.""" return HWBColor( hue=self.hue, @@ -896,13 +888,13 @@ class HWBColor(Color): class CMYKColor(Color): - """ A color expressed using its CMYK channels' intensities. + """A color expressed using its CMYK channels' intensities. - :param cyan: Value for :py:attr:`cyan`. - :param magenta: Value for :py:attr:`magenta`. - :param yellow: Value for :py:attr:`yellow`. - :param black: Value for :py:attr:`black`. - :param alpha: Value for :py:attr:`alpha`. + :param cyan: Value for :py:attr:`cyan`. + :param magenta: Value for :py:attr:`magenta`. + :param yellow: Value for :py:attr:`yellow`. + :param black: Value for :py:attr:`black`. + :param alpha: Value for :py:attr:`alpha`. """ __slots__ = ('_cyan', '_magenta', '_yellow', '_black') @@ -934,30 +926,30 @@ class CMYKColor(Color): @property def cyan(self): - """ Cyan channel intensity between 0.0 and 1.0. """ + """Get the cyan channel intensity between 0.0 and 1.0.""" return self._cyan @property def magenta(self): - """ Magenta channel intensity between 0.0 and 1.0. """ + """Get the magenta channel intensity between 0.0 and 1.0.""" return self._magenta @property def yellow(self): - """ Yellow channel intensity between 0.0 and 1.0. """ + """Get the yellow channel intensity between 0.0 and 1.0.""" return self._yellow @property def black(self): - """ Black channel intensity between 0.0 and 1.0. """ + """Get the black channel intensity between 0.0 and 1.0.""" return self._black def assrgb(self) -> 'SRGBColor': - """ Get an SRGBColor out of the current object. """ + """Get an SRGBColor out of the current object.""" c, m, y, k = self.cyan, self.magenta, self.yellow, self.black @@ -973,7 +965,7 @@ class CMYKColor(Color): ) def ascmyk(self) -> 'CMYKColor': - """ Get a CMYKColor out of the current object. """ + """Get a CMYKColor out of the current object.""" return CMYKColor( cyan=self.cyan, @@ -985,12 +977,12 @@ class CMYKColor(Color): class LABColor(Color): - """ A color expressed using its CIELAB color space cartesian coordinates. + """A color expressed using its CIELAB color space cartesian coordinates. - :param lightness: Value for :py:attr:`lightness`. - :param a: Value for :py:attr:`a`. - :param b: Value for :py:attr:`b`. - :param alpha: Value for :py:attr:`alpha`. + :param lightness: Value for :py:attr:`lightness`. + :param a: Value for :py:attr:`a`. + :param b: Value for :py:attr:`b`. + :param alpha: Value for :py:attr:`alpha`. """ __slots__ = ('_lightness', '_a', '_b') @@ -1014,33 +1006,33 @@ class LABColor(Color): @property def lightness(self) -> float: - """ The CIE lightness. + """Get the CIE lightness. - Similar to the lightness in the HSL representation. - Represented as a float between 0.0 and 1.0. + Similar to the lightness in the HSL representation. + Represented as a float between 0.0 and 1.0. """ return self._lightness @property def a(self) -> float: - """ The A axis value in the Lab colorspace. """ + """Get the A axis value in the Lab colorspace.""" return self._a @property def b(self) -> float: - """ The B axis value in the Lab colorspace. """ + """Get the B axis value in the Lab colorspace.""" return self._b def assrgb(self) -> 'SRGBColor': - """ Get an SRGBColor out of the current object. """ + """Get an SRGBColor out of the current object.""" return self.asxyz().assrgb() def aslab(self) -> 'LABColor': - """ Get a LABColor out of the current object. """ + """Get a LABColor out of the current object.""" return LABColor( lightness=self.lightness, @@ -1050,7 +1042,7 @@ class LABColor(Color): ) def aslch(self) -> 'LCHColor': - """ Get a LCHColor out of the current object. """ + """Get a LCHColor out of the current object.""" l, a, b = self.lightness, self.a, self.b @@ -1063,12 +1055,12 @@ class LABColor(Color): class LCHColor(Color): - """ A color expressed using its CIELAB color space polar coordinates. + """A color expressed using its CIELAB color space polar coordinates. - :param lightness: Value for :py:attr:`lightness`. - :param chroma: Value for :py:attr:`chroma`. - :param hue: Value for :py:attr:`hue`. - :param alpha: Value for :py:attr:`alpha`. + :param lightness: Value for :py:attr:`lightness`. + :param chroma: Value for :py:attr:`chroma`. + :param hue: Value for :py:attr:`hue`. + :param alpha: Value for :py:attr:`alpha`. """ __slots__ = ('_lightness', '_chroma', '_hue', '_alpha') @@ -1111,36 +1103,36 @@ class LCHColor(Color): @property def lightness(self) -> float: - """ The CIE lightness. + """Get the CIE lightness. - Similar to the lightness in the HSL representation. - Represented as a float between 0.0 and 1.0. + Similar to the lightness in the HSL representation. + Represented as a float between 0.0 and 1.0. """ return self._lightness @property def chroma(self) -> float: - """ The chroma. + """Get the chroma. - Represented as a positive number theoretically unbounded. + Represented as a positive number theoretically unbounded. """ return self._chroma @property def hue(self) -> _Angle: - """ The hue, as an angle. """ + """Get the hue, as an angle.""" return self._hue def assrgb(self) -> 'SRGBColor': - """ Get an SRGBColor out of the current object. """ + """Get an SRGBColor out of the current object.""" return self.aslab().asxyz().assrgb() def aslab(self) -> 'LABColor': - """ Get a LABColor out of the current object. """ + """Get a LABColor out of the current object.""" l, c, h = self.lightness, self.chroma, self.hue.asradians() @@ -1152,7 +1144,7 @@ class LCHColor(Color): ) def aslch(self) -> 'LCHColor': - """ Get a LCHColor out of the current object. """ + """Get a LCHColor out of the current object.""" return LCHColor( lightness=self.lightness, @@ -1163,12 +1155,12 @@ class LCHColor(Color): class XYZColor(Color): - """ A color expressed using its CIEXYZ color space coordinates. + """A color expressed using its CIEXYZ color space coordinates. - :param x: Value for :py:attr:`x`. - :param y: Value for :py:attr:`y`. - :param z: Value for :py:attr:`z`. - :param alpha: Value for :py:attr:`alpha`. + :param x: Value for :py:attr:`x`. + :param y: Value for :py:attr:`y`. + :param z: Value for :py:attr:`z`. + :param alpha: Value for :py:attr:`alpha`. """ __slots__ = ('_x', '_y', '_z') @@ -1210,24 +1202,24 @@ class XYZColor(Color): @property def x(self) -> float: - """ The CIE X component, between 0.0 and 1.0. """ + """Get the CIE X component, between 0.0 and 1.0.""" return self._x @property def y(self) -> float: - """ The CIE Y component, between 0.0 and 1.0. """ + """Get the CIE Y component, between 0.0 and 1.0.""" return self._y @property def z(self) -> float: - """ The CIE Z component, between 0.0 and 1.0. """ + """Get the CIE Z component, between 0.0 and 1.0.""" return self._z def assrgb(self) -> 'SRGBColor': - """ Get an SRGBColor out of the current object. """ + """Get an SRGBColor out of the current object.""" # For more information about this algorithm, see these links: # @@ -1258,7 +1250,7 @@ class XYZColor(Color): ) def aslab(self) -> 'LABColor': - """ Get a LABColor out of the current object. """ + """Get a LABColor out of the current object.""" x, y, z = self.x, self.y, self.z @@ -1288,7 +1280,7 @@ class XYZColor(Color): ) def asxyz(self) -> 'XYZColor': - """ Get an XYZColor out of the current object. """ + """Get an XYZColor out of the current object.""" return XYZColor( x=self.x, @@ -1299,12 +1291,12 @@ class XYZColor(Color): class YIQColor(Color): - """ A color expressed using its YIQ components. + """A color expressed using its YIQ components. - :param y: Value for :py:attr:`y`. - :param i: Value for :py:attr:`i`. - :param q: Value for :py:attr:`q`. - :param alpha: Value for :py:attr:`alpha`. + :param y: Value for :py:attr:`y`. + :param i: Value for :py:attr:`i`. + :param q: Value for :py:attr:`q`. + :param alpha: Value for :py:attr:`alpha`. """ __slots__ = ('_y', '_i', '_q') @@ -1328,24 +1320,24 @@ class YIQColor(Color): @property def y(self) -> float: - """ The luma. """ + """Get the luma.""" return self._y @property def i(self) -> float: - """ The orange-blue range value. """ + """Get the orange-blue range value.""" return self._i @property def q(self) -> float: - """ The purple-green range value. """ + """Get the purple-green range value.""" return self._q def assrgb(self) -> 'SRGBColor': - """ Get an SRGBColor out of the current object. """ + """Get an SRGBColor out of the current object.""" y, i, q = self.y, self.i, self.q @@ -1363,7 +1355,7 @@ class YIQColor(Color): ) def asyiq(self) -> 'YIQColor': - """ Get an YIQColor out of the current object. """ + """Get an YIQColor out of the current object.""" return YIQColor( y=self.y, @@ -1374,12 +1366,12 @@ class YIQColor(Color): class YUVColor(Color): - """ A color expressed using its YUV components. + """A color expressed using its YUV components. - :param y: Value for :py:attr:`y`. - :param u: Value for :py:attr:`u`. - :param v: Value for :py:attr:`v`. - :param alpha: Value for :py:attr:`alpha`. + :param y: Value for :py:attr:`y`. + :param u: Value for :py:attr:`u`. + :param v: Value for :py:attr:`v`. + :param alpha: Value for :py:attr:`alpha`. """ __slots__ = ('_y', '_u', '_v') @@ -1403,24 +1395,24 @@ class YUVColor(Color): @property def y(self) -> float: - """ The luma. """ + """Get the luma.""" return self._y @property def u(self) -> float: - """ The U chrominance. """ + """Get the U chrominance.""" return self._u @property def v(self) -> float: - """ The V chrominance. """ + """Get the V chrominance.""" return self._v def asyuv(self) -> 'YUVColor': - """ Get an YUVColor out of the current object. """ + """Get an YUVColor out of the current object.""" return YUVColor( y=self.y, @@ -1429,5 +1421,4 @@ class YUVColor(Color): alpha=self.alpha, ) - # End of file. diff --git a/thcolor/decoders.py b/thcolor/decoders.py index f8383be..b29f149 100644 --- a/thcolor/decoders.py +++ b/thcolor/decoders.py @@ -3,10 +3,9 @@ # Copyright (C) 2019-2022 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr> # This file is part of the thcolor project, which is MIT-licensed. # ***************************************************************************** -""" Function and data reference. """ +"""Function and data reference.""" import re as _re - from abc import ABCMeta as _ABCMeta from collections.abc import Mapping as _Mapping from enum import Enum as _Enum, auto as _auto @@ -37,21 +36,20 @@ _NO_DEFAULT_VALUE = type('_NO_DEFAULT_VALUE_TYPE', (), {})() def _canonicalkey(x): - """ Get a canonical key for the decoder mapping. """ + """Get a canonical key for the decoder mapping.""" return str(x).replace('-', '_').casefold() def _issubclass(cls, types): - """ Check if ``cls`` is a subclass of ``types``. + """Check if ``cls`` is a subclass of ``types``. - Until Python 3.10, this function is not aware of the special - types available in the standard ``typing`` module; this function - aims at fixing this for Python <= 3.9. + Until Python 3.10, this function is not aware of the special + types available in the standard ``typing`` module; this function + aims at fixing this for Python <= 3.9. """ # Check if is a special case from typing that we know of. - try: origin = types.__origin__ except AttributeError: @@ -64,7 +62,6 @@ def _issubclass(cls, types): # If ``types`` is any iterable type (tuple, list, you name it), # then we check if the class is a subclass of at least one element # in the iterable. - try: types_iter = iter(types) except TypeError: @@ -73,7 +70,6 @@ def _issubclass(cls, types): return any(_issubclass(cls, type_) for type_ in types_iter) # We default on the base ``issubclass`` from here. - try: return issubclass(cls, types) except TypeError: @@ -81,31 +77,33 @@ def _issubclass(cls, types): def _isinstance(value, types): - """ Check if ``cls`` is a subclass of ``types``. + """Check if ``cls`` is a subclass of ``types``. - Until Python 3.10, this function is not aware of the special - types available in the standard ``typing`` module; this function - aims at fixing this for Python <= 3.9. + Until Python 3.10, this function is not aware of the special + types available in the standard ``typing`` module; this function + aims at fixing this for Python <= 3.9. """ return _issubclass(type(value), types) def _get_args(func) -> _Sequence[_Tuple[str, _Any, _Any]]: - """ Get the arguments definition from a function. + """Get the arguments definition from a function. - Each argument is returned as a tuple containing: + Each argument is returned as a tuple containing: - * The name of the argument. - * The type of the argument. - * The default value of the argument; - ``_NO_DEFAULT_VALUE`` if the argument has no default - value. + * The name of the argument. + * The type of the argument. + * The default value of the argument; + ``_NO_DEFAULT_VALUE`` if the argument has no default value. - In case an argument is keyword-only with no default - value, the function will raise an exception. + In case an argument is keyword-only with no default + value, the function will raise an exception. """ + if isinstance(func, staticmethod): + func = func.__func__ + argspec = _getfullargspec(func) try: kwarg = next( @@ -141,14 +139,14 @@ def _get_args(func) -> _Sequence[_Tuple[str, _Any, _Any]]: def _make_function(func, name: str, args: _Optional[_Sequence[_Any]]): - """ Create a function calling another. + """Create a function calling another. - This function will rearrange the given positional - arguments with the given argument order ``args``. + This function will rearrange the given positional + arguments with the given argument order ``args``. - It will also set the default value and annotations of - the previous function, rearranged using the given - function. + It will also set the default value and annotations of + the previous function, rearranged using the given + function. """ # Here, we get the variables for the base function call. @@ -156,7 +154,6 @@ def _make_function(func, name: str, args: _Optional[_Sequence[_Any]]): # # * ``proxy_args``: the proxy arguments as (name, type, defvalue) tuples. # * ``args_index``: a args index -> proxy index correspondance. - args_spec = _get_args(func) args_index: _List[_Optional[int]] @@ -182,7 +179,6 @@ def _make_function(func, name: str, args: _Optional[_Sequence[_Any]]): # We want to check that no value without a default value was # left unspecified. - for (aname, _, adefvalue), proxy_index in zip(args_spec, args_index): if proxy_index is None and adefvalue is _NO_DEFAULT_VALUE: raise ValueError( @@ -190,10 +186,7 @@ def _make_function(func, name: str, args: _Optional[_Sequence[_Any]]): ) # Produce the function code. - - locals_ = { - '__func': func, - } + locals_ = {'__func': func} def _iter_proxy_args(d, args): # First, obtain the index of the last optional argument from the @@ -278,7 +271,7 @@ def _ncol_func( class _ColorExpressionTokenType(_Enum): - """ Token type. """ + """Token type.""" NAME = _auto() ANGLE = _auto() @@ -296,7 +289,7 @@ class _ColorExpressionTokenType(_Enum): class _ColorExpressionToken: - """ A token as expressed by the color expression lexer. """ + """A token as expressed by the color expression lexer.""" __slots__ = ('_type', '_name', '_value', '_column', '_rawtext') @@ -343,31 +336,31 @@ class _ColorExpressionToken: @property def type_(self): - """ Token type. """ + """Token type.""" return self._type @property def name(self): - """ Token name. """ + """Token name.""" return self._name @property def value(self): - """ Token value. """ + """Token value.""" return self._value @property def column(self): - """ Column at which the token is located. """ + """Column at which the token is located.""" return self._column @property def rawtext(self): - """ Raw text for decoding. """ + """Raw text for decoding.""" return self._rawtext @@ -406,11 +399,11 @@ _colorexpressionpattern = _re.compile( def _get_color_tokens(string: str, extended_hex: bool = False): - """ Get color tokens. + """Get color tokens. - :param string: The string to get the color tokens from. - :param extended_hex: Whether 4 or 8-digit hex colors are - allowed (``True``) or not (``False``). + :param string: The string to get the color tokens from. + :param extended_hex: Whether 4 or 8-digit hex colors are + allowed (``True``) or not (``False``). """ start: int = 0 @@ -439,20 +432,22 @@ def _get_color_tokens(string: str, extended_hex: bool = False): elif result['agl'] is not None: value = float(result['agl_sign'] + result['agl_val']) typ = result['agl_typ'] + + ang: _Angle if typ == 'deg': - value = _DegreesAngle(value) + ang = _DegreesAngle(value) elif typ == 'rad': - value = _RadiansAngle(value) + ang = _RadiansAngle(value) elif typ == 'grad': - value = _GradiansAngle(value) + ang = _GradiansAngle(value) elif typ in ('turn', 'turns'): - value = _TurnsAngle(value) + ang = _TurnsAngle(value) else: raise NotImplementedError yield _ColorExpressionToken( _ColorExpressionToken.TYPE_ANGLE, - value=value, + value=ang, column=column, rawtext=result['agl'], ) @@ -538,7 +533,6 @@ def _get_color_tokens(string: str, extended_hex: bool = False): # # Note that ``func(a, b) / / c`` will still contain an empty # argument before name 'c'. - if not was_call_end: yield _ColorExpressionToken( _ColorExpressionToken.TYPE_EMPTY, @@ -573,14 +567,14 @@ def _get_color_tokens(string: str, extended_hex: bool = False): # --- def fallback(value: _Union[_Color, _Angle, int, float]): - """ Decorator for setting a fallback value on a function. + """Set a fallback value on a function, if used as a variable. - When a function is used as a symbol instead of a function, - by default, it yields that it is callable and should be - accompanied with arguments. + When a function is used as a symbol instead of a function, + by default, it yields that it is callable and should be + accompanied with arguments. - Using this decorator on a function makes it to be evaluated - as a color in this case. + Using this decorator on a function makes it to be evaluated + as a color in this case. """ if not isinstance(value, (_Color, _Angle, int, float)): @@ -597,7 +591,7 @@ def fallback(value: _Union[_Color, _Angle, int, float]): class alias: - """ Define an alias for a function. """ + """Define an alias for a function.""" __slots__ = ('_name', '_args') @@ -612,47 +606,43 @@ class alias: @property def name(self): - """ Get the name of the function the current alias targets. """ + """Get the name of the function the current alias targets.""" return self._name @property def args(self) -> _Sequence[str]: - """ Get the arguments' order of the alias. """ + """Get the arguments' order of the alias.""" return self._args class ColorDecoder(_Mapping): - """ Base color decoder. - - This color decoder behaves as a mapping returning syntax elements, - with the additional properties controlling its behaviour. - - The properties defined at class definition time are the following: - - ``__mapping__`` + """Base color decoder. - Defines the base mapping that is copied at the - instanciation of each class. + This color decoder behaves as a mapping returning syntax elements, + with the additional properties controlling its behaviour. - ``__ncol_support__`` + The properties defined at class definition time are the following: - Defines whether natural colors (NCol) are - supported while decoding or not. + ``__mapping__`` + Defines the base mapping that is copied at the + instanciation of each class. - ``__extended_hex_support__`` + ``__ncol_support__`` + Defines whether natural colors (NCol) are + supported while decoding or not. - Defines whether 4 or 8-digit - hexadecimal colors (starting with a '#') are allowed or not. + ``__extended_hex_support__`` + Defines whether 4 or 8-digit + hexadecimal colors (starting with a '#') are allowed or not. - ``__defaults_to_netscape_color`` + ``__defaults_to_netscape_color`` + Defines whether color decoding + defaults to Netscape color parsing or not. - Defines whether color decoding - defaults to Netscape color parsing or not. - - These properties cannot be changed at runtime, although they might - be in a future version of thcolor. + These properties cannot be changed at runtime, although they might + be in a future version of thcolor. """ __slots__ = ('_mapping',) @@ -699,17 +689,17 @@ class ColorDecoder(_Mapping): prefer_colors: bool = False, prefer_angles: bool = False, ) -> _Sequence[_Optional[_Union[_Color, _Angle, int, float]]]: - """ Decode a color expression. + """Decode a color expression. - When top-level result(s) are not colors and colors are - actually expected if possible to obtain, the caller should - set ``prefer_colors`` to ``True`` in order for top-level - conversions to take place. + When top-level result(s) are not colors and colors are + actually expected if possible to obtain, the caller should + set ``prefer_colors`` to ``True`` in order for top-level + conversions to take place. - Otherwise, when top-level result(s) are not angles and angles - are actually expected if possible to obtain, the caller should - set ``prefer_angles`` to ``True`` in order for top-level - conversions to take place. + Otherwise, when top-level result(s) are not angles and angles + are actually expected if possible to obtain, the caller should + set ``prefer_angles`` to ``True`` in order for top-level + conversions to take place. """ global _color_pattern @@ -729,14 +719,13 @@ class ColorDecoder(_Mapping): # * ``current``: the current list of elements, which is put on # top of the stack when a call is started and is popped out of # the top of the stack when a call is ended. - stack: _List[_List[_ColorExpressionToken]] = [] func_stack: _List[_ColorExpressionToken] = [] implicit_stack: _List[int] = [] current: _List[_ColorExpressionToken] = [] def _get_parent_func(): - """ Get the parent function name for exceptions. """ + """Get the parent function name for exceptions.""" if not func_stack: return None @@ -774,7 +763,6 @@ class ColorDecoder(_Mapping): _ColorExpressionToken.TYPE_EMPTY, ): # Current token is a value, we add it. - current.append(token) elif token.type_ == _ColorExpressionToken.TYPE_CALL_START: if func_stack and ( @@ -812,7 +800,6 @@ class ColorDecoder(_Mapping): # We pop the function out of the stack with the # arguments we have. - implicit_stack.pop(0) old_current = stack.pop(0) old_current.append(_ColorExpressionToken( @@ -824,7 +811,6 @@ class ColorDecoder(_Mapping): current = old_current # We have a function name and it is not implicit. - old_current = stack.pop(0) old_current.append(_ColorExpressionToken( _ColorExpressionTokenType.CALL, @@ -850,7 +836,6 @@ class ColorDecoder(_Mapping): # We have the required number of tokens for this # to work. - old_current = stack.pop(0) old_current.append(_ColorExpressionToken( type_=_ColorExpressionToken.TYPE_CALL, @@ -863,7 +848,6 @@ class ColorDecoder(_Mapping): # We want to pop out all implicit functions we have. # If we still have an explicit function in the function stack, # that means a parenthesis was omitted. - while func_stack: try: name_token = func_stack.pop(0) @@ -879,7 +863,6 @@ class ColorDecoder(_Mapping): # We pop the function out of the stack with the # arguments we have. - implicit_stack.pop(0) old_current = stack.pop(0) old_current.append(_ColorExpressionToken( @@ -891,16 +874,15 @@ class ColorDecoder(_Mapping): current = old_current # Evaluating stage. - def evaluate(element, parent_func=None): - """ Evaluate the element in the current context. + """Evaluate the element in the current context. - Always returns a tuple where: + Always returns a tuple where: - * The first element is the real answer. - * The second element is the string which can be used - in the case of a color fallback (when Netscape color - defaulting is on). + * The first element is the real answer. + * The second element is the string which can be used + in the case of a color fallback (when Netscape color + defaulting is on). """ if element.type_ == _ColorExpressionTokenType.NAME: @@ -962,7 +944,6 @@ class ColorDecoder(_Mapping): ) # Check the function. - try: args_spec = _get_args(func) except ValueError as exc: @@ -978,7 +959,6 @@ class ColorDecoder(_Mapping): # We remove empty arguments at the end of calls so that # even calls such as ``func(a, b, , , , , ,)`` will only # have arguments 'a' and 'b'. - unevaluated_args = element.value while ( unevaluated_args and unevaluated_args[-1].type_ @@ -996,7 +976,6 @@ class ColorDecoder(_Mapping): # Now we should evaluate subtokens and check the types of # the function. - new_args = [] for token, (argname, argtype, argdefvalue) in _zip_longest( @@ -1049,7 +1028,6 @@ class ColorDecoder(_Mapping): new_args.append(arg) # Get the result. - result = func(*new_args) if result is None: raise _ColorExpressionSyntaxError( @@ -1086,7 +1064,7 @@ class ColorDecoder(_Mapping): class _MetaColorDecoderType(_ABCMeta): - """ The decoder type. """ + """The decoder type.""" def __new__(mcls, clsname, superclasses, attributedict): elements = {} @@ -1097,7 +1075,6 @@ class _MetaColorDecoderType(_ABCMeta): } # Explore the parents. - for supercls in reversed(superclasses): if not issubclass(supercls, ColorDecoder): continue @@ -1112,7 +1089,6 @@ class _MetaColorDecoderType(_ABCMeta): options[option] = attributedict.get(option, value) # Instanciate the class. - clsattrs = { '__doc__': attributedict.get('__doc__', None), '__mapping__': elements, @@ -1123,7 +1099,6 @@ class _MetaColorDecoderType(_ABCMeta): del clsattrs # Get the elements and aliases from the current attribute dictionary. - aliases = {} childelements = {} for key, value in attributedict.items(): @@ -1157,7 +1132,6 @@ class _MetaColorDecoderType(_ABCMeta): # # This is not optimized. However, given the cases we have, # it'll be enough. - aliaselements = {} while aliases: resolved = len(aliases) @@ -1169,14 +1143,12 @@ class _MetaColorDecoderType(_ABCMeta): # Check if the current alias still depends on an alias # that hasn't been resolved yet; in that case, ignore # it for now. - if funcname in aliases: resolved -= 1 nextaliases[key] = alias_value continue # Check the function name and arguments. - try: func = childelements[funcname] except KeyError: @@ -1207,12 +1179,10 @@ class _MetaColorDecoderType(_ABCMeta): ) # Add all of the elements in order of importance. - elements.update(aliaselements) elements.update(childelements) # Check the types of the annotations, just in case. - for key, element in elements.items(): try: args_spec = _get_args(element) @@ -1262,7 +1232,7 @@ class _MetaColorDecoderType(_ABCMeta): class MetaColorDecoder(ColorDecoder, metaclass=_MetaColorDecoderType): - """ Base meta color decoder, which gets the function and things. """ + """Base meta color decoder, which gets the function and things.""" pass diff --git a/thcolor/errors.py b/thcolor/errors.py index 37bf337..5c6e0f4 100755 --- a/thcolor/errors.py +++ b/thcolor/errors.py @@ -3,17 +3,15 @@ # Copyright (C) 2019-2022 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr> # This file is part of the thcolor project, which is MIT-licensed. # ***************************************************************************** -""" Exception definitions. """ +"""Exception definitions.""" -__all__ = [ - 'ColorExpressionSyntaxError', -] +__all__ = ['ColorExpressionSyntaxError'] class ColorExpressionSyntaxError(Exception): - """ An error has occurred while decoding a color expression. + """An error has occurred while decoding a color expression. - Such an error can happen during parsing or evaluating. + Such an error can happen during parsing or evaluating. """ def __init__(self, text, column=None, func=None): @@ -37,28 +35,28 @@ class ColorExpressionSyntaxError(Exception): @property def text(self): - """ Exception message, usually linked to the context. """ + """Exception message, usually linked to the context.""" return self._text @property def column(self): - """ Column of the expression at which the exception has occurred. + """Column of the expression at which the exception has occurred. - ``None`` if the error has occurred on an unknown column or on - the whole exception. + ``None`` if the error has occurred on an unknown column or on + the whole exception. """ return self._column @property def func(self): - """ Name of the function we were calling when the error occurred. + """Name of the function we were calling when the error occurred. - Either on arguments decoding or erroneous argument type or value. - Is ``None`` if the context is unknown or the error hasn't - occurred while calling a function or decoding its - arguments. + Either on arguments decoding or erroneous argument type or value. + Is ``None`` if the context is unknown or the error hasn't + occurred while calling a function or decoding its + arguments. """ return self._func diff --git a/thcolor/utils.py b/thcolor/utils.py index 7d60d0c..337c794 100644 --- a/thcolor/utils.py +++ b/thcolor/utils.py @@ -3,7 +3,7 @@ # Copyright (C) 2022 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr> # This file is part of the thcolor project, which is MIT-licensed. # ***************************************************************************** -""" Utilities for the thcolor module. """ +"""Utilities for the thcolor module.""" from typing import Optional as _Optional @@ -11,7 +11,7 @@ __all__ = ['factor', 'round_half_up'] def factor(x, max_: int = 100, clip: bool = False): - """ Return a factor based on if something is a float or an int. """ + """Return a factor based on if something is a float or an int.""" if isinstance(x, float): pass @@ -27,13 +27,13 @@ def factor(x, max_: int = 100, clip: bool = False): def round_half_up(number: float, ndigits: _Optional[int] = None) -> float: - """ Round a number to the nearest integer. + """Round a number to the nearest integer. - This function exists because Python's built-in ``round`` function - uses half-to-even rounding, also called "Banker's rounding". - This means that 1.5 is rounded to 2 and 2.5 is also rounded to 2. + This function exists because Python's built-in ``round`` function + uses half-to-even rounding, also called "Banker's rounding". + This means that 1.5 is rounded to 2 and 2.5 is also rounded to 2. - What we want is a half-to-up rounding, so we have this function. + What we want is a half-to-up rounding, so we have this function. """ if ndigits is None: diff --git a/thcolor/version.py b/thcolor/version.py index 5a3a5fc..886c512 100755 --- a/thcolor/version.py +++ b/thcolor/version.py @@ -3,7 +3,7 @@ # Copyright (C) 2021-2022 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr> # This file is part of the thcolor project, which is MIT-licensed. # ***************************************************************************** -""" Version definition for the thcolor module. """ +"""Version definition for the thcolor module.""" __all__ = ['version'] |