Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor code-style #17

Merged
merged 18 commits into from
Apr 2, 2024
Merged
24 changes: 12 additions & 12 deletions docs/source/contribute_colormaps.rst
Original file line number Diff line number Diff line change
Expand Up @@ -64,30 +64,30 @@ To start defining your colormap configuration YAML files, please refers
to the :doc:`Introduction to Univariate Colormaps <tutorials/Introduction_univariate_colormaps>`
documentation.

Consider adding relevant colormap categories within the `category` field of the `auxiliary` subdictionary.
Consider adding relevant colormap categories within the ``category`` field of the ``auxiliary`` subdictionary.
These categories help differentiate between colormaps based on:

- the type of colormap: `cyclic`, `diverging`, `sequential`, `qualitative`, `perceptual`.
- the variables for which the colormap is commonly used (i.e. elevation, bathymetry, precipitation, temperature, etc.).
- the author or source agency of the colormap (i.e. `brewer`, `crameri`, `meteoswiss`, `nasa`, etc.).
- the type of colormap: ``cyclic``, ``diverging``, ``sequential``, ``qualitative``, ``perceptual``.
- the variables for which the colormap is commonly used (i.e. ``elevation``, ``bathymetry``, ``precipitation``, ``temperature``, etc.).
- the author or source agency of the colormap (i.e. ``brewer``, ``crameri``, ``meteoswiss``, ``nasa``, etc.).

Additionally, in the `auxiliary` subdictionary, you can specify custom fields such
as `comments`, `references`, `url`, `author`, `license`.
Additionally, in the ``auxiliary`` subdictionary, you can specify custom fields such
as ``comments``, ``references``, ``url``, ``author``, ``license``.

Once your colormap configurations are finalized, save the configuration YAML files in the `pycolorbar/etc/colormaps` directory.
Once your colormap configurations are finalized, save the configuration YAML files in the ``pycolorbar/etc/colormaps`` directory.

Before proceeding, please review the existing directories within `pycolorbar/etc/colormaps`.
Before proceeding, please review the existing directories within ``pycolorbar/etc/colormaps``.
If it seems appropriate, create a new directory and place your colormap configuration YAML files inside it.

.. note:: Guidelines for the naming of the colormap configuration YAML files:

* The colormap name must not end with `_r`.
* The colormap name must not end with ``_r``.

* Usage of dash ( - ) and underscore ( _ ) is allowed.
* Usage of dash (``-``) and underscore (``_``) is allowed.

* If you're adding colormap configurations of a specific author (i.e. `brewer`, `crameri`), consider naming the new directory after the original author.
* If you're adding colormap configurations of a specific author (i.e. ``brewer``, ``crameri``), consider naming the new directory after the original author.

* If you're adding colormap configurations of a specific variable (i.e. `precipitation`, `temperature`), consider naming the new directory after the variable.
* If you're adding colormap configurations of a specific variable (i.e. ``precipitation``, ``temperature``), consider naming the new directory after the variable.


.. _step3:
Expand Down
81 changes: 44 additions & 37 deletions docs/source/maintainers_guidelines.rst
Original file line number Diff line number Diff line change
Expand Up @@ -50,35 +50,6 @@ Examples of non-breaking changes include :
- Internal function refactoring that does not affect the behavior of the software directly.


Release process
---------------

Before releasing a new version, the CHANGELOG.md file should be updated. Run

.. code-block:: bash

make changelog X.Y.Z

to update the CHANGELOG.md file with the list of issues and pull requests that have been closed since the last release.
Manually add a description to the release if necessary.
Then, commit the new CHANGELOG.md file.

.. code-block:: bash

git add CHANGELOG.md
git commit -m "update CHANGELOG.md for version X.Y.Z"
git push

Create a new tag to trigger the release process.

.. code-block:: bash

git tag -a vX.Y.Z -m "Version X.Y.Z"
git push --tags

On GitHub, edit the release description to add the list of changes from the CHANGELOG.md file.


Ongoing version support
-----------------------------------

Expand All @@ -89,7 +60,7 @@ The maintaners do their best but does not guarantee any period of support or mai
Releases that are 2 years or older may be considered as deprecated.


Documentation pipeline
Documentation
========================

pycolorbar's documentation is built using the powerful `Sphinx <https://www.sphinx-doc.org/en/master/>`_ framework,
Expand Down Expand Up @@ -141,27 +112,63 @@ By following these steps, you should have a local version of the pycolorbar docu
in the ``docs/build/html/`` directory, ready for review or deployment!


Documentation deployement
Documentation deployment
----------------------------

A webhook is defined in the GitHub repository to trigger automatically the publication process to `ReadTheDocs <https://about.readthedocs.com/?ref=readthedocs.com>`__
after each Pull Request.

This webhook is linked to the pycolorbar core developer.

.. image:: /static/documentation_pipeline.png
.. image:: /static/documentation_release.png

Ghiggi Gionata owns the `ReadTheDoc <https://readthedocs.org/>`__ account.


Package releases pipeline
============================
Package release
=================

