aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--setup.cfg20
-rwxr-xr-xsetup.py3
-rwxr-xr-xtests/__init__.py2
-rwxr-xr-xtests/test_angles.py4
-rwxr-xr-xtests/test_builtin.py4
-rwxr-xr-xtests/test_colors.py30
-rwxr-xr-xtests/test_decoders.py57
-rwxr-xr-xthcolor/__init__.py8
-rw-r--r--thcolor/angles.py78
-rw-r--r--thcolor/builtin.py108
-rw-r--r--thcolor/colors.py343
-rw-r--r--thcolor/decoders.py212
-rwxr-xr-xthcolor/errors.py28
-rw-r--r--thcolor/utils.py14
-rwxr-xr-xthcolor/version.py2
16 files changed, 448 insertions, 467 deletions
diff --git a/.gitignore b/.gitignore
index dd52273..baae2cb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,8 @@ __pycache__
/*.egg-info
/dist
/.spyproject
+/.tool-versions
+/.python-version
/*.kdev4
/build
/docs/_build
diff --git a/setup.cfg b/setup.cfg
index acc9161..d002530 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -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
diff --git a/setup.py b/setup.py
index dc73720..b4d2739 100755
--- a/setup.py
+++ b/setup.py
@@ -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']