A `GitHub Action <https://github.com/ghiggi/gpm_api/actions>`_ is configured to automate the packaging and uploading process
to `PyPI <https://pypi.org/project/pycolorbar/>`_.
This action, detailed `here <https://github.com/ghiggi/pycolorbar/blob/main/.github/workflows/release_to_pypi.yml>`_,
triggers the packaging workflow depicted in the following image:

One `GitHub Action <https://github.com/ghiggi/pycolorbar/actions>`_ is defined to trigger the packaging and the upload on `pypi.org <https://pypi.org/project/pycolorbar/>`_.
.. image:: /static/package_release.png

.. image:: /static/package_pipeline.png
Upon the release of the package on PyPI, a conda-forge bot attempts to automatically update the
`conda-forge recipe <https://github.com/conda-forge/pycolorbar-feedstock/>`__.
Once the conda-forge recipe is updated, a new conda-forge package is released.

The PyPI project and the conda-forge recipes are collaboratively maintained by core contributors of the project.


Release process
---------------

Before releasing a new version, the ``CHANGELOG.md`` file should be updated. Run

.. code-block:: bash

make changelog X.Y.Z

to update the ``CHANGELOG.md`` file with the list of issues and pull requests that have been closed since the last release.
Manually add a description to the release if necessary.
Then, commit the new ``CHANGELOG.md`` file.

.. code-block:: bash

git add CHANGELOG.md
git commit -m "update CHANGELOG.md for version X.Y.Z"
git push

Create a new tag to trigger the release process.

.. code-block:: bash

git tag -a vX.Y.Z -m "Version X.Y.Z"
git push --tags

The `PyPI <https://pypi.org/>`__ project is shared between the core contributors.
On GitHub, edit the release description to add the list of changes from the ``CHANGELOG.md`` file.



Expand Down
Binary file modified docs/source/static/collaborative_process.png
100644 → 100755
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/source/static/documentation_pipeline.png
Binary file not shown.
Binary file added docs/source/static/documentation_release.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 3 additions & 5 deletions pycolorbar/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
# SOFTWARE.

# -----------------------------------------------------------------------------.
import contextlib
import os
from importlib.metadata import PackageNotFoundError, version

Expand Down Expand Up @@ -59,15 +60,12 @@
# TODO: donfig config !
# pycolorbar.register_default_colormaps()
# pycolorbar.register_default_colorbars()
_root_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) # noqa
_root_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
etc_directory = os.path.join(_root_path, "pycolorbar", "etc")


__all__ = []

# Get version
try:
with contextlib.suppress(PackageNotFoundError):
__version__ = version("pycolorbar")
except PackageNotFoundError:
# package is not installed
pass
50 changes: 29 additions & 21 deletions pycolorbar/colors/colors_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@
# SOFTWARE.

# -----------------------------------------------------------------------------.
"""Color encoding and decoding functions."""
import numpy as np


class ColorEncoderDecoder:
"""Base Color Encoding-Decoding Class."""

def __init__(self, external_data_range, internal_data_range, name):
"""
Initialize a color encoder-decoder to convert color values between external and internal representations.
"""Initialize a color encoder-decoder to convert color values between external and internal representations.

Parameters
----------
Expand Down Expand Up @@ -73,10 +75,12 @@ def decode(self, colors):
return np.array(
[
self.decoding_functions[channel](
val, *self.external_data_range[channel], *self.internal_data_range[channel]
val,
*self.external_data_range[channel],
*self.internal_data_range[channel],
)
for channel, val in zip(self.external_data_range.keys(), colors.T)
]
],
).T

def encode(self, colors):
Expand All @@ -93,15 +97,16 @@ def encode(self, colors):
np.ndarray
Encoded color values in external representation.
"""
encoded_values = np.array(
return np.array(
[
self.encoding_functions[channel](
val, *self.internal_data_range[channel], *self.external_data_range[channel]
val,
*self.internal_data_range[channel],
*self.external_data_range[channel],
)
for channel, val in zip(self.internal_data_range.keys(), colors.T)
]
],
).T
return encoded_values

def _default_decode(self, value, from_min, from_max, to_min, to_max):
"""Default decoding function (linear scaling)."""
Expand Down Expand Up @@ -145,8 +150,8 @@ def check_colors(self, colors):
return colors

def check_valid_internal_data_range(self, colors):
"""
Check if the color values are within the internal data range for each channel.
"""Check if the color values are within the internal data range for each channel.

Raises an informative ValueError if a channel does not comply with the data range.

Parameters
Expand All @@ -166,12 +171,13 @@ def check_valid_internal_data_range(self, colors):
if not ((min_val <= channel_colors) & (channel_colors <= max_val)).all():
raise ValueError(
f"Channel '{channel}' values are not within the internal data range. "
f"Expected range ({min_val}, {max_val}), but got values outside this range."
f"Expected range ({min_val}, {max_val}), but got values outside this range.",
)

def check_valid_external_data_range(self, colors, strict=False):
"""
Check if the color values are within the external data range for each channel.

Raises an informative ValueError if a channel does not comply with the data range.
If 'strict' is True, it ensures that not all values are within the internal data range.

Expand All @@ -196,15 +202,15 @@ def check_valid_external_data_range(self, colors, strict=False):
if not ((min_val <= channel_colors) & (channel_colors <= max_val)).all():
raise ValueError(
f"Channel '{channel}' values are not within the external data range. "
f"Expected range ({min_val}, {max_val}), but got values outside this range."
f"Expected range ({min_val}, {max_val}), but got values outside this range.",
)

if strict:
internal_min_val, internal_max_val = self.internal_data_range[channel]
if ((internal_min_val <= channel_colors) & (channel_colors <= internal_max_val)).all():
raise ValueError(
f"All '{channel}' values are within the internal data range "
"while expecting external representation."
"while expecting external representation.",
)

def is_within_internal_data_range(self, colors):
Expand Down Expand Up @@ -234,6 +240,7 @@ def is_within_internal_data_range(self, colors):
def is_within_external_data_range(self, colors, strict: bool = False):
"""
Check if the color values of each channels are within the external data range.

Optionally, perform a strict check to ensure that not all values are also within the internal data range.

Parameters
Expand Down Expand Up @@ -262,8 +269,7 @@ def is_within_external_data_range(self, colors, strict: bool = False):
is_within = np.all(np.vstack(conditions))
if not strict:
return is_within
else:
return is_within and not self.is_within_internal_data_range(colors)
return is_within and not self.is_within_internal_data_range(colors)


class RGBEncoderDecoder(ColorEncoderDecoder):
Expand Down Expand Up @@ -319,11 +325,11 @@ def __init__(self):
self.decoding_functions["H"] = self._hue_decode
self.encoding_functions["H"] = self._hue_encode

def _hue_decode(self, hue, from_min, from_max, to_min, to_max):
def _hue_decode(self, hue, from_min, from_max, to_min, to_max): # noqa: ARG002
"""Custom decode function for Hue channel (from degrees to radians)."""
return hue * (2 * np.pi) / 360 # Convert degrees to radians

def _hue_encode(self, hue, from_min, from_max, to_min, to_max):
def _hue_encode(self, hue, from_min, from_max, to_min, to_max): # noqa: ARG002
"""Custom encode function for Hue channel (from radians to degrees)."""
return hue * 360 / (2 * np.pi) # Convert radians to degrees

Expand Down Expand Up @@ -378,11 +384,11 @@ def __init__(self):
self.decoding_functions["H"] = self._hue_decode
self.encoding_functions["H"] = self._hue_encode

def _hue_decode(self, hue, from_min, from_max, to_min, to_max):
def _hue_decode(self, hue, from_min, from_max, to_min, to_max): # noqa: ARG002
"""Custom decode function for Hue channel (from degrees to radians)."""
return hue * (2 * np.pi) / 360 # Convert degrees to radians

def _hue_encode(self, hue, from_min, from_max, to_min, to_max):
def _hue_encode(self, hue, from_min, from_max, to_min, to_max): # noqa: ARG002
"""Custom encode function for Hue channel (from radians to degrees)."""
return hue * 360 / (2 * np.pi) # Convert radians to degrees

Expand Down Expand Up @@ -460,7 +466,7 @@ def __init__(self):


def _get_color_space_dict():
class_dict = {
return {
"RGB": RGBEncoderDecoder,
"RGBA": RGBAEncoderDecoder,
"HSV": HSVEncoderDecoder,
Expand All @@ -471,7 +477,6 @@ def _get_color_space_dict():
"CIEXYZ": CIEXYZEncoderDecoder,
"CMYK": CMYKEncoderDecoder,
}
return class_dict


def get_color_space_class(color_space):
Expand Down Expand Up @@ -570,6 +575,7 @@ def is_within_internal_data_range(colors, color_space):
def is_within_external_data_range(colors, color_space, strict=False):
"""
Check if the color values are within the external data range for the specified color space.

Optionally, perform a strict check to ensure that not all values are also within the internal data range.

Parameters
Expand All @@ -595,6 +601,7 @@ def is_within_external_data_range(colors, color_space, strict=False):
def check_valid_internal_data_range(colors, color_space):
"""
Check if the color values are within the internal data range for the specified color space.

Raises an informative ValueError if a channel does not comply with the data range.

Parameters
Expand All @@ -616,6 +623,7 @@ def check_valid_internal_data_range(colors, color_space):
def check_valid_external_data_range(colors, color_space, strict=False):
"""
Check if the color values are within the external data range for each channel.

Raises an informative ValueError if a channel does not comply with the data range.
If 'strict' is True, it ensures that not all values are within the internal data range.

Expand Down
3 changes: 1 addition & 2 deletions pycolorbar/settings/colorbar_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ def is_single_colorbar_settings(dictionary):
"""Determine if a dictionary is a single colorbar settings."""
if np.any(np.isin(["cmap", "norm", "cbar", "auxiliary"], list(dictionary))):
return True
else:
return False
return False


def read_cbar_dict(filepath, name=None, validate=False):
Expand Down
Loading