diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..1ef56c9e --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,66 @@ +name: Documentation + +on: + push: + branches: + - main + tags: + - 'v*' + pull_request: + branches: + - dev + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install system dependencies + run: sudo apt-get update && sudo apt-get install -y graphviz + + - name: Install uv + uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + python-version: "3.12" + + - name: Install the project + run: uv sync --only-group docs + + - name: Build documentation + run: | + cd docs + uv run make html + + - name: Add .nojekyll file + run: touch docs/build/html/.nojekyll + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: 'docs/build/html' + + deploy: + # Only deploy on push to main or release tags + if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 05dd88fd..1c781eec 100644 --- a/.gitignore +++ b/.gitignore @@ -70,6 +70,7 @@ instance/ # Sphinx documentation docs/_build/ +docs/build/ docs/source/_build docs/source/generated diff --git a/.readthedocs.yaml b/.readthedocs.yaml deleted file mode 100644 index 69ec3f78..00000000 --- a/.readthedocs.yaml +++ /dev/null @@ -1,15 +0,0 @@ -version: 2 - -build: - os: ubuntu-22.04 - tools: - python: "3.12" - apt_packages: - - graphviz - -python: - install: - - requirements: docs/requirements.txt - -sphinx: - configuration: docs/source/conf.py \ No newline at end of file diff --git a/docs/requirements.txt b/docs/requirements.txt deleted file mode 100644 index a5a29f03..00000000 --- a/docs/requirements.txt +++ /dev/null @@ -1,7 +0,0 @@ -pydata_sphinx_theme -ezmsg-sigproc -sphinxext.rediraffe -myst_parser -sphinx_design -sphinx_autodoc_typehints -sphinx_copybutton \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py index da011818..d7a569fc 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,13 +1,26 @@ # Configuration file for the Sphinx documentation builder. +import os +import sys + # -- Project information -------------------------- project = "ezmsg" copyright = "2022, JHU/APL" author = "JHU/APL" -release = "3.3.4" -version = "3.3.4" +# Read version from pyproject.toml +try: + import tomllib +except ImportError: + import tomli as tomllib + +pyproject_path = os.path.join(os.path.dirname(__file__), "../../pyproject.toml") +with open(pyproject_path, "rb") as f: + pyproject_data = tomllib.load(f) + release = pyproject_data["project"]["version"] + version = release + # -- General configuration -------------------------- @@ -16,22 +29,20 @@ "sphinx.ext.autosummary", "sphinx.ext.doctest", "sphinx.ext.duration", - "sphinx.ext.graphviz", "sphinx.ext.intersphinx", "sphinx.ext.linkcode", "sphinx.ext.napoleon", - "sphinxext.rediraffe", - "myst_parser", "sphinx_autodoc_typehints", "sphinx_copybutton", - "sphinx_design", ] templates_path = ["_templates"] -source_suffix = [".rst", ".md"] +source_suffix = [".rst"] exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] +add_module_names = True + # The toctree master document master_doc = "index" @@ -41,17 +52,16 @@ # -- Autodoc configuration ------------------------------ autodoc_typehints_format = "short" python_use_unqualified_type_names = True -autodoc_type_aliases = { - "numpy.typing.NDArray": "numpy NDArray", -} # -- Intersphinx configuration -------------------------- intersphinx_mapping = { - "python": ("https://docs.python.org/3/", None), - "sphinx": ("https://www.sphinx-doc.org/en/master/", None), - "numpy": ("https://numpy.org/doc/stable/", None), - "scipy": ("https://docs.scipy.org/doc/scipy/", None), + "python": ("https://docs.python.org/3", None), + "sphinx": ("https://www.sphinx-doc.org/en/master", None), + "numpy": ("https://numpy.org/doc/stable", None), + "ezmsg.sigproc": ("https://www.ezmsg.org/ezmsg-sigproc", None), + "ezmsg.lsl": ("https://www.ezmsg.org/ezmsg-lsl", None), + "ezmsg.learn": ("https://www.ezmsg.org/ezmsg-learn", None), } intersphinx_disabled_domains = ["std"] @@ -60,26 +70,18 @@ html_theme = "pydata_sphinx_theme" html_logo = "_static/_images/ezmsg_logo.png" html_favicon = "_static/_images/ezmsg_logo.png" +html_title = f"ezmsg {version}" html_static_path = ["_static"] -# Redirects for pages that are unavailable or moved -rediraffe_redirects = { - "about.rst": "explanations/content-explanations.rst", - "getting-started.rst": "tutorials/start.rst", -} - # Timestamp is inserted at every page bottom in this strftime format. html_last_updated_fmt = '%Y-%m-%d' # -- Options for EPUB output -------------------------- epub_show_urls = "footnote" -add_module_names = False - branch = "main" -code_url = f"https://github.com/iscoe/ezmsg/blob/{branch}/" -sigproc_code_url = f"https://github.com/ezmsg-org/ezmsg-sigproc/blob/{branch}/" +code_url = f"https://github.com/ezmsg-org/ezmsg/blob/{branch}/" def linkcode_resolve(domain, info): @@ -88,9 +90,7 @@ def linkcode_resolve(domain, info): if not info["module"]: return None filename = info["module"].replace(".", "/") - if "sigproc" in filename: - return f"{sigproc_code_url}src/{filename}.py" - elif "core" in filename: + if "core" in filename: return f"{code_url}src/ezmsg/core/__init__.py" else: return f"{code_url}src/{filename}.py" diff --git a/docs/source/developer/content-developer.rst b/docs/source/developer/content-developer.rst deleted file mode 100644 index 30299e23..00000000 --- a/docs/source/developer/content-developer.rst +++ /dev/null @@ -1,12 +0,0 @@ -Information for ezmsg developers and contributors -###################################################### - -We welcome contributions to the ezmsg project! This section provides guidelines and standards for developers looking to contribute code, documentation, or other improvements to the `ezmsg` codebase. - - -.. toctree:: - :maxdepth: 1 - - install - standard - documentation diff --git a/docs/source/developer/documentation.rst b/docs/source/developer/documentation.rst deleted file mode 100644 index e227d5b3..00000000 --- a/docs/source/developer/documentation.rst +++ /dev/null @@ -1,70 +0,0 @@ -ezmsg Documentation -####################### - -Anyone planning to develop or contribute to `ezmsg`, should adhere to good documentation practices. - -|ezmsg_logo_small| Docstrings -================================= - -All functions and classes should be documented with docstrings. The docstrings should be clear, concise, and easy to read. - -A typical docstring will contain: - -- a short summary (usually a single sentence) of the function or class -- a paragraph of additional notes, which might include notes on usage, or anything else that -- a list of function parameters or class attributes (with type annotation) -- the return value and any exceptions raised by a function should be listed and described. -- (optionally) code snippets demonstrating usage (only for major public API functions or classes) - -There are many styles for writing docstrings. In `ezmsg`, we follow the `PEP 287 `_ standard. The documentation package (Sphinx) can also read docstrings written in the `Google Python Style Guide `_. - - -.. _documentation_building: - -|ezmsg_logo_small| Building ezmsg Documentation -================================================ - -The documentation is built using Sphinx. Thus, our documentation content must be compatible with Sphinx tools. - -To build the documentation locally into HTML format, you need to have the development environment set up as described in the `developer install documentation `_. Then - -- change directory to the `docs` folder in the ezmsg repository: - -.. code-block:: bash - - $ cd path/to/your/ezmsg/docs - -- Build the documentation: - -.. code-block:: bash - - # on Windows - $ uv run .\make.bat html - - # on Linux, MacOS - $ uv run make html - - -|ezmsg_logo_small| Documentation Standards -============================================ - -The documentation is written in `reStructuredText (ReST) `_. `Here `_ is a convenient ReST cheat sheet. - -The documentation contains four main sections representing our approach to documentation: - -1. **Tutorial**: step-by-step guides for new users to get started with ezmsg. Not intended to be comprehensive, but rather to get users up and running quickly. -2. **Explanation**: A more in-depth explanation of the concepts and architecture of ezmsg. This section is intended for users who want to understand how ezmsg works under the hood. -3. **How-To**: Practical guides on how to accomplish specific tasks using ezmsg. This section is intended for users who want to learn how to use ezmsg for specific use cases. -4. **Reference API**: Comprehensive reference documentation for all public classes, functions, and modules in ezmsg. This section is intended for users who need detailed information about the ezmsg API. - -The API is intended to be self-updating using `sphinx-autodoc `_. Thus, all public classes and functions should be documented with docstrings as described in the previous section. - -If a feature is worth describing in further detail, please consider adding it to the documentation in the appropriate section. - -Consider also adding code examples and usage snippets where appropriate. This can greatly enhance the usability of the documentation. Example scripts can also be added to the `examples` folder in the root of the ezmsg repository. - - - -.. |ezmsg_logo_small| image:: ../_static/_images/ezmsg_logo.png - :width: 40 - :alt: ezmsg logo diff --git a/docs/source/developer/install.rst b/docs/source/developer/install.rst deleted file mode 100644 index b51836f6..00000000 --- a/docs/source/developer/install.rst +++ /dev/null @@ -1,32 +0,0 @@ -Install instructions for developers -###################################################### - -In order to develop for ezmsg, you need to set up a development environment. This involves installing the necessary dependencies and tools. Additionally, the `GitHub repository of ezmsg `_ is protected; you will need to fork the repository and set up ssh keys to access it. Any work can then be done in your forked repository, and the submitted via a `Pull Request `_ to the ``dev`` branch of the main repository. - -.. note:: It is recommended to use `uv `_ to manage the development environment. There are alternatives to this including `poetry`, but `uv` is the method used by the ezmsg development team and explained below. - -|ezmsg_logo_small| Prerequisites -================================= - -1. `Install uv `_ -2. `Set up ssh keys to access GitHub `_ - -Please ensure that your GitHub account is identifiable to you - either the username or email address associated with your account should include your first and last names. - - -|ezmsg_logo_small| Setup -========================== - -1. `Fork `_ the ezmsg repository. -2. `Clone `_ your forked repository to your local machine using ssh. -3. Change directory to your local ezmsg clone (``$ cd path/to/your/ezmsg``) -4. Run ``$ uv sync --all-groups`` to install all dependencies. - -We have tried to keep external dependencies to a minimum. However, for development purposes, we do have some additional dependencies for testing and documentation generation. The ``--all-extras`` flag ensures these are also installed. We have not included any pre-commit hooks by default, but expect all code to conform to the style guidelines outlined in the `developer standard documentation `_. `uv` has both a linter and formatter built-in, so you can use these to ensure your code meets the style guidelines. - -.. note:: the ``uv sync`` command will create a virtual environment in the `.venv` folder in the root of the ezmsg repository. It will then install the dependencies specified in the `pyproject.toml` file into this virtual environment. - - -.. |ezmsg_logo_small| image:: ../_static/_images/ezmsg_logo.png - :width: 40 - :alt: ezmsg logo diff --git a/docs/source/developer/standard.rst b/docs/source/developer/standard.rst deleted file mode 100644 index ac996c79..00000000 --- a/docs/source/developer/standard.rst +++ /dev/null @@ -1,106 +0,0 @@ -Developer Conventions and Standards -######################################## - -When intending to contribute to the ezmsg codebase, please follow the conventions and standards outlined in this section. - -|ezmsg_logo_small| Types of Contributions -========================================== - -There are many ways to contribute to the ezmsg project. These include: - -- Bug fixes -- New features -- Changes to existing features -- Documentation improvements - -For each of these, please open an issue on the main `ezmsg GitHub repository `_. - -Generally, we suggest structuring the issue as follows: - -- **A clear and descriptive title** -- **A detailed description of the issue or feature request**. We find that describing the use case in detail is more helpful than prescribing a specific implementation. It allows the wider ezmsg community to provide input on the best way to implement the feature. -- **Context**: This can be - - **Steps to reproduce the issue** (for bug reports) - - **Examples of how the feature would be used** (for feature requests) - - **Screenshots or code snippets** (if applicable) - - -|ezmsg_logo_small| Pushing Code to ezmsg repository -==================================================== - -Understanding how GitHub works is essential for contributing code to the ezmsg repository. We use `git-flow `_ for branching (the link is for educational purposes - where it differs from the below, please follow this documentation instead). The ``main`` branch is the release branch of `ezmsg`. All development should be done on top of the development ``dev`` branch. - -First, ensure the ``dev`` branch in your forked repository is up-to-date with the ``dev`` branch of the main repository. You can do this by synchronising your forked ``dev`` branch on the GitHub website or from the terminal by setting up a remote that points to the main repository and pulling the latest changes: - -.. code-block:: bash - - git remote add upstream https://github.com/ezmsg-org/ezmsg.git - git checkout dev - git pull upstream dev - -Rather than working directly on the ``dev`` branch, please create a branch off of ``dev`` for your changes. This keeps your work isolated and makes it easier to manage multiple contributions. Your new branch should indicate if it is a bugfix, feature, or other type of change. You can create a new branch using: - -.. code-block:: bash - - git checkout -b feature/your-feature-name - # or for bug fixes - git checkout -b bugfix/your-bugfix-name - # or for documentation changes - git checkout -b docs/your-docs-change-name - -After making your changes, commit them with clear and descriptive messages. Commit messages should follow the conventional commit style by answering the question "What will this commit do?". For example: - -.. code-block:: bash - - git add . - git commit -m "Add to ezmsg" - -Then push your branch to your forked repository: - -.. code-block:: bash - - git push origin feature/your-feature-name - -Finally, when you are ready to submit your changes, create a `Pull Request `_ from your branch to the ``dev`` branch of the main ezmsg repository. Please ensure that your pull request includes a clear description of the changes made and any relevant context. A good pull request has the following details: - -- **Description**: A concise summary of the changes. Preferably, this should reference the issue it addresses. -- **Type of Change**: Whether it is a bug fix, new feature, documentation update, etc. -- **Changes affecting current usage**: Explain if the changes will affect existing users of ezmsg, and how. -- **Changes**: A list of the main changes made in the codebase. -- **Testing**: Describe how the changes were tested, including any new tests added. - - -|ezmsg_logo_small| Code Style -============================== - -We follow standard Python code standards based on ` PEP 8 `_. We strongly suggest using a formatter and linter in order to ensure following standard Python code conventions. We use the `ruff `_ package that comes with `uv`. - -One should double check their code before committing for formatting and linting issues. with `ruff` you can run: - -- ``uv run ruff format .`` for formatting checks. -- ``uv run ruff check .``` for linting checks. - - -|ezmsg_logo_small| Documentation Standards -============================================== - -All functions and classes should be documented with docstrings. If it is a public API function or class, the docstring should be comprehensive and follow the recommended docstring style. One should also consider adding any useful information to the `ezmsg` documentation in the `docs` folder of the repository. - -Docstrings in ezmsg follow `PEP 287 `_. The documentation package (sphinx) can also read docstrings written in the `Google Python Style Guide `_. - -For more information on writing documentation for ezmsg, please see the `developer documentation guide `_. - -|ezmsg_logo_small| Testing -============================= - -Writing code for `ezmsg` should always be accompanied by tests. Please follow the Test-Driven Development (TDD) approach where possible. This means writing tests before implementing the actual functionality. All existing and new tests should be run and pass before any code is committed. - -You can run tests using `pytest` using the following command: - -.. code-block:: bash - - uv run pytest tests/ - -.. |ezmsg_logo_small| image:: ../_static/_images/ezmsg_logo.png - :width: 40 - :alt: ezmsg logo diff --git a/docs/source/explanations/axisarray.rst b/docs/source/explanations/axisarray.rst deleted file mode 100644 index daee5e9a..00000000 --- a/docs/source/explanations/axisarray.rst +++ /dev/null @@ -1,143 +0,0 @@ -About AxisArray -###################### - -``AxisArray`` is a specialized message format used within the `ezmsg` framework to represent multi-dimensional data structures. In simple terms they are **N-dimensional arrays with labeled axes and optional metadata**. It is designed for applications in signal processing, scientific computing, and data analysis, where both the data and its context are important. ``AxisArray`` originally took inspiration from the functionality of the `xarray `_ library. - -``AxisArray`` is built-in to ezmsg. Import using: - -.. code-block:: python - - from ezmsg.core.util import AxisArray - -.. warning:: Importing ``AxisArray`` from ``ezmsg.core.util`` will import the `numpy` library. For this reason, we have implemented ezmsg in such a way that if you do not import ``AxisArray``, `numpy` will not be imported either. This is ideal for users wanting very lightweight applications and have no need for the functionality of `numpy`. - -|ezmsg_logo_small| Why use AxisArray? -**************************************** - -The purpose of including `AxisArray` as part of ezmsg stems from wanting to avoid having to support a vast array of different message types. This is a major cause of bloat with other similar messaging platforms. - -For this to be useful, we have designed `AxisArray` to be convenient and flexible. It is important that it can be used for the many different use cases we have encountered. At its core it stores a numpy N-dimensional array, an array of axis labels, as well as multiple metadata attributes. - - -|ezmsg_logo_small| Description -********************************* - -An ``AxisArray`` is a multi-dimensional array with named axes. Each axis can have a name and a set of labels for its elements. This allows for more intuitive indexing and manipulation of the data. - -An `AxisArray` has the following attributes: - -- ``data``: a numpy ndarray containing the actual data. -- ``dims``: a list of axis names. -- ``axes``: a dictionary mapping axis names to their label information. -- ``attrs``: a dictionary for storing additional metadata. -- ``key``: a unique identifier for the array. - -Unsurprisingly, all of this must be self-consistent: the number of axis names in ``dims`` must match the number of dimensions in ``data``, and the axis names in ``axes`` should match the ones in ``dims``. The label information in ``axes`` refers to the 'value' of each axis index, e.g., for a time axis, the labels might be timestamps. We provide three commonly used axes type objects: - -- A ``LinearAxis``: represents a linear axis with evenly spaced values - you just need the ``offset`` (start value) and the ``gain`` (step size). An example of this would be simple numerical index (offset=0, gain=1) or regularly spaced time samples (offset=start time, gain=1/sampling rate). -- A ``TimeAxis``: this is a `LinearAxis` that represents a time axis. Its ``unit`` attribute is by default set to seconds (s). -- A ``CoordinateAxis``: this is our continuous/dense axis, which can represent any continuous variable, such as frequency or spatial coordinates. You provide the actual values for each index in a ``data`` array of values. - -The `AxisArray` class provides several methods for manipulating and accessing the data, designed to allow standard numpy array manipulation without losing track of how the axes are affected. For a list of such methods, see :ref:`axisarray_methods`. - - -|ezmsg_logo_small| Recommended Use -********************************************** - - -When to use AxisArray -======================= - -If any of the following are true, then `AxisArray` is an appropriate messaging format: - -- When the underlying data is in array form -- When it is prudent to keep information about the data array axes (eg. labels, units, spacing) -- When you want to store the data with additional metadata (eg. data origin device name, study name, date, attempt number, etc.) -- If you plan to use the signal processing extension package `ezmsg-sigproc`. - -Anything that is inherently multi-dimensional and needs to be processed in a multi-dimensional context, ``AxisArray`` is the preferred message format in ezmsg. We have designed the ezmsg signal processing extension package `ezmsg-sigproc` with `AxisArray` in mind. - -We do not recommend using `AxisArray` for very simple data types like integers, floats, or strings. `AxisArray` has the flexibility to store this kind of data: this can be done by sending the data as a zero-dimensional array, or by sending an empty data field and storing the simple data in the ``attrs`` field. However, for the sake of both memory and time efficiency, we would recommend simply using the basic types (int, float, str) as the message type instead. - -.. tip:: One example of a convenient use of the metadata components of `AxisArray` is in the case of adaptive transformers: we need to be able to send trigger messages that tell the adaptive transformer to change state/configuration in some way. We can do this by having an element of ``attrs`` that is called ``"trigger"`` set to ``True`` along with whatever parameters of the transformer we would like to alter (and any data needed for computation of the new parameters). The transformer can then check for this ``"trigger"`` attribute in the incoming `AxisArray` messages and change its state accordingly. - -.. _axisarray_methods: - -`AxisArray` utility methods -============================= - -There are many built in methods for manipulating `AxisArray` objects, such as: - -- `as2d`: Get a 2D view of the data with the specified dimension as the first axis. -- `shape2d`: Calculate the 2D shape when viewing array with specified axis first. -- `slice_along_axis`: Slice the input array along a specified axis using the given slice object or integer index. -- `sliding_win_oneaxis`: Generates a view of an array using a sliding window of specified length along a specified axis of the input array. -- `view2d`: Context manager providing a 2D view of the data. - -The following are AxisArray class methods for manipulating the underlying data: - -- `concatenate`: Concatenate multiple AxisArray objects along a specified dimension. -- `isel`: Select data using integer-based indexing along specified dimensions. -- `iter_over_axis`: Iterate over slices of the data along a specified axis. -- `sel`: Select data using label-based indexing along specified dimensions. -- `to_xr_dataarray`: Convert the AxisArray to an xarray DataArray. -- `transpose`: Transpose the dimensions of an AxisArray. - -Further utility methods (for getting data views, axis information, and shapes): - -- `as2d`: Get a 2D view of the data with the specified dimension as the first axis. -- `ax`: Get `AxisInfo` for a specified dimension. -- `axis_idx`: Get the axis index for a given dimension name or pass through if already an int. -- `get_axis`: Get the axis coordinate system for a specified dimension. -- `get_axis_idx`: Get the axis index for a given dimension name. -- `get_axis_name`: Get the axis name for a given axis index. -- `shape`: Get the shape of the data array. -- `shape_2d`: Get the shape of the 2D view of the data with the specified dimension as the first axis. -- `view_2d`: Context manager providing a 2D view of the data. - - -How to return an AxisArray object -================================= - -To return an ``AxisArray`` object, you can create an instance of the ``AxisArray`` class and populate it with your data. - -There is some time cost in the creation of the ``AxisArray`` object. For performance-critical code, the preferred way is to use ``replace`` (imported from **ezmsg.util.messages.axisarray**) to modify the data as needed before returning the ``AxisArray`` object. One can think of this as similar to how one would use ``dataclasses.replace`` to create a new instance of a dataclass with some attributes changed. - -.. code-block:: python - - from ezmsg.util.messages.axisarray import LinearAxis, replace - - # Create some sample data - new_data = some_processing_function(message.data) - - # Define newly created axis - axis = LinearAxis(offset=0.0, gain=0.01, unit='s') - - # Create a new AxisArray message by replacing data and axes - msg_out = replace( - message, - data=new_data, - axes={ - **message.axes, - new_axis: axis, - }, - ) - -Calling ``ezmsg.util.messages.axisarray.replace()`` calls the utility function ``fast_replace`` (in `ezmsg.util.messages.util`) which is an optimized version of the standard python ``dataclasses.replace`` function. The optimization occurs due to skipping certain validation checks that would normally occur when initialising a dataclass. Specifically, unlike ``dataclasses.replace``, this function does not check for type compatibility, nor does it check that the passed in fields are valid fields for the dataclass and not flagged as ``init=False``. - -If you have concerns over this reduced safety, if you set the environment variable ``EZMSG_DISABLE_FAST_REPLACE=1``, then this imported ``replace`` function will simply be the function ``dataclasses.replace`` defined in the python standard `dataclasses` module. - -.. note:: Use of this purpose-made ``replace`` function is not limited to ``AxisArray`` objects. It can be used to create any dataclass object given an instance of said class, including user-defined dataclasses. An example of this can be seen in the tutorial :ref:`here `. - -|ezmsg_logo_small| See Also -******************************** - -#. :doc:`../reference/API/axisarray` -#. :doc:`sigproc` -#. :doc:`../tutorials/signalprocessing` - -.. :doc:`../how-tos/axisarray/content-axisarray` - -.. |ezmsg_logo_small| image:: ../_static/_images/ezmsg_logo.png - :width: 40 - :alt: ezmsg logo \ No newline at end of file diff --git a/docs/source/explanations/content-explanations.rst b/docs/source/explanations/content-explanations.rst deleted file mode 100644 index 0ec818ae..00000000 --- a/docs/source/explanations/content-explanations.rst +++ /dev/null @@ -1,107 +0,0 @@ -What is ezmsg? -##################################### - -.. under construction - Griffin to add content - -This section of the documentation aims to provide a comprehensive overview of the `ezmsg` framework from a design and decision-making perspective. These are four "explainer" documents for users to gain an understanding of the major parts of `ezmsg` and why they are implemented the way that they are. This page provides a high-level overview of the framework, its design philosophy, and motivations. The remaining pages delve into the design details of `ezmsg` core components, the in-built message format `AxisArray` and the signal processing tools provided in the `ezmsg-sigproc` extension to `ezmsg` respectively. - -.. toctree:: - :maxdepth: 1 - - ezmsg-design - axisarray - sigproc - -Other ways to learn about `ezmsg` include following our :doc:`Tutorial <../tutorials/content-tutorials>`, checking the list of :doc:`HOW TO pages <../how-tos/content-howtos>` and the :doc:`reference documentation <../reference/content-reference>`. - - -|ezmsg_logo_small| Description -******************************* - -`ezmsg` is a lightweight, flexible, and extensible framework for building signal processing pipelines and message passing. It has the following characteristics: - -- **Modular** design: `ezmsg` is built around a modular design philosophy, where individual components (called `Units`) can be developed, tested, and reused independently. This modularity allows users to easily assemble and rearrange complex processing pipelines by connecting different Units together. -- **Pub/sub messaging**: `ezmsg` utilizes a publish/subscribe messaging pattern. This pattern allows components that output (`publishers`) to simultaneously publish to multiple components that receive input (`subscribers`) and vice versa: subscribers to be simultaneously able to receive from multiple publishers. -- **Asynchronous processing**: `ezmsg` supports asynchronous processing, allowing components to operate independently and in parallel. In effect, the pipeline will not be blocked by a slow component or device that isn't ready to provide data, as the asynchronous control allows the pipeline to move on and return when it is ready. -- **Multi-process/multi-machine** capability: `ezmsg` is designed to support multi-process and multi-machine configurations, enabling distributed processing and scalability. This allows for greater flexibility in designing signal processing hardware architectures. -- Efficient **message communication protocols**: `ezmsg` employs efficient message communication protocols to minimize latency and maximize throughput. Unless otherwise specified, publishers and subscribers in the same process communicate using the local message cache. If publishers and subscribers are in different processes, `ezmsg` leverages Python's fast `shared memory` capabilities to facilitate communication. In a context where neither local, nor shared memory communication is possible, `ezmsg` leverages TCP for communication. -- Written in **Python**: `ezmsg` is implemented in Python, making it usable on a wide range of platforms, and hence, accessible and easy to use for a wide range of users, from researchers to engineers. Other oft-spoken benefits of Python are its simplicity and readability, and heavy adoption within the machine learning community. -- **Minimal boilerplate code** required for `ezmsg` components: `ezmsg` is designed to minimize the amount of boilerplate code required to create new components. This allows users to focus on the core functionality of their components rather than getting bogged down in implementation details. -- Provides an **in-built message format** (`AxisArray`): `AxisArray` is a message format for handling multi-dimensional arrays with labeled axes. It is designed to facilitate the organization, manipulation, and analysis of complex data structures commonly encountered in signal processing and related fields. See :doc:`AxisArray ` for more information. -- Provides a **command line interface**: `ezmsg` includes a command line interface (CLI) that allows users to manage and interact with signal processing pipelines. The CLI provides commands for starting, stopping, and visualising pipelines. -- Provides fundamental **signal processing units**: through the extension `ezmsg-sigproc`, users of `ezmsg` have access to over 20 in-built signal processing units, that can be used both in an `ezmsg` context as well as outside of it. See :doc:`sigproc ` for more information. -- **Extensible** via extensions: `ezmsg` is designed to be extensible, allowing users to create and share custom components and extensions. This extensibility enables users to tailor the framework to their specific needs and contribute to the broader `ezmsg` community. See :doc:`Extensions <../extensions/content-extensions>` for more information. -- **Open-source**: `ezmsg` is an open-source project, released under the permissive MIT license. This encourages collaboration and contributions from the community, fostering a vibrant ecosystem of users and developers. - -In Media -========= - -* `BCI Society 2025 Poster `_ -* `Society For Neuroscience 2024 Poster `_ - -Publications -======================= - -A collection of academic papers, journals, and other publications that have cited or utilized `ezmsg` in research and development. -These publications provide insights into the practical applications and impact of `ezmsg` in various fields. - -- `A click-based electrocorticographic brain-computer interface enables long-term high-performance switch-scan spelling `_ -- `Stable Decoding from a Speech BCI Enables Control for an Individual with ALS without Recalibration for 3 Months `_ - - -|ezmsg_logo_small| Motivation -******************************** - -There are many signal processing and message passing frameworks available that employ various approaches and techniques. The original authors of `ezmsg` wanted something akin to (and were inspired by) the directed acyclic graph pub/sub design used by `labgraph `_. So, `ezmsg` is a pure-Python implementation which is optimized and intended for use in constructing real-time software. `ezmsg` implements much of the `labgraph` API (with a few notable differences), and owes a lot of its design to the `labgraph` developers/project. - -On a fundamental level, the `ezmsg` implementation focused on the following major needs: - -- lightweight (minimal dependencies), Python-based framework -- modular, directed acyclic graph design of pipelines -- the backend implementation to manage process handling, message passing, and memory management -- `Unit`\ s to require minimal boilerplate code to implement -- easy to use and understand -- open-source and permissively licensed - - -Differences between `ezmsg` and other frameworks -=========================================================== - -To be expanded on later. - -|ezmsg_logo_small| Extensions -********************************* - -`ezmsg` is extensible and already includes several extensions. Here are some of the extensions we manage or are aware of: - -- `ezmsg-sigproc `_ -- Timeseries signal processing modules -- `ezmsg-websocket `_ -- Websocket server and client nodes for `ezmsg` graphs -- `ezmsg-zmq `_ -- ZeroMQ pub and sub nodes for `ezmsg` graphs -- `ezmsg-panel `_ -- Plotting tools for `ezmsg` that use `panel `_ -- `ezmsg-blackrock `_ -- Interface for Blackrock Cerebus ecosystem (incl. Neuroport) using `pycbsdk` -- `ezmsg-lsl `_ -- Source unit for LSL Inlet and sink unit for LSL Outlet -- `ezmsg-unicorn `_ -- g.tec Unicorn Hybrid Black integration for `ezmsg` -- `ezmsg-gadget `_ -- USB-gadget with HID control integration for Raspberry Pi (Zero/W/2W, 4, CM4) -- `ezmsg-openbci `_ -- OpenBCI Cyton serial interface for `ezmsg` -- `ezmsg-ssvep `_ -- Tools for running SSVEP experiments with `ezmsg` -- `ezmsg-vispy `_ -- `ezmsg` visualization toolkit using PyQt6 and vispy. - -See :doc:`Extensions page <../extensions/content-extensions>` for more information. - -|ezmsg_logo_small| Financial Support -**************************************** - -`ezmsg` is supported by Johns Hopkins University (JHU), the JHU Applied Physics Laboratory (APL), and by the Wyss Center for Bio and Neuro Engineering. - -|ezmsg_logo_small| See Also -******************************** - -#. :doc:`axisarray` -#. :doc:`sigproc` -#. :doc:`../tutorials/start` -#. :doc:`../how-tos/basics/content-basics` -#. :doc:`../how-tos/pipeline/content-pipeline` - -.. |ezmsg_logo_small| image:: ../_static/_images/ezmsg_logo.png - :width: 40 - :alt: ezmsg logo \ No newline at end of file diff --git a/docs/source/explanations/ezmsg-design.rst b/docs/source/explanations/ezmsg-design.rst deleted file mode 100644 index 802560e0..00000000 --- a/docs/source/explanations/ezmsg-design.rst +++ /dev/null @@ -1,260 +0,0 @@ -About `ezmsg` Architecture -############################# - - -|ezmsg_logo_small| Modular Design -********************************************** - -`ezmsg` is built around a modular design philosophy, where individual components (called `Unit`\ s) can be developed, tested, and reused independently. This modularity allows users to easily assemble and rearrange complex processing pipelines by connecting different `Unit`\ s together. - -To showcase both the simplicity and modularity of `ezmsg`, I will explain how to build a simple signal processing pipeline in two steps: - -1. creation of the individual `Unit`\ s -2. creation of the pipeline by connecting the `Unit`\ s together - -Step 1: Creation of the individual `Unit`\ s -============================================= - -The design details of `Unit`\ s are explained in more detail in :ref:`unit_details`, but we show the structure here. - -.. code-block:: python - - class MyUnitSettings(ez.Settings): - # optional dataclass containing configuration parameters for the Unit - - @dataclass - class MyUnitState: - # optional dataclass containing state variables for the Unit - - class MyUnit(ez.Unit): - INPUT = ez.InputStream(InputMessageType) - OUPUT = ez.OutputStream(OutputMessageType) - - SETTINGS = MyUnitSettings - STATE = MyUnitState - - @relevant_decorator - def method(self, msg: InputMessageType) -> OutputMessageType: - # processing logic here - -What we have done is: - -- an optional settings dataclass containing configuration parameters for the `Unit` -- an optional state dataclass containing state variables for the `Unit` -- the actual `Unit` is a class inheriting from ``ez.Unit`` containing: - - at least one input or output stream (here we have one of each) - - optional ``SETTINGS`` and ``STATE`` attributes referencing the relevant dataclasses - - one or more methods decorated with relevant decorators to define the processing logic of the `Unit` - -An example can be found in the :doc:`tutorial <../tutorials/pipeline>`. - -Step 2: Creation of the pipeline by connecting the `Unit`\ s together -=============================================================================== - -Once we have created the individual `Unit`\ s, we can connect them together to form a pipeline. We first define the nodes of our graph in terms of our `Unit`\ s, and then we define the connections between the nodes: - -.. code-block:: python - - components = { - "COUNT": Count(settings=CountSettings(iterations=10)), - "ADD_ONE": AddOne(), - "PRINT": PrintValue() - } - connections = ( - (components["COUNT"].OUTPUT_COUNT, components["ADD_ONE"].INPUT_COUNT), - (components["ADD_ONE"].OUTPUT_PLUS_ONE, components["PRINT"].INPUT) - ) - ez.run(components = components, connections = connections) - -What we have done is: - -- created a dictionary of components, where the keys are the names of the components and the values are instances of the `Unit`\ s (in this case, `Count`, `AddOne` and `PrintValue`) -- created a tuple of connections, where each connection is a tuple of the output stream of one component and the input stream of another component -- called ``ez.run()`` with the components and connections to start the pipeline - - -|ezmsg_logo_small| Backend Implementation -****************************************** - -.. include diagram of architecture here - -.. maybe include detailed process order diagram here - - -GraphServer -====================== - -When an `ezmsg` pipeline is started, `ezmsg` initializes a GraphServer, which does two main things: - -- spins up a process that keeps track of the graph state during execution. One GraphServer should be initialized per ezmsg pipeline. -- spins up a process that initialises a Shared Memory Server using Python's `multiprocessing.shared_memory` module and allocates blocks of memory to nodes of the pipeline graph. This is used for efficient inter-process message communication. - - -Pub/Sub Design -======================= - -When a pipeline is initialised `ezmsg` constructs a directed acyclic graph (DAG) of the nodes (usually `Unit`\ s) in the pipeline. Each `Unit` can have one or more input and output streams, which are used to receive and send messages respectively. `ezmsg` handles these input and output streams as instances of the `Subscriber` and `Publisher` classes. - -`Publisher` and `Subscriber` instances are created with a publisher/subscriber design architecture in mind. Each `Publisher` has a list of `Subscriber`\ s it can simultaneously publish to. Similarly, each `Subscriber` has a list of `Publisher`\ s it is listening to. This kind of architecture allows pipelines that are not simply a linear chain of nodes. - -Messaging between `Publisher`\ s and `Subscriber`\ s is facilitated by the `ezmsg GraphServer` through the use of an appropriate messaging protocol identified at initialisation. `Publisher`\ s and `Subscriber`\ s in the same process use local memory cache. Inter-process communication is managed by the shared memory process and otherwise communication is done via TCP. - - - -|ezmsg_logo_small| Command Line Interface -******************************************* - -The `ezmsg` command line interface exposes extra tools to manage a pipeline that is running in the background on a machine. Run - -.. code-block:: python - - ezmsg -h - -to see all the available options. - -Currently, one can use the CLI to: - -- start a pipeline in the background (``ezmsg serve --address :``) -- start a pipeline in the foreground (``ezmsg start --address :``) -- shutdown a pipeline that is running (``ezmsg shutdown --address :``) -- visualise a pipeline that is running (``ezmsg mermaid --address :`` or ``ezmsg graphviz --address :``) - - -.. _unit_details: - -|ezmsg_logo_small| Basic ezmsg building blocks -*********************************************** - -Basic building block in `ezmsg` is a `Unit`, which represents a discrete processing element within a pipeline. Users can create custom Units by subclassing the base Unit class and implementing the required processing logic. One can combine multiple `Unit`\ s to form a `Collection`, which functions much like a `Unit` does (one may want to abstract away complexity by having a `Collection` representing a logical grouping of `Unit`\ s). The following discussion applies as much to `Collection`\ s as it does to `Unit`\ s, so we will just refer to `Unit`\ s for simplicity. - -A `Unit` typically contains the following attributes/components: - -- ``SETTINGS``: Configuration parameters that define the behavior of the `Unit`. These can be set during initialization or modified at runtime - though they are usually chosen for the lifetime of the `Unit`. -- ``STATE``: Internal state variables that maintain the current status of the `Unit`. These can be updated during processing to reflect changes in the `Unit`\ s operation. Unlike the parameters in ``SETTINGS``, these are expected to change frequently during the lifetime of the `Unit`. -- input and output streams: Data channels through which the `Unit` receives input and sends output. These streams facilitate communication between different `Unit`\ s in a pipeline. -- processing methods: Functions that define the core processing logic of the `Unit`. These methods can be decorated to be invoked when data is received on the input streams, and produce output that is sent to the output streams. - - -SETTINGS -========================= - -This attribute is to be declared in the `Unit` in the format: - -.. code-block:: python - - SETTINGS = RelevantSettingsClass - -The capitalization is important as ezmsg reserves this attribute name for this purpose and this is critical for the backend implementation of the `Unit`. Notice that we do not instantiate the settings class here, we just provide a reference to the class. ezmsg will take care of instantiating the settings class when the pipeline is created or in some cases when it receives the first message. There must be at most one such attribute in a `Unit` or `Collection`. - - - -STATE -========================= - -This attribute is to be declared in the `Unit` in the format: - -.. code-block:: python - - STATE = RelevantStateClass - -As with ``SETTINGS``, the capitalization here is important as ezmsg reserves this attribute name for this purpose and this is critical for the backend implementation of the `Unit`. Notice that we do not instantiate the state class here, we just provide a reference to the class. ezmsg will take care of instantiating the state class when the pipeline is created or in some cases when it receives the first message. There must be at most one such attribute in a `Unit` or `Collection`. - -streams -========================= - -A unit must have at least one input or output stream. Streams are defined as class attributes in the `Unit` in the format: - -.. code-block:: python - - INPUT = ez.InputStream(MessageInType) - OUTPUT = ez.OutputStream(MessageOutType) - -.. note:: ``ez`` here refers to the typical import alias for ezmsg, i.e. ``import ezmsg.core as ez`` - -Unlike with ``SETTINGS`` and ``STATE``, the capitalization of the stream names and the names in fact are not reserved, though we recommend using something understandable. One can have as many input and output streams as needed in a `Unit` or `Collection`. The message types can be any type, and for signal processing purposes, we recommend our own implemented message type :doc:`AxisArray `. - -.. _decorators: - -`Unit` methods -========================= - -Typically, a `Unit` will have one or more methods that define its processing logic. There are a few in-built decorators that can be used to configure the behaviour of these methods. In particular, we use specific decorators to connect the method to a `Publisher` and `Subscriber` (defined as output and input streams in the `Unit` body). We can also provide configuration options defining in which process this method should sit. - -These are the available decorators and their function: - -.. list-table:: - :widths: 20 40 40 - :header-rows: 1 - :class: front_page_table - - * - Decorator - - Description - - Usage - * - ``@ez.subscriber`` - - This decorator is used to indicate that a method should be invoked when data is received on a specific input stream. The decorated method should take in the message as an argument. - - .. code-block:: python - - @ez.subscriber('INPUT') - def process(self, msg: MessageInType) -> None: - # processing logic here - * - ``@ez.publisher`` - - This decorator is used to indicate that a method should publish messages to a specific output stream. The decorated method should produce the message to be sent. - - .. code-block:: python - - @ez.publisher('OUTPUT') - def generate(self) -> MessageOutType: - # message generation logic here - return message - * - ``@ez.main`` - - This decorator is used to indicate that a method should run in the main process of the `Unit`. This is useful for methods that need to perform tasks that are not directly related to message processing, such as initialization or cleanup. - - .. code-block:: python - - @ez.main - def initialize(self) -> None: - # initialization logic here - * - ``@ez.task`` - - This decorator is used to indicate that a method should run in a separate task process. This is useful for methods that perform long-running or blocking operations, allowing the `Unit` to continue processing other messages. - - .. code-block:: python - - @ez.task - def long_running_task(self) -> None: - # long-running task logic here - * - ``@ez.process`` - - This decorator is used to indicate that a method should run in its own separate process. This is useful for methods that require isolation from the main process, such as CPU-intensive tasks or operations that may crash. - - .. code-block:: python - - @ez.process - def cpu_intensive_task(self) -> None: - # CPU-intensive task logic here - * - ``@ez.timeit`` - - This decorator is used to measure the execution time of a method. It can be useful for performance monitoring and optimization. - - .. code-block:: python - - @ez.timeit - def monitored_method(self) -> None: - # method logic here - - - -.. note:: The decorators ``@ez.subscriber`` and ``@ez.publisher`` can be used together on the same method if the method both processes incoming messages and produces outgoing messages. - - -|ezmsg_logo_small| AxisArray -********************************* - -Our preferred Message format is `AxisArray`. See :doc:`axisarray` for more information. - - - -|ezmsg_logo_small| See Also -******************************** - -#. :doc:`axisarray` -#. :doc:`sigproc` -#. :doc:`../tutorials/start` -#. :doc:`../how-tos/basics/content-basics` -#. :doc:`../how-tos/pipeline/content-pipeline` - -.. |ezmsg_logo_small| image:: ../_static/_images/ezmsg_logo.png - :width: 40 - :alt: ezmsg logo \ No newline at end of file diff --git a/docs/source/explanations/sigproc.rst b/docs/source/explanations/sigproc.rst deleted file mode 100644 index be5c673d..00000000 --- a/docs/source/explanations/sigproc.rst +++ /dev/null @@ -1,365 +0,0 @@ -About ezmsg-sigproc Signal Processors -########################################### - - -`ezmsg-sigproc` is an `ezmsg` extension that provides a template for building signal processing classes as well a way to easily convert to ezmsg ``Unit``\ s. - -It also comes with a collection of pre-built signal processing classes and relevant ezmsg Units that accomplish standard signal processing tasks and are designed to work seamlessly within the ``ezmsg`` framework. - -A list of available signal processors and ezmsg Units can be found in the `ezmsg-sigproc reference <../extensions/sigproc/content-sigproc>`_. - - -|ezmsg_logo_small| Rationale For Implementation -******************************************************** - -Providing a flexible and extensible framework for signal processing tasks makes it - -- easier for users to create custom signal processors -- easier for users to integrate with ezmsg and create ezmsg Units -- easier to create processing pipelines in the ``ezmsg`` ecosystem -- allows standalone use outside of an ezmsg context - - -|ezmsg_logo_small| How to decide which processor template to use? -****************************************************************** - -We use the term "processor" to refer to any class that processes signals. We then separate processors into types based on whether or not they receive input messages (typically signal data), send output messages, or both: - -- A producer sends output messages, but does not receive input -- A consumer receives input, but does not output -- A transformer receives input and sends output. - -Furthermore, if a processor of any type must maintain state between processing calls (e.g., filtering, modulation, etc.), it is considered a stateful processor. For example, a producer that is stateful is called a stateful producer. - -Additionally, we also consider adaptive stateful transformers, which are stateful transformers that adapt their internal state based on the input signal characteristics (e.g., adaptive filters). If we would like a transformer to be asynchronous in all calls, we would use an asynchronous transformer. - -The decision tree for this classification is as follows: - -.. graphviz:: - :align: center - - digraph signal_processor_decision_tree { - node [shape=box, style="rounded,filled", fillcolor="#f0f0f0", fontname="Arial"]; - edge [fontname="Arial"]; - - AMP [label="Multiple Processors?"]; - ARI [label="Receives Input?"]; - ACB [label="Single Chain / Branching?"]; - P [label="Producer", shape=diamond, fillcolor="#27f21cff"]; - APO [label="Produces Output?"]; - NBC [label="no base class", style="none"]; - ACRI [label="Receives Input?"]; - C [label="Consumer", shape=diamond, fillcolor="#27f21cff"]; - T [label="Transformer", shape=diamond, fillcolor="#27f21cff"]; - PS [label="Stateful?"]; - CS [label="Stateful?"]; - TS [label="Stateful?"]; - TSA [label="Adaptive?"]; - TSAF [label="Async First?"]; - CompositeProducer [style="none, filled", fillcolor="#effb1aff"]; - CompositeProcessor [style="none, filled", fillcolor="#effb1aff"]; - BaseProducer [style="none, filled", fillcolor="#effb1aff"]; - BaseStatefulProducer [style="none, filled", fillcolor="#effb1aff"]; - BaseConsumer [style="none, filled", fillcolor="#effb1aff"]; - BaseStatefulConsumer [style="none, filled", fillcolor="#effb1aff"]; - BaseTransformer [style="none, filled", fillcolor="#effb1aff"]; - BaseAdaptiveTransformer [style="none, filled", fillcolor="#effb1aff"]; - BaseStatefulTransformer [style="none, filled", fillcolor="#effb1aff"]; - BaseAsyncTransformer [style="none, filled", fillcolor="#effb1aff"]; - - AMP -> ARI [label="no"]; - AMP -> ACB [label="yes"]; - ARI -> P [label="no"]; - ARI -> APO [label="yes"]; - ACB -> NBC [label="branching"]; - ACB -> ACRI [label="single chain"]; - P -> PS; - APO -> C [label="no"]; - APO -> T [label="yes"]; - ACRI -> CompositeProducer [label="no"]; - ACRI -> CompositeProcessor [label="yes"]; - PS -> BaseProducer [label="no"]; - PS -> BaseStatefulProducer [label="yes"]; - C -> CS; - T -> TS; - CS -> BaseConsumer [label="no"]; - CS -> BaseStatefulConsumer [label="yes"]; - TS -> BaseTransformer [label="no"]; - TS -> TSA [label="yes"]; - TSA -> TSAF [label="no"]; - TSA -> BaseAdaptiveTransformer [label="yes"]; - TSAF -> BaseStatefulTransformer [label="no"]; - TSAF -> BaseAsyncTransformer [label="yes"]; - } - -The leaf nodes in yellow are abstract base classes provided in `ezmsg.sigproc.base` for implementing standalone processors. The table below summarizes these base classes. - -|ezmsg_logo_small| Abstract implementations (Base Classes) for standalone processors -*************************************************************************************** - - -Generic TypeVars -=================== - -In this table, we summarize the generic TypeVars used in the processor class protocols and abstract base classes provided in `ezmsg.sigproc.base`. - -.. list-table:: - :widths: 5 20 30 - :header-rows: 1 - - * - Idx - - Class - - Description - * - 1 - - `MessageInType` (Mi) - - for messages passed to a consumer, processor, or transformer - * - 2 - - `MessageOutType` (Mo) - - for messages returned by a producer, processor, or transformer - * - 3 - - `SettingsType` - - bound to ``ez.Settings`` - * - 4 - - `StateType` (St) - - bound to ``ProcessorState`` which is simply ``ez.State`` with a ``hash: int`` field. - - -Processor Class Protocols -=========================== -In this table, we summarize the processor class protocols used to define the abstract base classes provided in `ezmsg.sigproc.base`. Each protocol corresponds to a specific processor type and characteristics as outlined in the decision tree above. - -+-----+-----------------------+--------+-------+------------------------+--------+-----------------+ -| Idx | Class | Parent | State | ``__call__`` signature | @state | ``partial_fit`` | -+=====+=======================+========+=======+========================+========+=================+ -| 1 | `Processor` | \- | No | Any -> Any | \- | \- | -+-----+-----------------------+--------+-------+------------------------+--------+-----------------+ -| 2 | `Producer` | \- | No | None -> Mo | \- | \- | -+-----+-----------------------+--------+-------+------------------------+--------+-----------------+ -| 3 | `Consumer` | 1 | No | Mi -> None | \- | \- | -+-----+-----------------------+--------+-------+------------------------+--------+-----------------+ -| 4 | `Transformer` | 1 | No | Mi -> Mo | \- | \- | -+-----+-----------------------+--------+-------+------------------------+--------+-----------------+ -| 5 | `StatefulProcessor` | \- | Yes | Any -> Any | Y | \- | -+-----+-----------------------+--------+-------+------------------------+--------+-----------------+ -| 6 | `StatefulProducer` | \- | Yes | None -> Mo | Y | \- | -+-----+-----------------------+--------+-------+------------------------+--------+-----------------+ -| 7 | `StatefulConsumer` | 5 | Yes | Mi -> None | Y | \- | -+-----+-----------------------+--------+-------+------------------------+--------+-----------------+ -| 8 | `StatefulTransformer` | 5 | Yes | Mi -> Mo | Y | \- | -+-----+-----------------------+--------+-------+------------------------+--------+-----------------+ -| 9 | `AdaptiveTransformer` | 8 | Yes | Mi -> Mo | Y | Y | -+-----+-----------------------+--------+-------+------------------------+--------+-----------------+ - -Note: ``__call__`` and ``partial_fit`` both have asynchronous alternatives: ``__acall__`` and ``apartial_fit`` respectively. - - -Processor Base Classes -======================== - -In this table, we summarize the abstract base classes provided in `ezmsg.sigproc.base` for implementing standalone signal processors. Each base class corresponds to a specific processor type and protocol, as outlined in the decision tree above. - -.. list-table:: - :widths: 5 20 5 5 30 - :header-rows: 1 - - * - Idx - - Class - - Parent - - Protocol - - Features - * - 1 - - ``BaseProcessor`` - - \- - - 1 - - ``__init__`` for settings; ``__call__`` (alias: ``send``) wraps abstract ``_process``. - * - 2 - - ``BaseProducer`` - - \- - - 2 - - Similar to ``BaseProcessor``; ``next``/``anext`` instead of ``send``/``asend`` aliases. async first! - * - 3 - - ``BaseConsumer`` - - 1 - - 3 - - Overrides return type to None. - * - 4 - - ``BaseTransformer`` - - 1 - - 4 - - Overrides input and return types. - * - 5 - - ``BaseStatefulProcessor`` - - 1 - - 5 - - ``state`` setter unpickles arg; ``stateful_op`` wraps ``__call__``. - * - 6 - - ``BaseStatefulProducer`` - - 2 - - 6 - - ``state`` setter and getter; ``stateful_op`` wraps ``__call__`` which runs ``__acall__``. - * - 7 - - ``BaseStatefulConsumer`` - - 5 - - 7 - - Overrides return type to None. - * - 8 - - ``BaseStatefulTransformer`` - - 5 - - 8 - - Overrides input and return types. - * - 9 - - ``BaseAdaptiveTransformer`` - - 8 - - 9 - - Implements ``partial_fit``. ``__call__`` may call ``partial_fit`` if message has ``.trigger``. - * - 10 - - ``BaseAsyncTransformer`` - - 8 - - 8 - - ``__acall__`` wraps abstract ``_aprocess``; ``__call__`` runs ``__acall__``. - * - 11 - - ``CompositeProcessor`` - - 1 - - 5 - - Methods iterate over sequence of processors created in ``_initialize_processors``. - * - 12 - - ``CompositeProducer`` - - 2 - - 6 - - Similar to ``CompositeProcessor``, but first processor must be a producer. - -NOTES: - -1. Producers do not inherit from ``BaseProcessor``, so concrete implementations should subclass ``BaseProducer`` or ``BaseStatefulProducer``. -2. For concrete implementations of non-producer processors, inherit from the base subclasses of ``BaseProcessor`` (eg. ``BaseConsumer``, ``BaseTransformer``) and from base subclasses of ``BaseStatefulProcessor``. These two processor classes are primarily used for efficient abstract base class construction. -3. For most base classes, the async methods simply call the synchronous methods where the processor logic is expected. Exceptions are ``BaseProducer`` (and its children) and ``BaseAsyncTransformer`` which are async-first and should be strongly considered for operations that are I/O bound. -4. For async-first classes, the logic is implemented in the async methods and the sync methods are thin wrappers around the async methods. The wrapper uses a helper method called ``run_coroutine_sync`` to run the async method in a synchronous context, but this adds some noticeable processing overhead. -5. If you need to call your processor outside ezmsg (which uses async), and you cannot easily add an async context* in your processing, then you might want to consider duplicating the processor logic in the sync methods. - - .. note:: Jupyter notebooks are async by default, so you can await async code in a notebook without any extra setup. - -6. ``CompositeProcessor`` and ``CompositeProducer`` are stateful, and structurally subclass the ``StatefulProcessor`` and ``StatefulProducer`` protocols, but they -do not inherit from ``BaseStatefulProcessor`` and ``BaseStatefulProducer``. They accomplish statefulness by inheriting from the mixin abstract base class ``CompositeStateful``, which implements the state related methods: ``get_state_type``, ``state.setter``, ``state.getter``, ``_hash_message``, ``_reset_state``, and ``stateful_op`` (as well as composite processor chain related methods). However, ``BaseStatefulProcessor``, ``BaseStatefulProducer`` implement ``stateful_op`` method for a single processor in an incompatible way to what is required for composite chains of processors. - - -|ezmsg_logo_small| Implementing a custom standalone processor -**************************************************************** - -1. Create a new settings dataclass: ``class MySettings(ez.Settings):`` -2. Create a new state dataclass: - -.. code-block:: python - - @processor_state - class MyState: - -3. Decide on your base processor class, considering the protocol, whether it should be async-first, and other factors using the decision tree above. - -4. Implement the child class. - * The minimum implementation is ``_process`` for sync processors, ``_aprocess`` for async processors, and ``_produce`` for producers. - * For any stateful processor, implement ``_reset_state``. - * For stateful processors that need to respond to a change in the incoming data, implement ``_hash_message``. - * For adaptive transformers, implement ``partial_fit``. - * For chains of processors (``CompositeProcessor``/ ``CompositeProducer``), need to implement ``_initialize_processors``. - * See processors in `ezmsg.sigproc` for examples. -5. Override non-abstract methods if you need special behaviour. For example: - * ``WindowTransformer`` overrides ``__init__`` to do some sanity checks on the provided settings. - * ``TransposeTransformer`` and ``WindowTransformer`` override ``__call__`` to provide a passthrough shortcut when the settings allow for it. - * ``ClockProducer`` overrides ``__call__`` in order to provide a synchronous call bypassing the default async behaviour. - - -|ezmsg_logo_small| Abstract implementations (Base Classes) for ezmsg Units using processors -********************************************************************************************** - -Generic TypeVars for ezmsg Units -================================== - -.. list-table:: - :widths: 5 20 30 - :header-rows: 1 - - * - Idx - - Class - - Description - * - 5 - - ``ProducerType`` - - bound to ``BaseProducer`` (hence, also ``BaseStatefulProducer``, ``CompositeProducer``) - * - 6 - - ``ConsumerType`` - - bound to ``BaseConsumer``, ``BaseStatefulConsumer`` - * - 7 - - ``TransformerType`` - - bound to ``BaseTransformer``, ``BaseStatefulTransformer``, ``CompositeProcessor`` (hence, also ``BaseAsyncTransformer``) - * - 8 - - ``AdaptiveTransformerType`` - - bound to ``BaseAdaptiveTransformer`` - - -Base Classes for ezmsg processor Units: -============================================================================== - -+-----+---------------------------------+---------+-----------------------------+ -| Idx | Class | Parents | Expected TypeVars | -+=====+=================================+=========+=============================+ -| 1 | ``BaseProcessorUnit`` | \- | \- | -+-----+---------------------------------+---------+-----------------------------+ -| 2 | ``BaseProducerUnit`` | \- | ``ProducerType`` | -+-----+---------------------------------+---------+-----------------------------+ -| 3 | ``BaseConsumerUnit`` | 1 | ``ConsumerType`` | -+-----+---------------------------------+---------+-----------------------------+ -| 4 | ``BaseTransformerUnit`` | 1 | ``TransformerType`` | -+-----+---------------------------------+---------+-----------------------------+ -| 5 | ``BaseAdaptiveTransformerUnit`` | 1 | ``AdaptiveTransformerType`` | -+-----+---------------------------------+---------+-----------------------------+ - -Note, it is strongly recommended to use `BaseConsumerUnit`, `BaseTransformerUnit`, or `BaseAdaptiveTransformerUnit` for implementing concrete subclasses rather than `BaseProcessorUnit`. - -|ezmsg_logo_small| How to implement a custom ezmsg Unit from a standalone processor -===================================================================================== - -1. Create and test custom standalone processor as above. -2. Decide which base unit to implement. - * Use the "Generic TypeVars for ezmsg Units" table above to determine the expected TypeVar. - * Find the Expected TypeVar in the "ezmsg Units" table. -3. Create the derived class. - -Often, all that is required is the following (e.g., for a custom transformer): - -.. code-block:: python - - import ezmsg.core as ez - from ezmsg.util.messages.axisarray import AxisArray - from ezmsg.sigproc.base import BaseTransformer, BaseTransformerUnit - - - class CustomTransformerSettings(ez.Settings): - ... - - - class CustomTransformer(BaseTransformer[CustomTransformerSettings, AxisArray, AxisArray]): - def _process(self, message: AxisArray) -> AxisArray: - # Your processing code here... - return message - - - class CustomUnit(BaseTransformerUnit[ - CustomTransformerSettings, # SettingsType - AxisArray, # MessageInType - AxisArray, # MessageOutType - CustomTransformer, # TransformerType - ]): - SETTINGS = CustomTransformerSettings - - -.. note:: The type of ProcessorUnit is based on the internal processor and not the input or output of the unit. Input streams are allowed in ProducerUnits and output streams in ConsumerUnits. For an example of such a use case, see ``BaseCounterFirstProducerUnit`` and its subclasses. ``BaseCounterFirstProducerUnit`` has an input stream that receives a flag signal from a clock that triggers a call to the internal producer. - -|ezmsg_logo_small| See Also -******************************** - -1. `Signal Processor Documentation `_ -#. `Signal Processing Tutorial <../../tutorials/signalprocessing.html>`_ -#. `Signal Processing HOW TOs <../../how-tos/signalprocessing/main.html>`_ - -.. |ezmsg_logo_small| image:: ../_static/_images/ezmsg_logo.png - :width: 40 - :alt: ezmsg logo \ No newline at end of file diff --git a/docs/source/extensions/content-extensions.rst b/docs/source/extensions/content-extensions.rst deleted file mode 100644 index 818617cd..00000000 --- a/docs/source/extensions/content-extensions.rst +++ /dev/null @@ -1,46 +0,0 @@ -List of Extensions -####################### - -`ezmsg` extensions can be installed individually or all at once. To install all the extension packages in one go, you can use the following command: - -.. code-block:: bash - - pip install "ezmsg[all_ext]" - - -This will install all the available public extension packages for `ezmsg` that are listed in `pyproject.toml`. -If you prefer to install a subset of extension packages, you can use the following command: - -.. code-block:: bash - - pip install "ezmsg[zmq,sigproc,...]" - -Please note that the `ezmsg` package itself can still be installed without any additional extensions using `pip install ezmsg`. - -Extensions can be managed manually as well. Here are some of the extensions we manage or are aware of: - -- `ezmsg-sigproc `_ -- Timeseries signal processing modules -- `ezmsg-websocket `_ -- Websocket server and client nodes for `ezmsg` graphs -- `ezmsg-zmq `_ -- ZeroMQ pub and sub nodes for `ezmsg` graphs -- `ezmsg-panel `_ -- Plotting tools for `ezmsg` that use `panel `_ -- `ezmsg-blackrock `_ -- Interface for Blackrock Cerebus ecosystem (incl. Neuroport) using `pycbsdk` -- `ezmsg-lsl `_ -- Source unit for LSL Inlet and sink unit for LSL Outlet -- `ezmsg-unicorn `_ -- g.tec Unicorn Hybrid Black integration for `ezmsg` -- `ezmsg-gadget `_ -- USB-gadget with HID control integration for Raspberry Pi (Zero/W/2W, 4, CM4) -- `ezmsg-openbci `_ -- OpenBCI Cyton serial interface for `ezmsg` -- `ezmsg-ssvep `_ -- Tools for running SSVEP experiments with `ezmsg` -- `ezmsg-vispy `_ -- `ezmsg` visualization toolkit using PyQt6 and vispy. - -|ezmsg_logo_small| Extension API References -*********************************************** - -Further details are available for the following extensions: - -.. toctree:: - :maxdepth: 2 - - sigproc/content-sigproc - -.. |ezmsg_logo_small| image:: ../_static/_images/ezmsg_logo.png - :width: 40 - :alt: ezmsg logo diff --git a/docs/source/extensions/sigproc/base.rst b/docs/source/extensions/sigproc/base.rst deleted file mode 100644 index 7d86330a..00000000 --- a/docs/source/extensions/sigproc/base.rst +++ /dev/null @@ -1,65 +0,0 @@ -Base Processors -======================================== - -Here is the API for the base processors included in the `ezmsg-sigproc` extension. For more detailed information on the design decisions behind these base processors, please refer to the :doc:`ezmsg-sigproc explainer <../../explanations/sigproc>`. - - -.. autoclass:: ezmsg.sigproc.base.BaseProcessor - :members: - :show-inheritance: - :inherited-members: - -.. autoclass:: ezmsg.sigproc.base.BaseProducer - :members: - :show-inheritance: - :inherited-members: - -.. autoclass:: ezmsg.sigproc.base.BaseConsumer - :members: - :show-inheritance: - :inherited-members: - -.. autoclass:: ezmsg.sigproc.base.BaseTransformer - :members: - :show-inheritance: -:inherited-members: - -.. autoclass:: ezmsg.sigproc.base.BaseStatefulProcessor - :members: - :show-inheritance: - :inherited-members: - -.. autoclass:: ezmsg.sigproc.base.BaseStatefulProducer - :members: - :show-inheritance: - :inherited-members: - -.. autoclass:: ezmsg.sigproc.base.BaseStatefulConsumer - :members: - :show-inheritance: - :inherited-members: - -.. autoclass:: ezmsg.sigproc.base.BaseStatefulTransformer - :members: - :show-inheritance: - :inherited-members: - -.. autoclass:: ezmsg.sigproc.base.BaseAdaptiveTransformer - :members: - :show-inheritance: - :inherited-members: - -.. autoclass:: ezmsg.sigproc.base.BaseAsyncTransformer - :members: - :show-inheritance: - :inherited-members: - -.. autoclass:: ezmsg.sigproc.base.CompositeProcessor - :members: - :show-inheritance: - :inherited-members: - -.. autoclass:: ezmsg.sigproc.base.CompositeProducer - :members: - :show-inheritance: - :inherited-members: diff --git a/docs/source/extensions/sigproc/content-sigproc.rst b/docs/source/extensions/sigproc/content-sigproc.rst deleted file mode 100644 index 79c9d5eb..00000000 --- a/docs/source/extensions/sigproc/content-sigproc.rst +++ /dev/null @@ -1,22 +0,0 @@ -ezmsg-sigproc -=============== - -Timeseries signal processing implementations in ezmsg, leveraging numpy and scipy. -Most of the methods and classes in this extension are intended to be used in building signal processing pipelines. -They use :class:`ezmsg.util.messages.axisarray.AxisArray` as the primary data structure for passing signals between components. -The message's data are expected to be a numpy array. - -.. note:: Some generators might yield valid :class:`AxisArray` messages with ``.data`` size of 0. -This may occur when the generator receives inadequate data to produce a valid output, such as when windowing or buffering. - -`ezmsg-sigproc` contains two types of modules: - -- base processors and units that provide fundamental building blocks for signal processing pipelines -- in-built signal processing modules that implement common signal processing techniques - -.. toctree:: - :maxdepth: 1 - - base - units - processors diff --git a/docs/source/extensions/sigproc/processors.rst b/docs/source/extensions/sigproc/processors.rst deleted file mode 100644 index dff5ca35..00000000 --- a/docs/source/extensions/sigproc/processors.rst +++ /dev/null @@ -1,142 +0,0 @@ -In-Built Signal Processing Modules -====================================================== - -Here is the API reference for the in-built signal processing modules included in the `ezmsg-sigproc` extension. - -ezmsg.sigproc.activation --------------------------- - -.. automodule:: ezmsg.sigproc.activation - :members: - - -ezmsg.sigproc.affinetransform -------------------------------- - -.. automodule:: ezmsg.sigproc.affinetransform - :members: - - -ezmsg.sigproc.aggregate -------------------------- - -.. automodule:: ezmsg.sigproc.aggregate - :members: - :undoc-members: - - -ezmsg.sigproc.bandpower -------------------------- - -.. automodule:: ezmsg.sigproc.bandpower - :members: - - -ezmsg.sigproc.filter ----------------------- - -.. automodule:: ezmsg.sigproc.filter - :members: - -ezmsg.sigproc.butterworthfilter ---------------------------------- - -.. automodule:: ezmsg.sigproc.butterworthfilter - :members: - - -ezmsg.sigproc.decimate ------------------------- - -.. automodule:: ezmsg.sigproc.decimate - :members: - - -ezmsg.sigproc.downsample --------------------------- - -.. automodule:: ezmsg.sigproc.downsample - :members: - - -ezmsg.sigproc.ewmfilter -------------------------- - -.. automodule:: ezmsg.sigproc.ewmfilter - :members: - - -ezmsg.sigproc.math ------------------------ - -.. automodule:: ezmsg.sigproc.math.clip - :members: - -.. automodule:: ezmsg.sigproc.math.difference - :members: - -.. automodule:: ezmsg.sigproc.math.invert - :members: - -.. automodule:: ezmsg.sigproc.math.log - :members: - -.. automodule:: ezmsg.sigproc.math.scale - :members: - - -ezmsg.sigproc.sampler ------------------------ - -.. automodule:: ezmsg.sigproc.sampler - :members: - - -ezmsg.sigproc.scaler ----------------------- - -.. automodule:: ezmsg.sigproc.scaler - :members: - - -ezmsg.sigproc.signalinjector ------------------------------- - -.. automodule:: ezmsg.sigproc.signalinjector - :members: - - -ezmsg.sigproc.slicer ------------------------ - -.. automodule:: ezmsg.sigproc.slicer - :members: - - -ezmsg.sigproc.spectrum ------------------------- - -.. automodule:: ezmsg.sigproc.spectrum - :members: - :undoc-members: - - -ezmsg.sigproc.spectrogram ---------------------------- - -.. automodule:: ezmsg.sigproc.spectrogram - :members: - - -ezmsg.sigproc.synth ---------------------- - -.. automodule:: ezmsg.sigproc.synth - :members: - - -ezmsg.sigproc.window ----------------------- - -.. automodule:: ezmsg.sigproc.window - :members: \ No newline at end of file diff --git a/docs/source/extensions/sigproc/units.rst b/docs/source/extensions/sigproc/units.rst deleted file mode 100644 index 322e8927..00000000 --- a/docs/source/extensions/sigproc/units.rst +++ /dev/null @@ -1,29 +0,0 @@ -Base Processor Units -============================= - -Here is the API for the base processor ezmsg ``Unit``\ s included in the `ezmsg-sigproc` extension. For more detailed information on the design decisions behind these base units, please refer to the :doc:`ezmsg-sigproc explainer <../../explanations/sigproc>`. - -.. autoclass:: ezmsg.sigproc.base.BaseProducerUnit - :members: - :show-inheritance: - :inherited-members: - -.. autoclass:: ezmsg.sigproc.base.BaseProcessorUnit - :members: - :show-inheritance: - :inherited-members: - -.. autoclass:: ezmsg.sigproc.base.BaseConsumerUnit - :members: - :show-inheritance: - :inherited-members: - -.. autoclass:: ezmsg.sigproc.base.BaseTransformerUnit - :members: - :show-inheritance: - :inherited-members: - -.. autoclass:: ezmsg.sigproc.base.BaseAdaptiveTransformerUnit - :members: - :show-inheritance: - :inherited-members: diff --git a/docs/source/how-tos/advanced/content-advanced.rst b/docs/source/how-tos/advanced/content-advanced.rst deleted file mode 100644 index c82d40c1..00000000 --- a/docs/source/how-tos/advanced/content-advanced.rst +++ /dev/null @@ -1,10 +0,0 @@ -Advanced ezmsg HOW TOs -########################## - -.. toctree:: - :maxdepth: 1 - - online_offline - jupyter - performance - diff --git a/docs/source/how-tos/advanced/jupyter.rst b/docs/source/how-tos/advanced/jupyter.rst deleted file mode 100644 index 5efaadb0..00000000 --- a/docs/source/how-tos/advanced/jupyter.rst +++ /dev/null @@ -1,4 +0,0 @@ -How to integrate ezmsg with Jupyter notebooks? -################################################ - -(under construction) \ No newline at end of file diff --git a/docs/source/how-tos/advanced/online_offline.rst b/docs/source/how-tos/advanced/online_offline.rst deleted file mode 100644 index 96a045ff..00000000 --- a/docs/source/how-tos/advanced/online_offline.rst +++ /dev/null @@ -1,4 +0,0 @@ -How to convert an online ezmsg pipeline for offline analysis? -############################################################### - -(under construction) \ No newline at end of file diff --git a/docs/source/how-tos/advanced/performance.rst b/docs/source/how-tos/advanced/performance.rst deleted file mode 100644 index 5954af4f..00000000 --- a/docs/source/how-tos/advanced/performance.rst +++ /dev/null @@ -1,4 +0,0 @@ -How to profile the performance of a signal processor / method /ezmsg Unit? -################################################################################ - -(under construction) \ No newline at end of file diff --git a/docs/source/how-tos/axisarray/content-axisarray.rst b/docs/source/how-tos/axisarray/content-axisarray.rst deleted file mode 100644 index f846160e..00000000 --- a/docs/source/how-tos/axisarray/content-axisarray.rst +++ /dev/null @@ -1,10 +0,0 @@ -AxisArray HOW TOs -########################## - -.. toctree:: - :maxdepth: 1 - - convert - modify - nwb - diff --git a/docs/source/how-tos/axisarray/convert.rst b/docs/source/how-tos/axisarray/convert.rst deleted file mode 100644 index 5006b6e4..00000000 --- a/docs/source/how-tos/axisarray/convert.rst +++ /dev/null @@ -1,4 +0,0 @@ -How do I convert a Numpy array / pandas DataFrame to an AxisArray? -##################################################################### - -(under construction) \ No newline at end of file diff --git a/docs/source/how-tos/axisarray/modify.rst b/docs/source/how-tos/axisarray/modify.rst deleted file mode 100644 index 3e95416b..00000000 --- a/docs/source/how-tos/axisarray/modify.rst +++ /dev/null @@ -1,4 +0,0 @@ -How do I modify an existing AxisArray? -############################################## - -(under construction) \ No newline at end of file diff --git a/docs/source/how-tos/axisarray/nwb.rst b/docs/source/how-tos/axisarray/nwb.rst deleted file mode 100644 index a17fe9c3..00000000 --- a/docs/source/how-tos/axisarray/nwb.rst +++ /dev/null @@ -1,4 +0,0 @@ -How do I convert AxisArrays to/from NWB files? -################################################# - -(under construction) \ No newline at end of file diff --git a/docs/source/how-tos/basics/commandline.rst b/docs/source/how-tos/basics/commandline.rst deleted file mode 100644 index f2d4795f..00000000 --- a/docs/source/how-tos/basics/commandline.rst +++ /dev/null @@ -1,83 +0,0 @@ -How to run ezmsg from the command line? -########################################## - -In order to run an ezmsg pipeline from the command line, you need a python script with the following three things: - -1. a dictionary of components (Units or Collections) -2. a tuple of connections between (the input and output streams of) the components -3. a call to ``ezmsg.core.run()`` with the components and connections as arguments. - -The ezmsg pipeline is a graph defined by these components and connections. The ``run()`` function is the entry point to start the pipeline. - -When we use the command line to run an ezmsg pipeline, we can choose the address of the backend server (`GraphServer`) that will manage the pipeline execution. This is useful when we want to run multiple pipelines on the same machine or when we want to run a pipeline on a remote machine. - - -|ezmsg_logo_small| Run the pipeline using the command line -************************************************************ - -The ``ezmsg`` command line interface exposes extra tools to manage a pipeline that is running in the background on a machine. Run ``ezmsg -h`` to see all the available options. Currently, there are five commands available: ``start``, ``serve``, ``shutdown``, ``mermaid``, and ``graphviz``. The first three are used to manage the ezmsg system, while the last two are used for visualising the pipeline. - - -Step 1. Start the ezmsg system -================================= - -Paste and run the following in your terminal: - -.. code-block:: bash - - ezmsg serve --address 127.0.0.1:25978 - -This command will start the ezmsg system and listen for incoming connections on host `127.0.0.1` at port `25978`. You can change the address and port as needed - the values here are actually the default that `ezmsg` will use if you leave out the ``--address`` option. - -.. note:: You will notice that with the command ``serve``, ezmsg takes over your terminal in the sense that it runs ezmsg in the foreground, not allowing you to use this terminal for other purposes. In order to continue using the terminal, you will need to open a new terminal (you can have multiple terminals open). If instead you use the command ``start``, ezmsg will be forked to another process allowing you to use the current terminal. This is useful for running ezmsg in the background or as a service: - - .. code-block:: bash - - ezmsg start --address 127.0.0.1:25978 - - -Step 2. Run the pipeline in ezmsg -================================== - -Now that an ezmsg instance is running one can connect and run a pipeline to it. -You will need to change the way you call ``ez.run()`` by adding an address to the call. - -In your script, change the ``run()`` call to include the address from Step 1. For example: - -.. code-block:: python - - ez.run(components=components, connections=connections, address="127.0.0.1:25978") - -Now you can run your pipeline script from a **new** terminal window. - -.. code-block:: bash - - python3 my_pipeline.py - - -Step 3. Shutdown ezmsg instance -================================= - -When you are done with ezmsg, you can shutdown the ezmsg system by running the following command in a new terminal window: - -.. code-block:: bash - - ezmsg shutdown --address 127.0.0.1:25978 - -This will gracefully shutdown the ezmsg system and release any resources it was using. Make sure to use the same address you used to start and run the ezmsg system. - -.. note:: If you used the ``serve`` command to start the ezmsg system, after running the ``shutdown`` command, you will see that the terminal where you started the ezmsg system will return to the command prompt. - -|ezmsg_logo_small| What happens under the hood? -************************************************************ - -What actually happens when you run a script with an ezmsg pipeline and a ``run()`` function is that ezmsg starts a backend server called a **`GraphServer`**. This `GraphServer` manages the operation of any ezmsg pipeline connected to it. The naming comes from the fact that ezmsg pipelines are simply nodes - Units or Collections - connected in a :term:`directed acyclic graph` (:term:`DAG`\ ). - -The `GraphServer` is coupled with a **`SharedMemoryServer`** that allows for efficient data transfer between the different components of the pipeline. This is particularly useful for high-throughput applications where low latency is crucial. It utilises the in-built Python ``multiprocessing.shared_memory`` library to handle the data transfer between processes. - -The reverse is true too: you can run ezmsg instance (start a `GraphServer`) without a pipeline, but until you attach an ezmsg pipeline nothing interesting will happen. - - -.. |ezmsg_logo_small| image:: ../../_static/_images/ezmsg_logo.png - :width: 40 - :alt: ezmsg logo diff --git a/docs/source/how-tos/basics/content-basics.rst b/docs/source/how-tos/basics/content-basics.rst deleted file mode 100644 index bab86c8a..00000000 --- a/docs/source/how-tos/basics/content-basics.rst +++ /dev/null @@ -1,13 +0,0 @@ -ezmsg System Basics HOW TOs -############################ - -.. toctree:: - :maxdepth: 1 - - install - update - commandline - sandbox - jupyter - visualisation - diff --git a/docs/source/how-tos/basics/install.rst b/docs/source/how-tos/basics/install.rst deleted file mode 100644 index 02d9d32e..00000000 --- a/docs/source/how-tos/basics/install.rst +++ /dev/null @@ -1,105 +0,0 @@ -How to install ezmsg? -########################### - - -You can install ezmsg in two main ways: using :term:`pip` to install from the Python Package Index (:term:`PyPI`) or by cloning the source code from the GitHub repository. The first method is described below and the second method is described in :doc:`../../developer/install`. Use the first method if you are using ezmsg and the latter if you plan to develop for ezmsg. - -|ezmsg_logo_small| System Requirements -*************************************** - -ezmsg is written in and for **Python**, so it requires a Python interpreter to run. Currently, ezmsg requires at minimum Python 3.10. Beyond that, ezmsg is a pure Python library with no external dependencies. - -This also means that ezmsg is cross-platform and should run on any operating system that supports Python, including Windows, macOS, and Linux. - -In Windows and macOS, Python can be downloaded from the official website: `python.org `_. For newer users, we recommend using the `Anaconda `_ distribution of Python, which comes with a package manager and many useful libraries pre-installed. - -On Linux, Python is usually pre-installed, but you can also install it using your package manager in your :term:`terminal`: - -.. code-block:: bash - - # For Debian/Ubuntu-based systems - sudo apt install python3. python3-pip -y - - # For Red Hat/CentOS-based systems - sudo dnf install python3. python3-pip -y - - # For Arch Linux-based systems - sudo pacman -S python3. python-pipx - -where ``3.`` is the version of Python you want to install (e.g. 3.12). - -Whichever you choose, ensure that you have Python 3.8 or later installed on your system. You can verify your Python version by running in your terminal: - -.. code-block:: bash - - python3 --version - - -|ezmsg_logo_small| Installing ezmsg -************************************ - -We can install ezmsg using :term:`pip`, which is the package manager for Python. It will handle all dependencies and ensure that you have the latest version. It will download the package from the Python Package Index (:term:`PyPI`) and install it on your system. To install the ezmsg framework, run the following code in your terminal: - -.. code-block:: bash - - pip install ezmsg - - - -.. _updating-ezmsg: - -|ezmsg_logo_small| How to update ezmsg -*************************************** - -Updating ezmsg ensures you have the latest features, improvements, and bug fixes. - -If you installed ezmsg using pip, you can easily update it using the following command in your terminal: - -.. code-block:: bash - - pip install --upgrade ezmsg - - -|ezmsg_logo_small| Confirming installation -******************************************* - -To confirm that ezmsg is installed correctly, you can run the following command: - -.. code-block:: bash - - pip show ezmsg - -This will display information about the installed package, including its version and location. -You can also run a simple test script: - -.. code-block:: python - - # test_ezmsg.py - import ezmsg - - print("ezmsg is installed and working correctly!") - -with the following code (in the terminal) to check if ezmsg is functioning as expected: - -.. code-block:: bash - - python3 test_ezmsg.py - - -|ezmsg_logo_small| Installing Extensions -***************************************** - -ezmsg comes with a whole host of extensions that can be installed to add extra functionality. All of these are optional and can be installed as needed. - -To install an extension, you can use pip: - -.. code-block:: bash - - pip install ezmsg[extension_name] - -For more information on available extensions, please refer to the :doc:`Extensions page <../extensions/content-extensions>`. - - -.. |ezmsg_logo_small| image:: ../../_static/_images/ezmsg_logo.png - :width: 40 - :alt: ezmsg logo diff --git a/docs/source/how-tos/basics/jupyter.rst b/docs/source/how-tos/basics/jupyter.rst deleted file mode 100644 index 78d974f5..00000000 --- a/docs/source/how-tos/basics/jupyter.rst +++ /dev/null @@ -1,15 +0,0 @@ -How to run ezmsg in a Jupyter notebook? -#################################################################### - - -If you have an ezmsg pipeline defined in a Jupyter notebook, you can run it directly from the notebook as long as you call the `ez.run()` function with the optional keyword argument ``force_single_process=True``: - - .. code-block:: python - - ez.run(components=components, connections=connections, force_single_process=True) - -Here the components are the units of your pipeline and the connections define how the components are connected (for more details, see :ref:`creating-pipeline`). - -.. |ezmsg_logo_small| image:: ../../_static/_images/ezmsg_logo.png - :width: 40 - :alt: ezmsg logo diff --git a/docs/source/how-tos/basics/sandbox.rst b/docs/source/how-tos/basics/sandbox.rst deleted file mode 100644 index 86cd6152..00000000 --- a/docs/source/how-tos/basics/sandbox.rst +++ /dev/null @@ -1,47 +0,0 @@ -How to run ezmsg from a python script? -#################################################################### - - -If you have an ezmsg pipeline defined in a python script, you can run it in two ways: - -1. (sandbox mode) Directly from the script using the python interpreter. -2. (client-server mode) Using the command line interface to connect to an ezmsg instance running in the background. - -The latter is explained in :doc:`commandline`. - -The former is explained below. - -.. note:: We call running ezmsg without using the command line interface "*sandbox mode*". This is because ezmsg will automatically set up a local `GraphServer` to manage the pipeline execution. It will automatically choose appropriate settings for the `GraphServer`, including the address (host and port). In order to connect to this `GraphServer`, you would need to guess the address set by ezmsg. Strictly speaking, this is not a true sandbox, but this nomenclature is used to indicate that we are not setting any `GraphServer` details and we are not intending to manage the `GraphServer`. - -.. note:: If you want to specify the address of the `GraphServer`, you can either set the ``EZMSG_GRAPH_SERVER`` environment variable before running your script or start an ezmsg instance from the command line with the `serve` or `start` commands, which will allow you to specify the address and port. This is explained in :doc:`commandline` - - -|ezmsg_logo_small| The ``run()`` function -*********************************************************** - -In order to run the ezmsg pipeline in any mode, your script must contain a call to ``ezmsg.core.run()``. You can consult the API reference for :doc:`run() here <../../reference/API/entrypoint>`. - -At minimum, you need to provide the components and connections of your pipeline to the ``run()`` function. - -.. note:: Generally we suggest importing ezmsg as ``import ezmsg.core as ez`` and then using the shorthand ``ez.run()``. This is just a convention, but it makes the code easier to read and understand. - -As an example, consider the example script provided in :doc:`../../tutorials/run`. - -.. tip:: In order to be able to use your script as a script and module, it is good practice to wrap the call to ``ez.run()`` in a ``if __name__ == "__main__":`` block. This ensures that the pipeline is only run when the script is executed directly, and not when it is imported as a module in another script. - - -|ezmsg_logo_small| Run the ezmsg pipeline from your script -*********************************************************** - -To run the pipeline, you can use the following command in the :term:`terminal`: - -.. code-block:: bash - - python3 tutorial_pipeline.py - -This will start the pipeline and begin processing data. - - -.. |ezmsg_logo_small| image:: ../../_static/_images/ezmsg_logo.png - :width: 40 - :alt: ezmsg logo diff --git a/docs/source/how-tos/basics/update.rst b/docs/source/how-tos/basics/update.rst deleted file mode 100644 index c0ae9e25..00000000 --- a/docs/source/how-tos/basics/update.rst +++ /dev/null @@ -1,21 +0,0 @@ -How to update ezmsg? -########################### - - -Updating ezmsg ensures you have the latest features, improvements, and bug fixes. - -If you installed ezmsg using pip, you can easily update it using the following command in your terminal: - -.. code-block:: bash - - pip install --upgrade ezmsg - -If you installed ezmsg by cloning the ezmsg repository, you can pull the latest changes from the main branch: - -.. code-block:: bash - - git pull origin main - -.. |ezmsg_logo_small| image:: ../../_static/_images/ezmsg_logo.png - :width: 40 - :alt: ezmsg logo \ No newline at end of file diff --git a/docs/source/how-tos/basics/visualisation.rst b/docs/source/how-tos/basics/visualisation.rst deleted file mode 100644 index 28c2351c..00000000 --- a/docs/source/how-tos/basics/visualisation.rst +++ /dev/null @@ -1,56 +0,0 @@ -How to visualise the ezmsg pipeline graph? -############################################## - -Other than the ``serve``, ``start`` and ``shutdown`` commands, the ezmsg command-line interface allows you to visualise your pipeline graphically. The two available commands are ``graphviz`` and ``mermaid``, corresponding to the two visualisation formats we support. - -While the pipeline is running, if you run the following in a new terminal window: - -.. code-block:: bash - - ezmsg graphviz --address 127.0.0.1:25978 - -then ezmsg will output a **Graphviz** representation of the pipeline to the terminal. As an example, here is the representation of the simple pipeline from the :doc:`tutorial <../../tutorials/run>`: - -.. image:: ../../_static/_images/tutorial_graphviz.png - :width: 100% - :alt: Graphviz pipeline visualisation text output - -.. note:: You can copy this output and paste it into a Graphviz live editor, such as ``_, to view the pipeline graphically. - -.. note:: The really long numerical node identifiers are simply randomly generated unique identifiers for each connection point (since the names may be the same like `INPUT_SIGNAL`). These identifiers have no special meaning. - -.. warning:: This command and the ones described in the :ref:`mermaid-section` section below will only output the pipeline graph if the ezmsg system is running and has a pipeline connected to it. If you run this command when the pipeline is finished or not running, you will not get any output. Our example is so simple that it will finish very quickly, so in order to visualise the graph maybe change the `iterations` in the `CountSettings` to a larger number, e.g. 100000, so that you have time to run the command and see the output. - -.. _mermaid-section: - -Mermaid visualisation -=========================== - -To visualise your pipeline using **Mermaid**, you can use the following command: - -.. code-block:: bash - - ezmsg mermaid --address 127.0.0.1:25978 - -This will do two things: - -- generate a Mermaid diagram format text of your pipeline and display it in the terminal -- open a mermaid live editor in your browser with the diagram pre-loaded. - -.. image:: ../../_static/_images/tutorial_graph.png - :width: 100% - :alt: Mermaid pipeline visualisation - -.. note:: If you would like to disable the automatic opening of the Mermaid live editor, you can use the ``--nobrowser`` (or ``-n``) flag and append it to the call to ``ezmsg mermaid``. Then, if you would like to view the diagram, you can still copy and paste the diagram code from your terminal into a `Mermaid live editor `_. - -Notice that the diagram shows all the Components and connections in the pipeline, as well as the streams that are being used. If you don't care about the streams themselves, and are interested more in the Components and their connections, you can append the ``--compact`` (or ``-c``) option to simplify the visualisation: - -.. image:: ../../_static/_images/tutorial_graph_compact.png - :width: 100% - :alt: Compact Mermaid pipeline visualisation - - - -.. |ezmsg_logo_small| image:: ../../_static/_images/ezmsg_logo.png - :width: 40 - :alt: ezmsg logo diff --git a/docs/source/how-tos/content-howtos.rst b/docs/source/how-tos/content-howtos.rst deleted file mode 100644 index 009aac06..00000000 --- a/docs/source/how-tos/content-howtos.rst +++ /dev/null @@ -1,53 +0,0 @@ -HOW TOs -########################## - -Answering common questions and providing guidance on using ``ezmsg`` effectively. - -ezmsg System Basics -************************** - -This section covers the foundational aspects of the ``ezmsg`` system, including installation, updates, command-line usage, sandboxing, and visualization techniques. - -.. toctree:: - :maxdepth: 2 - - basics/content-basics - - -Creating ezmsg Units and Pipelines -************************************** - -.. toctree:: - :maxdepth: 2 - - pipeline/content-pipeline - -.. AxisArray Message Format -.. ************************ - -.. ``ezmsg`` provides a specialized message format for handling multidimensional data arrays, known as AxisArray. This section covers how to work with AxisArray messages. - -.. .. toctree:: -.. :maxdepth: 2 - -.. axisarray/content-axisarray - -.. Signal Processing with ezmsg -.. ******************************** - -.. The following HOW TOs provide guidance on implementing signal processing functionalities using the ``ezmsg`` extension called ``ezmsg-sigproc``. These HOW TOs are designed to help you create, manage, and optimize signal processing units within the ``ezmsg`` framework. - -.. .. toctree:: -.. :maxdepth: 2 - -.. signalprocessing/content-signalprocessing - -.. Advanced ezmsg Usage -.. ************************* - -.. This section delves into advanced topics such as online and offline processing, Jupyter integration, and performance optimization techniques. - -.. .. toctree:: -.. :maxdepth: 2 - -.. advanced/content-advanced \ No newline at end of file diff --git a/docs/source/how-tos/pipeline/collection.rst b/docs/source/how-tos/pipeline/collection.rst deleted file mode 100644 index 9259e65b..00000000 --- a/docs/source/how-tos/pipeline/collection.rst +++ /dev/null @@ -1,34 +0,0 @@ -How to create an ezmsg Collection of Units? -############################################### - -We can optionally combine several Units into a single node called a Collection. - -From an API perspective, a Collection is very similar to a Unit. It can have its own settings and state, and it can have input and output streams. For details on how to write an ezmsg Unit, see :doc:`unit`. However, instead of processing messages directly, a Collection contains multiple Units and defines how they are connected together. - -First, define the member Units. Then use ``configure()`` to apply settings to the component Units at initialisation. Finally use ``network()`` to provide an iterable of pairs that define how the Units are connected. Remember, connections are directed (one-way), so the pairs are in the form ``(from-Unit, to-Unit)``. As an example, in the first connection pair provided below, the first element is the output stream of ``COUNT``, and the second element is the input stream of ``ADD_ONE``. - -.. code-block:: python - - class CountCollection(ez.Collection): - - # Define member units - COUNT = Count() - ADD_ONE = AddOne() - PRINT = PrintValue() - - # Use the configure function to apply settings to member Units - def configure(self) -> None: - self.COUNT.apply_settings(CountSettings(iterations=20)) - - # Use the network function to connect inputs and outputs of Units - def network(self) -> ez.NetworkDefinition: - return ( - (self.COUNT.OUTPUT_COUNT, self.ADD_ONE.INPUT_COUNT), - (self.ADD_ONE.OUTPUT_PLUS_ONE, self.PRINT.INPUT) - ) - -Our example does not contain any settings or state for the Collection itself, but you can add these as needed. For example, you might want to add a setting that is common to multiple member Units, and then apply that setting to each member Unit in the ``configure()`` method. - -.. note:: Within the ``configure()`` method, we applied settings to the ``Count`` Unit by using an in-built method called ``apply_settings()``. This method takes an instance of the settings class and applies it to the Unit. In this case, we set the number of iterations to 20, meaning that the Count Unit will generate numbers from 0 to 19. - -.. note:: The ``configure()`` method is optional. If you do not need to apply settings to the member Units, you can omit it. The ``network()`` method is also optional, but it is required if you want to connect the Units together in a specific way. If you do not provide a ``network()`` method, the Units will not be connected and will not communicate with each other. \ No newline at end of file diff --git a/docs/source/how-tos/pipeline/content-pipeline.rst b/docs/source/how-tos/pipeline/content-pipeline.rst deleted file mode 100644 index 1f41604d..00000000 --- a/docs/source/how-tos/pipeline/content-pipeline.rst +++ /dev/null @@ -1,11 +0,0 @@ -ezmsg Pipeline HOW TOs -########################### - -.. toctree:: - :maxdepth: 1 - - unit - collection - inputoutput - pipeline - diff --git a/docs/source/how-tos/pipeline/inputoutput.rst b/docs/source/how-tos/pipeline/inputoutput.rst deleted file mode 100644 index 0966475c..00000000 --- a/docs/source/how-tos/pipeline/inputoutput.rst +++ /dev/null @@ -1,9 +0,0 @@ -How to get data from a device in ezmsg? -########################################### - -ezmsg provides several built-in Units to interface with common data sources, such as cameras, microphones, and files. To get data from a device, you typically use a specific Unit designed for that device type. - -For example, you can use: - -- `ezmsg-blackrock` for Blackrock neural data acquisition systems. -- `ezmsg-lsl` for Lab Streaming Layer (LSL) devices. LSL, in particular, supports a wide range of devices through its ecosystem. \ No newline at end of file diff --git a/docs/source/how-tos/pipeline/pipeline.rst b/docs/source/how-tos/pipeline/pipeline.rst deleted file mode 100644 index f3d8e793..00000000 --- a/docs/source/how-tos/pipeline/pipeline.rst +++ /dev/null @@ -1,64 +0,0 @@ -How to build an ezmsg pipeline? -####################################### - -An ezmsg pipeline is built by connecting together multiple Units and Collections to form a directed graph of data processing nodes. Each Unit or Collection processes incoming messages and produces outgoing messages that can be consumed by other Units or Collections. - -First, an example: - -.. code-block:: python - - import ezmsg.core as ez - from my_custom_module import UnitA, UnitB, CollectionC - - # Define components - components = { - "UNIT_A": UnitA(frequency=10), - "UNIT_TWO": UnitB(), - "COLLECTION_GAMMA": CollectionC(channel_count=4) - } - - # Define connections between components - connections = ( - (components["UNIT_A"].OUTPUT_STREAM, components["UNIT_TWO"].INPUT_STREAM), - (components["UNIT_TWO"].OUTPUT_STREAM, components["COLLECTION_GAMMA"].INPUT_STREAM), - ) - - # Create and run the pipeline - ez.run(components=components, connections=connections) - - -Now, the details. To build a pipeline, follow these steps: - -- Import the ezmsg library: - - .. code-block:: python - - import ezmsg.core as ez - -- Define the Units and Collections you want to use in your pipeline. You can use built-in Units provided by ezmsg or create your own custom Units and Collections. -- Create a dictionary of components (Units and Collections that make up your pipeline) mapping the names (as strings) you give your component instances to their corresponding objects. This dictionary should include all the Units and Collections you defined in the previous step. This is also where you instantiate your Units and Collections. For example: - - .. code-block:: python - - components = { - "UNIT_A": UnitA(frequency=10), - "UNIT_TWO": UnitB(), - "COLLECTION_GAMMA": CollectionC(channel_count=4) - } - -- Define the connections between the components in your pipeline as a tuple of pairs. Each connection specifies which output stream of one component is connected to which input stream of another component. For example: - - .. code-block:: python - - connections = ( - (components["UNIT_A"].OUTPUT_STREAM, components["UNIT_TWO"].INPUT_STREAM), - (components["UNIT_TWO"].OUTPUT_STREAM, components["COLLECTION_GAMMA"].INPUT_STREAM), - ) -- Create an ezmsg Pipeline instance by passing the components dictionary and connections tuple to the ``ezmsg.core.run()`` function, which will set up and start the pipeline. For example: - - .. code-block:: python - - ez.run(components=components, connections=connections) - - -Once the pipeline is running, the Units and Collections will process messages according to their defined behavior, and data will flow through the pipeline as specified by the connections you defined. \ No newline at end of file diff --git a/docs/source/how-tos/pipeline/unit.rst b/docs/source/how-tos/pipeline/unit.rst deleted file mode 100644 index 3fc2a236..00000000 --- a/docs/source/how-tos/pipeline/unit.rst +++ /dev/null @@ -1,218 +0,0 @@ -How to write an ezmsg Unit? -###################################### - -For a tutorial on this, see :doc:`../../tutorials/pipeline`. - -Here is an example of a simple ezmsg Unit: - -.. code-block:: python - - class CountMessages(ez.Unit): - SETTINGS = CountMessagesSettings - STATE = CountMessagesState - - INPUT = ez.InputStream(SimpleMessage) - OUTPUT = ez.OutputStream(EnhancedMessage) - - @ez.subscriber(INPUT) - @ez.publisher(OUTPUT) - async def on_message(self, message) -> AsyncGenerator: - STATE.count += 1 - if STATE.count >= self.SETTINGS.max_count: - raise ez.NormalTermination - yield self.OUTPUT, EnhancedMessage(message=message, count=STATE.count) - -.. important:: This example assumes that ``CountMessagesSettings``, ``CountMessagesState``, ``SimpleMessage``, and ``EnhancedMessage`` are defined elsewhere in your code. For example, - - .. code-block:: python - - @dataclass - class CountMessagesSettings(ez.Settings): - max_count: int = 10 - - @dataclass - class CountMessagesState(ez.State): - count: int = 0 - - @dataclass - class SimpleMessage: - data: str - - @dataclass - class EnhancedMessage: - message: SimpleMessage - count: int - -A very simple ezmsg Unit will be made up of the following components: - -- settings provided in the form of a settings class (optional, at most one) -- state provided in the form of a state class (optional, at most one) -- input / output streams (required, at least one input or output) -- method that does the processing you desire for the Unit (required, at least one method) - -As one can see from above there is a lot of flexibility in which components are used and in what quantity. In particular, one can have multiple streams and processing methods (but not settings or state) adding more functionality to each Unit. - -Our simple example above takes in a message (in the form of a SimpleMessage class) and sends out an enhanced message (the same message along with an additional count of messages received). It will stop once it reaches a predetermined `max_count` value. - -Below is an explanation of each of the components of the Unit: - -|ezmsg_logo_small| Import ezmsg -*********************************************************** - -First, you need to import the ezmsg core module. It is convention to import it as ``ez`` for brevity. - -.. code-block:: python - - import ezmsg.core as ez - -|ezmsg_logo_small| Inherit from ezmsg.core.Unit -********************************************************** - -Every ezmsg Unit must inherit from the base class ``ezmsg.core.Unit`` (``ez.Unit`` if you have imported ``ezmsg.core`` as ``ez``). This is how ezmsg recognises the class as a Unit and provides the necessary functionality to make it work within an ezmsg System. - -.. code-block:: python - - class CountMessages(ez.Unit): - ... - - -|ezmsg_logo_small| Define Settings (optional) -************************************************** -Settings provide configuration data for the Unit. They are defined as a dataclass which inherits from ``ezmsg.core.Settings`` (which is a frozen dataclass). They are typically used for configuration parameters that do not change during the execution of the Unit. In our example, our settings class contained a single variable ``max_count`` which determined how many messages the Unit would process before terminating. - -.. tip:: It is good practice to name the settings class with the Unit name followed by `Settings` to make it clear which settings belong to which Unit. So, for a Unit named `CountMessages`, the settings class is named `CountMessagesSettings`. - -So, if you have need of a settings class, you would define it as follows: - -.. code-block:: python - - @dataclass - class CountMessagesSettings(ez.Settings): - # include settings parameters here - -In the Unit itself, you will need to provide the settings class as a class attribute called ``SETTINGS``. This is how ezmsg knows what settings type to use when instantiating the Unit. - -.. code-block:: python - - class CountMessages(ez.Unit): - SETTINGS = CountMessagesSettings - -.. note:: It is ``SETTINGS = YourSettingsClass``, not ``SETTINGS: YourSettingsClass``. This is due to how the backend initialises the Unit. We also DO NOT instantiate the settings class here (i.e., do not use ``SETTINGS = YourSettingsClass()``). - -How do we actually set the settings values? **The settings attribute is initialised (with values) when the Unit is initialised**. Typically this is when you create a pipeline (see :doc:`pipeline`). If all you need to do when initialising the Unit is set the settings attribute values, then simply call the class with the desired settings values as keyword arguments. For example: - -.. code-block:: python - - count_unit = CountMessages(max_count=5) - -or equivalently: - -.. code-block:: python - - count_unit = CountMessages(settings=CountMessagesSettings(max_count=5)) - -This example sets the ``max_count`` attribute in the settings for the ``CountMessages`` Unit to 5. - -Under the hood, ezmsg will create an instance of ``CountMessagesSettings`` with the provided keyword arguments and assign it to the Unit's ``SETTINGS`` attribute. It will do this automatically when you create the Unit in a pipeline by running the default implementation of the ``initialize()`` method. (see :ref:`unit_methods` for more on the ``initialize()`` method). If you want to do some custom set up logic during initialisation, you can override this method. - -|ezmsg_logo_small| Define State (optional) -************************************************** -A Unit's state attribute keeps track of certain desirable variables that may change during the execution of the Unit. In our above example, we keep track of how many messages we receive. - -How we use this is very similar to the SETTINGS attribute: - -- Define a state class that inherits from ``ezmsg.core.State``. This is a Python dataclass (but not frozen like ``ez.Settings``). Being a dataclass you don't need to write an ``__init__`` method; the dataclass decorator will create one for you - just define the attributes you want to keep track of. -- In the Unit, define a class attribute called ``STATE`` and assign it to the state class you created using the syntax: ``STATE = YourStateClass`` (but do not initialise it). - -That's it! Since the state is mutable, you can always set/get the state attributes using ``self.STATE.attribute_name`` within your Unit methods. - -.. tip:: It is good practice to name the state class with the Unit name followed by `State` to make it clear which state belong to which Unit. So, for a Unit named `CountMessages`, the state class is named `CountMessagesState`. - -Example: - -.. code-block:: python - - @dataclass - class CountMessagesState(ez.State): - # include state attributes here - - class CountMessages(ez.Unit): - STATE = CountMessagesState - -|ezmsg_logo_small| Input/Output streams -************************************************** -Your unit can have any number of input and output streams. Streams are defined as class attributes using the ``ezmsg.core.InputStream`` and ``ezmsg.core.OutputStream`` classes. Each stream must be given a name (the name of the class attribute) and a message type (the type of messages that will be sent/received on that stream). Example: - -.. code-block:: python - - class CountMessages(ez.Unit): - INPUT = ez.InputStream(InputMessageType) - OUTPUT = ez.OutputStream(OutputMessageType) - -The ``InputMessageType`` and ``OutputMessageType`` can be any Python type: - -- For something simple like keeping track of the number of messages, you would simply use ``int``. If you were passing text, then these could be ``str``. -- You can also make your own custom MessageType class. Above, I created the ``EnhancedMessage`` as a dataclass that contains a ``SimpleMessage`` attribute and integer count attribute: - - .. code-block:: python - - @dataclass - class EnhancedMessage: - msg: SimpleMessage - count: int -- For signal processing applications, and data analysis applications, we recommend using our in-built labelled array messaging class ``AxisArray``. For more details, see :doc:`../../explanations/axisarray`. - -We can use data coming in through an input stream by subscribing to it in one of our Unit methods (see :ref:`unit_methods` for more on this). Similarly, we can send data out through an output stream by publishing to it in one of our Unit methods. Finally, we need to connect the input and output streams to other Units in the pipeline (see :doc:`pipeline` for more on this). - - -.. _unit_methods: - -|ezmsg_logo_small| Unit methods -************************************************** - -You can define any number of methods in your Unit to perform the processing you desire. These can have any name you like. In our example, we defined a method called ``on_message()`` which is responsible for receiving messages from the input stream, processing them, and sending out enhanced messages through the output stream. - -There are a few important notes to remember when implementing these Unit methods: - -- Each method must be asynchronous to allow for non-blocking message processing. This means that each method must be defined with the ``async def`` keywords, not ``def``. -- Each method should be decorated with the appropriate decorators (see :ref:`decorators`). The most important decorators are ``@ez.subscriber`` and ``@ez.publisher``, which are used to subscribe to input streams and publish to output streams, respectively. These two decorators should take the relevant streams as arguments. -- Each publishing method must use ``yield`` to produce output messages. The syntax of the yield statement should be ``yield self.OUTPUT_STREAM, MessageType(...)``, where ``self.OUTPUT_STREAM`` is the output stream you are publishing to, and ``MessageType(...)`` is the message you are sending. -- Each subscribing method must take in a message parameter which will receive the incoming message from the input stream. The method signature should be ``async def method_name(self, message)``. Additionally, in the ``ez.subscriber`` decorator, you can specify the keyword boolean argument ``zero_copy`` to indicate whether you want to receive a zero-copy reference (``zero_copy=True``) to the message (if supported by the message type) or a copy of the message. The default is ``zero_copy=False``. -- If a method is to stop processing and terminate normally, it should raise the ``ez.NormalTermination`` exception. This indicates to ezmsg that the Unit has completed its task and can be safely terminated. -- There are other decorators available for other purposes. See :ref:`decorators` for more details. Note, one can stack decorators. - -With these components discussed, we can see the example from this question again: - -.. code-block:: python - - class CountMessages(ez.Unit): - SETTINGS = CountMessagesSettings - STATE = CountMessagesState - - INPUT = ez.InputStream(SimpleMessage) - OUTPUT = ez.OutputStream(EnhancedMessage) - - @ez.subscriber(INPUT) - @ez.publisher(OUTPUT) - async def on_message(self, message) -> AsyncGenerator: - STATE.count += 1 - if STATE.count >= self.SETTINGS.max_count: - raise ez.NormalTermination - yield self.OUTPUT, EnhancedMessage(message=message, count=STATE.count) - -The asynchronous ``on_message()`` method is decorated with both the ``@ez.subscriber(INPUT)`` and ``@ez.publisher(OUTPUT)`` decorators. This indicates that the method subscribes to the ``INPUT`` stream and publishes to the ``OUTPUT`` stream. The method takes in a ``message`` parameter, which will receive messages of type ``SimpleMessage`` from the ``INPUT`` stream. Inside the method, we increment the ``count`` attribute in the ``STATE`` by 1 each time a message is received. If the count reaches the ``max_count`` value from the ``SETTINGS``, we raise ``ez.NormalTermination`` to indicate that the Unit has completed its task. Otherwise, we yield an ``EnhancedMessage`` containing the original message and the current count to the ``OUTPUT`` stream. - -|ezmsg_logo_small| The Unit backend -************************************************** - -When a Unit is initialised within a pipeline, ezmsg takes care of setting up the necessary infrastructure to manage the Unit's execution. This includes: - -- Initialising the settings and state attributes based on the provided classes by running the ``initialize()`` method. -- Setting up the input and output streams to facilitate message passing between Units. Each publishing stream comes with message channels for each process containing subscribers connected to it. This manages transport via local cache, sharedmemory or TCP depending on location of the relevant subscriber. -- Registering the Unit methods with the appropriate decorators to handle message processing. The ``@ez.subscriber`` and ``@ez.publisher`` decorators, are registered for message transport. Other decorators like ``@ez.main``, or ``@ez.task`` define which process the method runs in (the Unit's main process, or a separate task process, respectively). - - - -.. |ezmsg_logo_small| image:: ../../_static/_images/ezmsg_logo.png - :width: 40 - :alt: ezmsg logo \ No newline at end of file diff --git a/docs/source/how-tos/signalprocessing/adaptive.rst b/docs/source/how-tos/signalprocessing/adaptive.rst deleted file mode 100644 index 3dbaf7d3..00000000 --- a/docs/source/how-tos/signalprocessing/adaptive.rst +++ /dev/null @@ -1,4 +0,0 @@ -How to implement adaptive signal processing in ezmsg? -####################################################### - -(under construction) \ No newline at end of file diff --git a/docs/source/how-tos/signalprocessing/checkpoint.rst b/docs/source/how-tos/signalprocessing/checkpoint.rst deleted file mode 100644 index 79fcdc46..00000000 --- a/docs/source/how-tos/signalprocessing/checkpoint.rst +++ /dev/null @@ -1,4 +0,0 @@ -How to use checkpoints for ezmsg signal processing Units that leverage ML models? -###################################################################################### - -(under construction) \ No newline at end of file diff --git a/docs/source/how-tos/signalprocessing/composite.rst b/docs/source/how-tos/signalprocessing/composite.rst deleted file mode 100644 index ae05d7c3..00000000 --- a/docs/source/how-tos/signalprocessing/composite.rst +++ /dev/null @@ -1,4 +0,0 @@ -How to efficiently chain multiple signal processors in ezmsg? -############################################################### - -(under construction) \ No newline at end of file diff --git a/docs/source/how-tos/signalprocessing/content-signalprocessing.rst b/docs/source/how-tos/signalprocessing/content-signalprocessing.rst deleted file mode 100644 index 4aac0758..00000000 --- a/docs/source/how-tos/signalprocessing/content-signalprocessing.rst +++ /dev/null @@ -1,13 +0,0 @@ -Signal Processing HOW TOs -########################## - -.. toctree:: - :maxdepth: 1 - - processor - stateful - standalone - adaptive - composite - unit - checkpoint diff --git a/docs/source/how-tos/signalprocessing/processor.rst b/docs/source/how-tos/signalprocessing/processor.rst deleted file mode 100644 index ffc6c2cc..00000000 --- a/docs/source/how-tos/signalprocessing/processor.rst +++ /dev/null @@ -1,4 +0,0 @@ -How to write a signal processor in ezmsg? -############################################### - -(under construction) \ No newline at end of file diff --git a/docs/source/how-tos/signalprocessing/standalone.rst b/docs/source/how-tos/signalprocessing/standalone.rst deleted file mode 100644 index 2e8eefa8..00000000 --- a/docs/source/how-tos/signalprocessing/standalone.rst +++ /dev/null @@ -1,4 +0,0 @@ -How to use ezmsg-sigproc signal processors outside of an ezmsg context? -############################################################################### - -(under construction) \ No newline at end of file diff --git a/docs/source/how-tos/signalprocessing/stateful.rst b/docs/source/how-tos/signalprocessing/stateful.rst deleted file mode 100644 index 54937c0e..00000000 --- a/docs/source/how-tos/signalprocessing/stateful.rst +++ /dev/null @@ -1,4 +0,0 @@ -How to implement a stateful signal processor in ezmsg? -############################################################### - -(under construction) \ No newline at end of file diff --git a/docs/source/how-tos/signalprocessing/unit.rst b/docs/source/how-tos/signalprocessing/unit.rst deleted file mode 100644 index a2a1b2e5..00000000 --- a/docs/source/how-tos/signalprocessing/unit.rst +++ /dev/null @@ -1,11 +0,0 @@ -How to turn a signal processor into an ``ezmsg`` Unit? -####################################################### - -To convert a signal processor to an ``ezmsg`` Unit, you can follow these steps: - -1. **Define the Processor**: Create a class that inherits from the appropriate signal processor template (e.g., `SignalProcessor`, `Filter`, etc.). -2. **Implement the Processing Logic**: Override the necessary methods to implement the signal processing logic. -3. **Define Input and Output Ports**: Use the `ezmsg` port system to define input and output ports for the signal processor. -4. **Register the Unit**: Use the `ezmsg` registration system to register the signal processor as an `ezmsg` Unit. - -(under construction) \ No newline at end of file diff --git a/docs/source/index.rst b/docs/source/index.rst index d0cdc593..22ce1b46 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,159 +1,21 @@ - -|ezmsg_logo_large| - -ezmsg +ezmsg API Reference #################### -Welcome to the ezmsg documentation! - -ezmsg is a **highly performant messaging and multiprocessing framework** designed for building modular, high-performance signal processing pipelines. It is built in pure Python with no dependencies, making it easy to install and use. - -|ezmsg_logo_small| What is ezmsg? -********************************** - - ``ezmsg`` (n) - "easy message" - - ezmsg is a high-performance execution engine and multiprocessing backend for executing a directed acyclic graph of stream-processing units via a publish-subscribe pattern facilitated by shared memory transfer with zero-copy messaging between composable nodes written in pure Python with no dependencies or stated application space. - -.. todo:: Add diagram of architecture - -ezmsg implements much of the LabGraph API (with a few notable differences), and owes a lot of its design to the LabGraph developers/project. Afterall, imitation is the sincerest form of flattery. - -The ezmsg library eases the creation of modular systems whose elements can be switched out easily. Consider the use case of building a processing pipeline where you will be experimenting with adding and removing steps until you find an optimal workflow. ezmsg allows you to easily separate each step into a discrete entity and piece together a workflow from those entities. Check out the helpful :doc:`tutorial series ` to see how this works. - -.. `Examples `_ to see how this works. - - -|ezmsg_logo_small| Quick start -************************************** - -For a detailed description of the installation process and getting started with ezmsg, please refer to the :doc:`Getting Started guide `. - -But in short, if you have Python 3.10+ installed, you can install ezmsg via pip: - -.. code-block:: bash - - pip install ezmsg - -Then in your Python script or Jupyter notebook, you can import ezmsg like so: - -.. code-block:: python - - import ezmsg.core as ez - -If you would like to develop with ezmsg or contribute to the project, you can follow the steps outlined in the :doc:`Developer Guide `. - -If you would like to build the documentation locally, please refer to the instructions found in :ref:`documentation_building`. - -A substantial list of extensions exist for ezmsg. One can install extensions as follows: - -.. code-block:: bash - - pip install ezmsg[extension_name] - - -See :doc:`Extensions <../extensions/content-extensions>` for more information. - -|ezmsg_logo_small| Documentation Overview -******************************************* - -This documentation is designed to help you get started with ezmsg, understand its architecture, and learn how to use it effectively in your projects. - -Give it a try with our :doc:`specially created tutorial series ` and see how it can simplify your signal processing needs. - -If you don't want to download and install anything just yet, you can check out this `Google Colab page `_ for an example ezmsg notebook to experiment with. It's a little dated, but it should give you a good idea of how to use ezmsg in a Jupyter notebook environment. +This is the API reference documentation for the **ezmsg** core library. -These are a few high-level topics to help you learn more about ezmsg and the ezmsg ecosystem. +For tutorials, guides, examples, and general documentation, visit ``_ -.. list-table:: - :widths: 50 50 - :class: front_page_table - - * - :doc:`tutorials/content-tutorials` - - Tutorials and examples to help you get started - - - :doc:`tutorials/start` - - Installation instructions for ezmsg - - * - :doc:`explanations/content-explanations` - - About ezmsg, its components and design philosophy - - - :doc:`release` - - New features, changes, deprecation notes, and bug fixes - - * - :doc:`reference/content-reference` - - APIs for ezmsg - - - :doc:`how-tos/content-howtos` - - How to use ezmsg in various scenarios and applications - - * - :doc:`Contributor Guides ` - - Info for those wanting to contribute and help develop ezmsg - - - :doc:`reference/glossary` - - Glossary of terms used in this documentation - - -|ezmsg_logo_small| Why use ezmsg? -*********************************** - -Why use ezmsg over a comparable tool? ezmsg is extremely fast and uses Python's ``multiprocessing.shared_memory`` module to facilitate efficient message passing without C++ or any compilation/build tooling. It is easy to install and contains less boilerplate than similar frameworks. It also provides a framework for building processing pipelines while keeping best practices in mind. - - -|ezmsg_logo_small| Table of Contents -*************************************** - -Below is a breakdown of all the documentation available on this site. +API Documentation +***************** .. toctree:: :maxdepth: 2 - :titlesonly: - - Tutorial - What is ezmsg? - How To - Reference - Developer - Extensions - Release Notes - - -|ezmsg_logo_small| Other Information -************************************** - -Financial Support -========================== - -`ezmsg` is supported by Johns Hopkins University (JHU), the JHU Applied Physics Laboratory (APL), and by the Wyss Center for Bio and Neuro Engineering. - -In Media -========= - -* `BCI Society 2025 Poster `_ -* `Society For Neuroscience 2024 Poster `_ - -Publications -======================= - -A collection of academic papers, journals, and other publications that have cited or utilized `ezmsg` in research and development. -These publications provide insights into the practical applications and impact of `ezmsg` in various fields. -- `A click-based electrocorticographic brain-computer interface enables long-term high-performance switch-scan spelling `_ -- `Stable Decoding from a Speech BCI Enables Control for an Individual with ALS without Recalibration for 3 Months `_ + reference/content-reference -.. |ezmsg_logo_large| image:: _static/_images/ezmsg_logo.png - :width: 120 - :alt: ezmsg logo +Indices and tables +****************** -.. |ezmsg_logo_small| image:: _static/_images/ezmsg_logo.png - :width: 40 - :alt: ezmsg logo +* :ref:`genindex` +* :ref:`modindex` diff --git a/docs/source/reference/API/api.rst b/docs/source/reference/API/api.rst deleted file mode 100644 index 84254558..00000000 --- a/docs/source/reference/API/api.rst +++ /dev/null @@ -1,4 +0,0 @@ -API -=== - -(more API docs to come) \ No newline at end of file diff --git a/docs/source/reference/API/axisarray-util-units.rst b/docs/source/reference/API/axisarray-util-units.rst new file mode 100644 index 00000000..41c94e1e --- /dev/null +++ b/docs/source/reference/API/axisarray-util-units.rst @@ -0,0 +1,42 @@ +AxisArray Utility Methods and Units +================================================= + +Utility classes which implement functionality related to messages. These include a method for instantiating new instances of a dataclass given an existing instance: + +- :class:`replace ` - Function for creating a new dataclass instance by replacing attributes of an existing instance + +and several :class:`ezmsg Unit ` classes which operate on :class:`AxisArray ` messages: + +- :class:`ArrayChunker ` - ezmsg Unit to chunk AxisArray messages along specified axes +- :class:`SetKey ` - ezmsg Unit for setting the key of incoming AxisArray messages +- :class:`FilterOnKey ` - ezmsg Unit for filtering an AxisArray based on its key. +- :class:`ModifyAxis ` - ezmsg Unit for modifying axis names and dimensions of AxisArray messages. + +replace +------------ + +.. autofunction:: ezmsg.util.messages.axisarray.replace + + +Array Chunking +--------------- + +.. automodule:: ezmsg.util.messages.chunker + :show-inheritance: + :members: + + +Modifying AxisArray "key" +--------------------------- + +.. automodule:: ezmsg.util.messages.key + :show-inheritance: + :members: + + +Modifying AxisArray "axes" +------------------------------ + +.. automodule:: ezmsg.util.messages.modify + :show-inheritance: + :members: diff --git a/docs/source/reference/API/components.rst b/docs/source/reference/API/components.rst index 98aae986..8f7c190d 100644 --- a/docs/source/reference/API/components.rst +++ b/docs/source/reference/API/components.rst @@ -1,34 +1,27 @@ Components - Units and Collections =================================== -An ``ezmsg`` pipeline is created from a few basic components. -``ezmsg`` provides a framework for you to define your own graphs using its building blocks. -Inherit from its base components to define a pipeline that works for your project. +An ezmsg pipeline is created from a few basic components. ezmsg provides a framework for you to define your own graphs using its building blocks. The nodes of the graph that defines a pipeline all inherit from the base class :class:`Component `. The two types of Component are :class:`Unit ` and :class:`Collection `. -.. automodule:: ezmsg.core +.. note:: It is convention to ``import ezmsg.core as ez`` and then use this shorthand in your code. e.g., -Most ``ezmsg`` classes intended for use in building pipelines are available in ``ezmsg.core``. -It is convention to ``import ezmsg.core as ez`` and then use this shorthand in your code. e.g., + .. code-block:: python -.. code-block:: python + class MyUnit(ez.Unit): + ... - class MyUnit(ez.Unit): - ... - -The two types of nodes in an ezmsg pipeline are ``Unit`` and ``Collection``. Component ---------- -The base class for ``Unit``\ s and ``Collection``\ s. -.. autoclass:: Component +.. autoclass:: ezmsg.core.Component Unit --------- -The nodes of an ezmsg pipeline graph are ``Unit``\ s. +The basic nodes of an ezmsg pipeline graph are :class:`Units `. -.. autoclass:: Unit +.. autoclass:: ezmsg.core.Unit :show-inheritance: :members: :inherited-members: @@ -37,11 +30,11 @@ The nodes of an ezmsg pipeline graph are ``Unit``\ s. Collection ------------ -A ``Collection`` is a special type of ``Component`` that contains other ``Component``\ s (``Unit``\ s and/or other ``Collection``\ s). +A :class:`Collection ` is a special type of :class:`Component ` that contains other :class:`Components ` (:class:`Units ` and/or other :class:`Collections `). -.. autoclass:: NetworkDefinition +.. autoclass:: ezmsg.core.NetworkDefinition -.. autoclass:: Collection +.. autoclass:: ezmsg.core.Collection :show-inheritance: :members: @@ -49,27 +42,27 @@ A ``Collection`` is a special type of ``Component`` that contains other ``Compon Component Interaction --------------------- -Two fundamental attributes of a ``Component`` are its ``Settings`` and ``State``, both of which are optional but initialised by the ezmsg backend during ``Unit`` initialisation (if present). +Two fundamental attributes of a :class:`Component ` are its :class:`Settings ` and :class:`State `, both of which are optional but initialised by the ezmsg backend during :class:`Unit ` initialisation (if present). -.. autoclass:: Settings +.. autoclass:: ezmsg.core.Settings -.. autoclass:: State +.. autoclass:: ezmsg.core.State Stream ------ -Facilitates a flow of ``Messages`` into or out of a ``Component``. +Facilitates a flow of Messages into or out of a :class:`Component `. -.. autoclass:: InputStream +.. autoclass:: ezmsg.core.InputStream -.. autoclass:: OutputStream +.. autoclass:: ezmsg.core.OutputStream Custom Exceptions ----------------- These are custom exceptions defined in ezmsg. -.. autoclass:: Complete +.. autoclass:: ezmsg.core.Complete -.. autoclass:: NormalTermination \ No newline at end of file +.. autoclass:: ezmsg.core.NormalTermination \ No newline at end of file diff --git a/docs/source/reference/API/content-api.rst b/docs/source/reference/API/content-api.rst index 9e46dcb8..0a05e476 100644 --- a/docs/source/reference/API/content-api.rst +++ b/docs/source/reference/API/content-api.rst @@ -7,7 +7,7 @@ Commonly Used API components functiondecorators - axisarray entrypoint - utils - message-utils + axisarray + axisarray-util-units + util-units diff --git a/docs/source/reference/API/entrypoint.rst b/docs/source/reference/API/entrypoint.rst index 736be991..ebe53dc9 100644 --- a/docs/source/reference/API/entrypoint.rst +++ b/docs/source/reference/API/entrypoint.rst @@ -1,18 +1,9 @@ `ezmsg` Entry Point ======================== -An ``ezmsg`` pipeline is created from a few basic components. -``ezmsg`` provides a framework for you to define your own graphs using its building blocks. -Inherit from its base components to define a pipeline that works for your project. +An ezmsg pipeline is instantiated through a call to ``ezmsg.core.run()``. -.. automodule:: ezmsg.core +.. note:: It is convention to ``import ezmsg.core as ez`` and then use this shorthand in your code. -Most ``ezmsg`` classes intended for use in building pipelines are available in ``ezmsg.core``. -It is convention to ``import ezmsg.core as ez`` and then use this shorthand in your code. e.g., - - -Entry Point ------------ - -.. automethod:: ezmsg.core.run +.. autofunction:: ezmsg.core.run diff --git a/docs/source/reference/API/functiondecorators.rst b/docs/source/reference/API/functiondecorators.rst index 62d5ee59..6b474e2e 100644 --- a/docs/source/reference/API/functiondecorators.rst +++ b/docs/source/reference/API/functiondecorators.rst @@ -1,18 +1,18 @@ Unit Function Decorators ****************************************** -These function decorators can be added to member functions. +These function decorators can be added to member functions of an ezmsg ``Unit`` or ``Collection``. -.. automethod:: ezmsg.core.subscriber +.. autodecorator:: ezmsg.core.subscriber -.. automethod:: ezmsg.core.publisher +.. autodecorator:: ezmsg.core.publisher -.. automethod:: ezmsg.core.main +.. autodecorator:: ezmsg.core.main -.. automethod:: unit.thread +.. autodecorator:: ezmsg.core.thread -.. automethod:: ezmsg.core.task +.. autodecorator:: ezmsg.core.task -.. automethod:: ezmsg.core.process +.. autodecorator:: ezmsg.core.process -.. automethod:: ezmsg.core.timeit +.. autodecorator:: ezmsg.core.timeit diff --git a/docs/source/reference/API/message-utils.rst b/docs/source/reference/API/message-utils.rst deleted file mode 100644 index 15b5069f..00000000 --- a/docs/source/reference/API/message-utils.rst +++ /dev/null @@ -1,41 +0,0 @@ -Message Utility Classes -=========================== - -Utility classes which implement functionality related to messages. - -replace ------------- - -.. autofunction:: ezmsg.util.messages.axisarray.replace - - -MessageReplay -------------- - -.. automodule:: ezmsg.util.messagereplay - :show-inheritance: - :members: - - -MessageGate ------------ - -.. automodule:: ezmsg.util.messagegate - :show-inheritance: - :members: - - -MessageLogger -------------- - -.. automodule:: ezmsg.util.messagelogger - :show-inheritance: - :members: - - -MessageQueue ------------- - -.. automodule:: ezmsg.util.messagequeue - :show-inheritance: - :members: diff --git a/docs/source/reference/API/util-units.rst b/docs/source/reference/API/util-units.rst new file mode 100644 index 00000000..e3d246fb --- /dev/null +++ b/docs/source/reference/API/util-units.rst @@ -0,0 +1,71 @@ +Utility Units and Classes +=========================== + +Utility :class:`ezmsg Units ` and associated classes for `ezmsg` pipelines. Currently includes the following Units: + +- :class:`DebugLog `: An ezmsg Unit for logging message debug information. Convenient way to print message contents without modifying other units. +- :class:`TerminateOnTimeout `: An ezmsg Unit that terminates the pipeline after a specified timeout period. +- :class:`TerminateOnTotal `: An ezmsg Unit that terminates the pipeline after a specified total number of messages have been processed. +- :class:`MessageGate `: An ezmsg Unit that blocks messages based on a condition: open, open after N messages, closed, or closed after N messages. +- :class:`MessageLogger `: An ezmsg Unit that logs all messages passing through it to a file. +- :class:`MessageQueue `: An ezmsg Unit that is placed between two other Units to induce backpressure. +- :class:`MessageCollector `: An ezmsg Unit that collects messages into a local list. +- :class:`MessageReplay `: An ezmsg Unit that streams messages from :class:`MessageLogger ` created files. + +and utility classes for encoding/decoding messages: + +- :class:`MessageDecoder `: Utility class that decodes ezmsg messages from their JSON representation. +- :class:`MessageEncoder `: Utility class that encodes ezmsg messages to their JSON representation. + +DebugLog +-------- + +.. automodule:: ezmsg.util.debuglog + :show-inheritance: + :members: + + +Terminate +--------- + +.. automodule:: ezmsg.util.terminate + :show-inheritance: + :members: + +MessageReplay +------------- + +.. automodule:: ezmsg.util.messagereplay + :show-inheritance: + :members: + + +MessageGate +----------- + +.. automodule:: ezmsg.util.messagegate + :show-inheritance: + :members: + + +MessageLogger +------------- + +.. automodule:: ezmsg.util.messagelogger + :show-inheritance: + :members: + + +MessageQueue +------------ + +.. automodule:: ezmsg.util.messagequeue + :show-inheritance: + :members: + +MessageCodec +------------ + +.. automodule:: ezmsg.util.messagecodec + :show-inheritance: + :members: diff --git a/docs/source/reference/API/utils.rst b/docs/source/reference/API/utils.rst deleted file mode 100644 index cece17d7..00000000 --- a/docs/source/reference/API/utils.rst +++ /dev/null @@ -1,21 +0,0 @@ -Utility Classes -=========================== - -Utility Units and associated classes for `ezmsg` pipelines. - - -DebugLog --------- - -.. automodule:: ezmsg.util.debuglog - :show-inheritance: - :members: - - -Terminate ---------- - -.. automodule:: ezmsg.util.terminate - :show-inheritance: - :members: - diff --git a/docs/source/reference/content-reference.rst b/docs/source/reference/content-reference.rst index 389cf828..e891248b 100644 --- a/docs/source/reference/content-reference.rst +++ b/docs/source/reference/content-reference.rst @@ -1,21 +1,7 @@ -Reference Documentation +API Reference ########################### -Commonly used API -********************* - -For most users, the following API references will be the most relevant: - .. toctree:: :maxdepth: 1 API/content-api - - -Glossary -*************** - -.. toctree:: - :maxdepth: 1 - - glossary diff --git a/docs/source/reference/glossary.rst b/docs/source/reference/glossary.rst deleted file mode 100644 index 5376f828..00000000 --- a/docs/source/reference/glossary.rst +++ /dev/null @@ -1,28 +0,0 @@ -Glossary -######## - -.. glossary:: - - AxisArray - A specialized message format in `ezmsg` for handling multidimensional data arrays with labels and metadata, designed to facilitate signal processing tasks. - - DAG - A :term:`directed acyclic graph`. - - directed acyclic graph - A graph structure where nodes are connected by directed edges, and there are no cycles, meaning you cannot return to a node once you leave it. Often shortened simply to DAG. In `ezmsg`, pipelines are represented as DAGs. for more information, see ``_. - - ezmsg - A messaging framework designed for efficient communication and signal processing. - - PyPI - The **Py**\ thon **P**\ ackage **I**\ ndex, a repository of software for the Python programming language. - - pip - A package management system for installing and managing Python packages. - - Signal Processing - The analysis, interpretation, and manipulation of signals using the `ezmsg` framework. - - terminal - A text-based interface for interacting with the operating system, often used to run commands and scripts. In Windows, it is often referred to as Command Prompt or PowerShell, while in macOS and Linux, it is typically called Terminal. \ No newline at end of file diff --git a/docs/source/release.rst b/docs/source/release.rst deleted file mode 100644 index 41f20f54..00000000 --- a/docs/source/release.rst +++ /dev/null @@ -1,3 +0,0 @@ -Release Notes -################# - diff --git a/docs/source/tutorials/content-tutorials.rst b/docs/source/tutorials/content-tutorials.rst deleted file mode 100644 index 9ed0e96b..00000000 --- a/docs/source/tutorials/content-tutorials.rst +++ /dev/null @@ -1,23 +0,0 @@ -Tutorial -########### - -Let's get acquainted with ezmsg - the practical way. This tutorial provides a hands-on approach to learning the ezmsg framework. By following the examples and exercises, you will gain practical experience in using ezmsg for your messaging and signal processing needs. - -If you would like to understand the design and decision-making behind ezmsg, please refer to the :doc:`../explanations/content-explanations` section. - -For more in-depth documentation of the code, please refer to the :doc:`reference documentation <../reference/content-reference>` and :doc:`HOW TOs <../how-tos/content-howtos>`. - -|ezmsg_logo_small| Contents -============================== - -.. toctree:: - :maxdepth: 1 - - start - pipeline - run - signalprocessing - -.. |ezmsg_logo_small| image:: ../_static/_images/ezmsg_logo.png - :width: 40 - :alt: ezmsg logo \ No newline at end of file diff --git a/docs/source/tutorials/pipeline.rst b/docs/source/tutorials/pipeline.rst deleted file mode 100644 index 727ef700..00000000 --- a/docs/source/tutorials/pipeline.rst +++ /dev/null @@ -1,230 +0,0 @@ -Your First ezmsg Pipeline -############################## - -If this is your first time using ezmsg, you're in the right place. This notebook will walk you through the basics of creating a very simple ezmsg system. You can copy the code into your own Python script or Jupyter notebook environment using the helpful copy button in the top right corner of each code block. - -ezmsg is ideal for creating modular processing pipelines whose steps can be arranged as a directed acyclic graph. In this notebook, we will walk through a very simple graph which generates a count of numbers, adds 1 to each number, and prints to standard output. - -|tutorial_pipeline| - -In ezmsg syntax, this graph would look like this: - -|tutorial_system| - -We will write an ezmsg Unit for each discrete step of our pipeline, and then later show you how to optionally connect them together in a Collection. - -First, ensure you have ezmsg installed. Please consult :doc:`start` for installation instructions. - -.. note:: If you are using a Jupyter notebook, you can install ezmsg directly from the notebook using the following command: - - .. code-block:: bash - - !pip install ezmsg - - -Next, ensure we have all the necessary imports: - -.. code-block:: python - - import ezmsg.core as ez - from dataclasses import dataclass - from collections.abc import AsyncGenerator - - -|ezmsg_logo_small| Building a basic ezmsg Unit -*********************************************** - -A very simple ezmsg Unit will be made up of the following components: - -- settings provided in the form of a settings class (optional, at most one) -- state provided in the form of a state class (optional, at most one) -- input / output streams (required, at least one input or output) -- method that does the processing you desire for the Unit (required, at least one method) - -As one can see from above there is a lot of flexibility in which components are used and in what quantity. In particular, one can have multiple streams and processing methods (but not settings or state) adding more functionality to each Unit. - -Our very basic counting Unit will simply produce integers starting at 0. We don't need to worry about its state but we would like to specify at what point it stops so we'll create a settings class that contains how many numbers to generate. - -.. code-block:: python - - class CountSettings(ez.Settings): - iterations: int - -.. note:: We inherited from ``ez.Settings`` which is a frozen dataclass. This means that the settings are immutable after they are set, which is good practice for configuration data. - -Next, we need to create a message type that the Unit will produce. This message will be sent to the next Unit in the pipeline. In this case, we will create a simple dataclass that contains an integer value. (In such a simple case, we could simply avoid creating a new class and use a simple integer, but this is a good practice for more complex messages.) - -.. code-block:: python - - @dataclass - class CountMessage: - value: int - -Now that we have the ingredients, we will create the Unit that will generate the count. Every Unit represents a node in the directed acyclic graph and should contain inputs and/or outputs and at least one function which subscribes to the inputs or publishes to the outputs. - -For Count, we create an OutputStream and a publishing function which will perform the number calculation and yield CountMessages to the OutputStream. - -.. code-block:: python - - class Count(ez.Unit): - # Settings must be provided in this way: `SETTINGS = YourSettingsClass` - # Do not instantiate the settings class here - SETTINGS = CountSettings - - OUTPUT_COUNT = ez.OutputStream(CountMessage) - - @ez.publisher(OUTPUT_COUNT) - async def count(self) -> AsyncGenerator: - count = 0 - while count < self.SETTINGS.iterations: - yield self.OUTPUT_COUNT, CountMessage(value=count) - count = count + 1 - - raise ez.NormalTermination - -First, notice that we inherit from ``ez.Unit``, which is the base class for all ezmsg Units. - -Next, notice that we provide the settings class as a class attribute called ``SETTINGS``. This is how ezmsg knows what settings type to use when instantiating the Unit. Instantiation is done at the system level as we will see in the section :ref:`creating-pipeline`. - -.. note:: The ``SETTINGS = CountSettings`` format is important. It should always be in the format ``SETTINGS = YourSettingsClass`` with the only change being to replace `YourSettingsClass` with the name of the class. - -Then, we define an output stream called ``OUTPUT_COUNT`` which will send messages of type ``CountMessage``. ``OUTPUT_COUNT`` is the name you provide to the outgoing stream, and ``CountMessage`` is the type of messages that will be sent on this stream. As an output stream, it will be used to connect the Unit to other Units in a System. - - -.. note:: ``CountMessage`` literally contains an integer, so in this very simple case we could have simply used ``OUTPUT_COUNT = ez.OutputStream(int)``. But this is good practice for more complex messages. - -The method ``count()`` is where the actual number generation occurs. From the code, we can see that it generates integer values starting at 0 for the variable ``count`` and stops once it reaches the value of ``iterations`` within the Unit's ``SETTINGS`` attribute. - -It yields the tuple ``(self.OUTPUT_COUNT, CountMessage(value=count))``, which gives the output stream ``OUTPUT_COUNT`` a ``CountMessage`` with the value of ``count``. But in order for it to actually publish this value to other units we decorate the method with ``@ez.publisher(OUTPUT_COUNT)``. Finally, when the Unit completes its task, we raise ``ez.NormalTermination``, which indicates to ezmsg that this Unit is done processing. - -.. note:: Important final note on the ``count()`` method. It is preceded by the ``async`` keyword. This indicates that the method is a coroutine function, and can be run asynchronously. This means that if it isn't ready to yield a value it relinquishes the control to another process. The benefit is that it does not block other processes from executing allowing more parallelised execution. This is a key feature of ezmsg and recommended when working with I/O bound tasks or long-running computations. - -.. todo:: Provide picture on asynchronous execution and how it works in ezmsg. - -|ezmsg_logo_small| Building a Unit with multiple streams -*********************************************************** - -The next Unit in the chain should accept a CountMessage from the first Unit, add 1 to its value, and yield a new CountMessage. To do this, we create a new Unit which contains a function which both subscribes and publishes. Analogously to when we decorated the ``count()`` method with ``@ez.publisher(OUTPUT_COUNT)``, we will decorate this function with both ``@ez.subscriber()`` to indicate that it will receive messages from the input stream, and ``@ez.publisher()`` to indicate that it will send messages to the output stream. - -.. code-block:: python - - class AddOne(ez.Unit): - - INPUT_COUNT = ez.InputStream(CountMessage) - OUTPUT_PLUS_ONE = ez.OutputStream(CountMessage) - - @ez.subscriber(INPUT_COUNT) - @ez.publisher(OUTPUT_PLUS_ONE) - async def on_message(self, message) -> AsyncGenerator: - yield self.OUTPUT_PLUS_ONE, CountMessage(value=message.value + 1) - -The subscribing function will be called anytime the Unit receives a message to the InputStream ``INPUT_COUNT`` that the function subscribes to. It will then publish the result of adding one to the OutputStream ``OUTPUT_PLUS_ONE``. - -We will connect this Unit to Count later on, when we create the full pipeline. - -|ezmsg_logo_small| Another Unit - print the result -*************************************************** - -Finally, the last unit should print the value of any messages it receives. It will only have an input stream and no output stream, as it will not send any messages to other Units. - -.. code-block:: python - - class PrintValue(ez.Unit): - - INPUT = ez.InputStream(CountMessage) - - @ez.subscriber(INPUT) - async def on_message(self, message) -> None: - print(message.value) - - -|ezmsg_logo_small| Combining multiple Units into a Collection -************************************************************** -We can optionally combine Units into a single node called a Collection. - -First, define the member Units. Then use ``configure()`` to apply settings to the component Units at initialisation. Finally use ``network()`` to provide an iterable of pairs that define how the Units are connected. Remember, connections are directed (one-way), so the pairs are in the form ``(from-Unit, to-Unit)``. As an example, in the first connection pair provided below, the first element is the output stream of ``COUNT``, and the second element is the input stream of ``ADD_ONE``. - -.. code-block:: python - - class CountCollection(ez.Collection): - - # Define member units - COUNT = Count() - ADD_ONE = AddOne() - PRINT = PrintValue() - - # Use the configure function to apply settings to member Units - def configure(self) -> None: - self.COUNT.apply_settings(CountSettings(iterations=20)) - - # Use the network function to connect inputs and outputs of Units - def network(self) -> ez.NetworkDefinition: - return ( - (self.COUNT.OUTPUT_COUNT, self.ADD_ONE.INPUT_COUNT), - (self.ADD_ONE.OUTPUT_PLUS_ONE, self.PRINT.INPUT) - ) - -.. note:: Within the ``configure()`` method, we applied settings to the ``Count`` Unit by using an in-built method called ``apply_settings()``. This method takes an instance of the settings class and applies it to the Unit. In this case, we set the number of iterations to 20, meaning that the Count Unit will generate numbers from 0 to 19. - -.. note:: The ``configure()`` method is optional. If you do not need to apply settings to the member Units, you can omit it. The ``network()`` method is also optional, but it is required if you want to connect the Units together in a specific way. If you do not provide a ``network()`` method, the Units will not be connected and will not communicate with each other. - -.. _creating-pipeline: - -|ezmsg_logo_small| Creating a pipeline -*************************************** - -We combined the Units above into a Collection, but this doesn't always make sense, so let's step back and see how to create a pipeline using just the Units we created above. Notice, that setting up the connections is very similar to how we defined ``network()`` in the Collection above. - -.. code-block:: python - - components = { - "COUNT": Count(settings=CountSettings(iterations=10)), - "ADD_ONE": AddOne(), - "PRINT": PrintValue() - } - connections = ( - (components["COUNT"].OUTPUT_COUNT, components["ADD_ONE"].INPUT_COUNT), - (components["ADD_ONE"].OUTPUT_PLUS_ONE, components["PRINT"].INPUT) - ) - ez.run(components = components, connections = connections) - -.. note:: We instantiated the ``Count`` Unit with settings directly in the components dictionary. Another way is to use ``apply_settings()``: - - .. code-block:: python - - "COUNT": Count().apply_settings(iterations=10) - -.. note:: The ``ez.run()`` function is the entry point to running an ezmsg system. It takes a dictionary of components (Units or Collections) and an iterable of connections (pairs of input/output streams) and runs the system. More on this in the next :doc:`tutorial section `. - - -Quick Aside - Using the Collection to Create a Pipeline -========================================================= - -If using a Collection, the above code is no different. Units and Collections are both seen by ezmsg as an object called a Component, so we could have simply done: - -.. code-block:: python - - components = {"COUNT_COLLECTION": CountCollection()} - connections = () - ez.run(components = components, connections = connections) - -.. warning:: DO NOT have two different ``ez.run()`` calls in the same script or notebook. Only one ``ez.run()`` call is allowed per execution context. We have given you two examples of how to build a pipeline from Units, so when running the code, comment out one of the previous two code blocks (components, connections, and ``ez.run()`` calls). - -|ezmsg_logo_small| How to run the pipeline? -********************************************* - -All the code is there now! If you want to run the pipeline, we just need to run the script. If you're using a Jupyter notebook, you can run the code cells directly. If you're using a Python script, save the code in a file (e.g., `pipeline.py`) and run it from the command line: - -.. code-block:: bash - - python3 pipeline.py - -There is more to running the pipeline than this little tutorial has shown you so far, so click Next below to check out the next section on :doc:`running ezmsg ` for more details on how to run your ezmsg pipelines and systems. - -.. |tutorial_pipeline| image:: ../_static/_images/tutorial_pipeline.png - -.. |tutorial_system| image:: ../_static/_images/tutorial_system.png - -.. |ezmsg_logo_small| image:: ../_static/_images/ezmsg_logo.png - :width: 40 - :alt: ezmsg logo diff --git a/docs/source/tutorials/run.rst b/docs/source/tutorials/run.rst deleted file mode 100644 index be25c83b..00000000 --- a/docs/source/tutorials/run.rst +++ /dev/null @@ -1,224 +0,0 @@ -Running An ezmsg System -############################ - -In the previous :doc:`tutorial `, we finished by running a simple ezmsg pipeline directly through the use of the ``run()`` function within a python script. In this tutorial, we will explore how to run an ezmsg system in different ways, including using the command line interface and visualising the pipeline. - -What actually happens when you run a script with an ezmsg pipeline and a ``run()`` function is that ezmsg starts a backend server called a **`GraphServer`**. This `GraphServer` manages the operation of any ezmsg pipeline connected to it. The naming comes from the fact that ezmsg pipelines are simply nodes - Units or Collections - connected in a :term:`directed acyclic graph` (:term:`DAG`\ ). - -The `GraphServer` is coupled with a **`SharedMemoryServer`** that allows for efficient data transfer between the different components of the pipeline. This is particularly useful for high-throughput applications where low latency is crucial. It utilises the in-built Python ``multiprocessing.shared_memory`` library to handle the data transfer between processes. - -The reverse is true too: you can run ezmsg instance (start a `GraphServer`) without a pipeline, but until you attach an ezmsg pipeline nothing interesting will happen. - -.. note:: For this tutorial, you will need to have :doc:`ezmsg installed ` and have an :doc:`ezmsg pipeline ` ready to run. - -For the sake of this tutorial, we recreate the script from the :doc:`pipeline` tutorial here. Copy it into a file called `tutorial_pipeline.py` in the root of the ezmsg repository. - -.. code-block:: python - - # tutorial_pipeline.py - import ezmsg.core as ez - from dataclasses import dataclass - from collections.abc import AsyncGenerator - - class CountSettings(ez.Settings): - iterations: int - - @dataclass - class CountMessage: - value: int - - class Count(ez.Unit): - SETTINGS = CountSettings - - OUTPUT_COUNT = ez.OutputStream(CountMessage) - - @ez.publisher(OUTPUT_COUNT) - async def count(self) -> AsyncGenerator: - count = 0 - while count < self.SETTINGS.iterations: - yield self.OUTPUT_COUNT, CountMessage(value=count) - count = count + 1 - - raise ez.NormalTermination - - class AddOne(ez.Unit): - INPUT_COUNT = ez.InputStream(CountMessage) - OUTPUT_PLUS_ONE = ez.OutputStream(CountMessage) - - @ez.subscriber(INPUT_COUNT) - @ez.publisher(OUTPUT_PLUS_ONE) - async def on_message(self, message) -> AsyncGenerator: - yield self.OUTPUT_PLUS_ONE, CountMessage(value=message.value + 1) - - class PrintValue(ez.Unit): - INPUT = ez.InputStream(CountMessage) - - @ez.subscriber(INPUT) - async def on_message(self, message) -> None: - print(message.value) - - components = { - "COUNT": Count(settings=CountSettings(iterations=10)), - "ADD_ONE": AddOne(), - "PRINT": PrintValue() - } - connections = ( - (components["COUNT"].OUTPUT_COUNT, components["ADD_ONE"].INPUT_COUNT), - (components["ADD_ONE"].OUTPUT_PLUS_ONE, components["PRINT"].INPUT) - ) - ez.run(components=components, connections=connections) - -|ezmsg_logo_small| Run the ezmsg pipeline from your script -*********************************************************** - -To run the pipeline, you can use the following command in the :term:`terminal`: - -.. code-block:: bash - - python3 tutorial_pipeline.py - -This will start the pipeline and begin processing data. - -.. important:: You can also run this ezmsg pipeline in a **Jupyter notebook** environment with a slight tweak. Simply copy the above into a code cell (or cells). But when using ``run()``, include the optional kwarg ``force_single_process=True``: - - .. code-block:: python - - ez.run(components=components, connections=connections, force_single_process=True) - -.. tip:: As mentioned before when running a script like this, `ezmsg` sets up a local `GraphServer` to manage the pipeline execution. It will automatically choose appropriate settings for the `GraphServer`, including the address (host and port). We often call this running ezmsg in "*sandbox mode*", because in order to connect to it you would need to guess the address set by ezmsg. - -The alternative, if you want the graphserver to use a particular address, is to either: - -1. set the ``EZMSG_GRAPH_SERVER`` environment variable before running your script. -2. start an ezmsg instance from the command line with the `serve` or `start` commands, which will allow you to specify the address and port. - -.. note:: For a deeper dive into the backend of ezmsg, please refer to the :doc:`../explanations/ezmsg` section. Here you will find more information about the ezmsg graphserver, how it works, and how to configure it. - - -|ezmsg_logo_small| Run the pipeline using the command line -************************************************************ - -The ``ezmsg`` command line interface exposes extra tools to manage a pipeline that is running in the background on a machine. Run ``ezmsg -h`` to see all the available options. Currently, there are five commands available: ``start``, ``serve``, ``shutdown``, ``mermaid``, and ``graphviz``. The first three are used to manage the ezmsg system, while the last two are used for visualising the pipeline. - -.. tip:: If you're working with the ezmsg repository, and you have a virtual environment set up through `uv`, you can ensure you're running with expected dependency versions by using `uv run` before all of the terminal commands in this section. For example: - - .. code-block:: bash - - # first terminal window - uv run ezmsg serve --address 127.0.0.1:25978 - - # second terminal window - uv run python3 tutorial_pipeline.py - - -Step 1. Start the ezmsg system -================================= - -Paste and run the following in your terminal: - -.. code-block:: bash - - ezmsg serve --address 127.0.0.1:25978 - -This command will start the ezmsg system and listen for incoming connections on host `127.0.0.1` at port `25978`. You can change the address and port as needed - the values here are actually the default that `ezmsg` will use if you leave out the ``--address`` option. - -.. note:: You will notice that with the command ``serve``, ezmsg takes over your terminal in the sense that it runs ezmsg in the foreground, not allowing you to use this terminal for other purposes. In order to continue using the terminal, you will need to open a new terminal (you can have multiple terminals open). If instead you use the command ``start``, ezmsg will be forked to another process allowing you to use the current terminal. This is useful for running ezmsg in the background or as a service: - - .. code-block:: bash - - ezmsg start --address 127.0.0.1:25978 - - -Step 2. Run the pipeline in ezmsg -================================== - -Now that an ezmsg instance is running one can connect and run a pipeline to it. -You will need to change the way you call ``ez.run()`` by adding an address to the call. - -In your script `tutorial_pipeline.py`, change the last line to: - -.. code-block:: python - - ez.run(components=components, connections=connections, address="127.0.0.1:25978") - -Now you can run your pipeline script from a **new** terminal window. - -.. code-block:: bash - - python3 tutorial_pipeline.py - - -Step 3. Shutdown ezmsg instance -================================= - -When you are done with ezmsg, you can shutdown the ezmsg system by running the following command in a new terminal window: - -.. code-block:: bash - - ezmsg shutdown --address 127.0.0.1:25978 - -This will gracefully shutdown the ezmsg system and release any resources it was using. Make sure to use the same address you used to start and run the ezmsg system. - -.. note:: If you used the ``serve`` command to start the ezmsg system, after running the ``shutdown`` command, you will see that the terminal where you started the ezmsg system will return to the command prompt. - - -|ezmsg_logo_small| Visualising the ezmsg pipeline -******************************************************* - -Other than the ``serve``, ``start`` and ``shutdown`` commands, the ezmsg command-line interface allows you to visualise your pipeline graphically. The two available commands are ``graphviz`` and ``mermaid``, corresponding to the two visualisation formats we support. - -While the pipeline is running, if you run the following in a new terminal window: - -.. code-block:: bash - - ezmsg graphviz --address 127.0.0.1:25978 - -then ezmsg will output a **Graphviz** representation of the pipeline to the terminal. - -.. image:: ../_static/_images/tutorial_graphviz.png - :width: 100% - :alt: Graphviz pipeline visualisation text output - -.. note:: You can copy this output and paste it into a Graphviz live editor, such as ``_, to view the pipeline graphically. - -.. note:: The really long numerical node identifiers are simply randomly generated unique identifiers for each connection point (since the names may be the same like `INPUT_SIGNAL`). These identifiers have no special meaning. - -.. warning:: This command and the ones described in the :ref:`mermaid-section` section below will only output the pipeline graph if the ezmsg system is running and has a pipeline connected to it. If you run this command when the pipeline is finished or not running, you will not get any output. Our example is so simple that it will finish very quickly, so in order to visualise the graph maybe change the `iterations` in the `CountSettings` to a larger number, e.g. 100000, so that you have time to run the command and see the output. - -.. _mermaid-section: - -Mermaid visualisation -=========================== - -To visualise your pipeline using **Mermaid**, you can use the following command: - -.. code-block:: bash - - ezmsg mermaid --address 127.0.0.1:25978 - -This will do two things: - -- generate a Mermaid diagram format text of your pipeline and display it in the terminal -- open a mermaid live editor in your browser with the diagram pre-loaded. - -.. image:: ../_static/_images/tutorial_graph.png - :width: 100% - :alt: Mermaid pipeline visualisation - -.. note:: If you would like to disable the automatic opening of the Mermaid live editor, you can use the ``--nobrowser`` (or ``-n``) flag and append it to the call to ``ezmsg mermaid``. Then, if you would like to view the diagram, you can still copy and paste the diagram code from your terminal into a `Mermaid live editor `_. - -Notice that the diagram shows all the Components and connections in the pipeline, as well as the streams that are being used. If you don't care about the streams themselves, and are interested more in the Components and their connections, you can append the ``--compact`` (or ``-c``) option to simplify the visualisation: - -.. image:: ../_static/_images/tutorial_graph_compact.png - :width: 100% - :alt: Compact Mermaid pipeline visualisation - - -|ezmsg_logo_small| Signal processing in ezmsg? -************************************************ - -Now that we have a basic ezmsg pipeline and can run it, it is time to learn about the full capabilities of ezmsg. In particular, we will explore how to use ezmsg for signal processing and make use of the specialized message format called :term:`AxisArray`. - -.. |ezmsg_logo_small| image:: ../_static/_images/ezmsg_logo.png - :width: 40 - :alt: ezmsg logo diff --git a/docs/source/tutorials/signalprocessing.rst b/docs/source/tutorials/signalprocessing.rst deleted file mode 100644 index f91c598c..00000000 --- a/docs/source/tutorials/signalprocessing.rst +++ /dev/null @@ -1,615 +0,0 @@ -Leveraging ezmsg For Signal Processing -############################################### - -`ezmsg` is a powerful framework for building signal processing applications. It provides a flexible and extensible architecture that allows users to create custom signal processors, integrate with ezmsg Units, and build complex processing pipelines. - -We will explore how to do this by recreating the `Downsample` signal processor unit. It will demonstrate how to create a signal processor, convert it to an ezmsg Unit, and use it in a processing pipeline. Additionally, it will provide a mini primer on the `AxisArray` class, which is the preferred ezmsg message format. - -.. tip:: Downsampling is a common signal processing operation that reduces the sampling rate of a signal by keeping only every nth sample. This is useful for reducing the amount of data to be processed, especially in real-time applications. - - -|ezmsg_logo_small| Choosing your signal processing class -********************************************************** - -We make use of the following decision tree to choose the appropriate signal processing class: - -.. graphviz:: - :align: center - - digraph signal_processor_decision_tree { - node [shape=box, style="rounded,filled", fillcolor="#f0f0f0", fontname="Arial"]; - edge [fontname="Arial"]; - - AMP [label="Multiple Processors?", fontcolor="#ff0000"]; - ARI [label="Receives Input?", fontcolor="#ff0000"]; - ACB [label="Single Chain / Branching?"]; - P [label="Producer", shape=diamond, fillcolor="#27f21cff"]; - APO [label="Produces Output?", fontcolor="#ff0000"]; - NBC [label="no base class", style="none"]; - ACRI [label="Receives Input?"]; - C [label="Consumer", shape=diamond, fillcolor="#27f21cff"]; - T [label="Transformer", shape=diamond, fillcolor="#27f21cff", fontcolor="#ff0000"]; - PS [label="Stateful?"]; - CS [label="Stateful?"]; - TS [label="Stateful?", fontcolor="#ff0000"]; - TSA [label="Adaptive?", fontcolor="#ff0000"]; - TSAF [label="Async First?", fontcolor="#ff0000"]; - CompositeProducer [style="none, filled", fillcolor="#effb1aff"]; - CompositeProcessor [style="none, filled", fillcolor="#effb1aff"]; - BaseProducer [style="none, filled", fillcolor="#effb1aff"]; - BaseStatefulProducer [style="none, filled", fillcolor="#effb1aff"]; - BaseConsumer [style="none, filled", fillcolor="#effb1aff"]; - BaseStatefulConsumer [style="none, filled", fillcolor="#effb1aff"]; - BaseTransformer [style="none, filled", fillcolor="#effb1aff"]; - BaseAdaptiveTransformer [style="none, filled", fillcolor="#effb1aff"]; - BaseStatefulTransformer [style="none, filled", fillcolor="#effb1aff", fontcolor="#ff0000"]; - BaseAsyncTransformer [style="none, filled", fillcolor="#effb1aff"]; - - AMP -> ARI [label="no", color="#ff0000", fontcolor="#ff0000"]; - AMP -> ACB [label="yes"]; - ARI -> P [label="no"]; - ARI -> APO [label="yes", color="#ff0000", fontcolor="#ff0000"]; - ACB -> NBC [label="branching"]; - ACB -> ACRI [label="single chain"]; - P -> PS; - APO -> C [label="no"]; - APO -> T [label="yes", color="#ff0000", fontcolor="#ff0000"]; - ACRI -> CompositeProducer [label="no"]; - ACRI -> CompositeProcessor [label="yes"]; - PS -> BaseProducer [label="no"]; - PS -> BaseStatefulProducer [label="yes"]; - C -> CS; - T -> TS [color="#ff0000", fontcolor="#ff0000"]; - CS -> BaseConsumer [label="no"]; - CS -> BaseStatefulConsumer [label="yes"]; - TS -> BaseTransformer [label="no"]; - TS -> TSA [label="yes", color="#ff0000", fontcolor="#ff0000"]; - TSA -> TSAF [label="no", color="#ff0000", fontcolor="#ff0000"]; - TSA -> BaseAdaptiveTransformer [label="yes"]; - TSAF -> BaseStatefulTransformer [label="no", color="#ff0000", fontcolor="#ff0000"]; - TSAF -> BaseAsyncTransformer [label="yes"]; - } -.. flowchart TD -.. AMP{Multiple Processors?}; -.. AMP -->|no| ARI{Receives Input?}; -.. AMP -->|yes| ACB{Single Chain / Branching?} -.. ARI -->|no| P(Producer); -.. ARI -->|yes| APO{Produces Output?}; -.. ACB -->|branching| NBC[no base class]; -.. ACB -->|single chain| ACRI{Receives Input?}; -.. P --> PS{Stateful?}; -.. APO -->|no| C(Consumer); -.. APO -->|yes| T(Transformer); -.. ACRI -->|no| CompositeProducer; -.. ACRI -->|yes| CompositeProcessor; -.. PS -->|no| BaseProducer; -.. PS -->|yes| BaseStatefulProducer; -.. C --> CS{Stateful?}; -.. T --> TS{Stateful?}; -.. CS -->|no| BaseConsumer; -.. CS -->|yes| BaseStatefulConsumer; -.. TS -->|no| BaseTransformer; -.. TS -->|yes| TSA{Adaptive?}; -.. TSA -->|no| TSAF{Async First?}; -.. TSA -->|yes| BaseAdaptiveTransformer; -.. TSAF -->|no| BaseStatefulTransformer; -.. TSAF -->|yes| BaseAsyncTransformer; - -In our case, we are creating a **single** signal processor that **receives input** and **produces output**. The decision tree indicates that we will be using a **transformer**-type base class. To continue, we need to determine if the processor is *stateful*, *adaptive* and *async first* or not. - -A stateful processor maintains internal state information that can affect its processing behavior, while a stateless processor does not maintain any internal state and processes each input independently. Adaptive transformers are a subtype of transformer that can adjust its settings based on trigger messages, whereas all other transformers are non-adaptive. Async first transformers prioritise asynchronous processing, meaning they can handle incoming messages without blocking, while non-async first transformers may block while processing messages. - -To answer whether our `Downsample` transformer is any of these types, we need to identify what we consider the settings (configuration) for the transformer and what we consider the state. - -A good rule of thumb is that settings are parameters used to configure the processor and are typically set once during initialization and remain constant. On the other hand, the processor state is internal data that the processor needs to maintain during its operation and can change dynamically as the processor processes data. - -We will see that `Downsample` is stateful, not adaptive and not async first, so we will inherit from the `BaseStatefulTransformer` class. This will become clearer as we implement the processor in the following sections. - -First, we need to install the `ezmsg-sigproc` package if we haven't already. This package contains the base classes for signal processing in ezmsg. You can install it using pip: - -.. code-block:: bash - - pip install "ezmsg[sigproc]" - - -|ezmsg_logo_small| Creating the `Downsample` signal processor -************************************************************* - -We begin by identifying the components needed to create the `Downsample` signal processor. This includes defining the settings, state, and the main processing class itself. - -First create a new Python file named `downsample.py` in your root directory. In this file we will implement the `Downsample` signal processor. - -Add the following import statements to the top of the `downsample.py` file: - -.. code-block:: python - - # downsample.py - import numpy as np - from ezmsg.util.messages.axisarray import ( - AxisArray, - slice_along_axis, - replace, - ) - import ezmsg.core as ez - - from ezmsg.sigproc.base import ( - BaseStatefulTransformer, - BaseTransformerUnit, - processor_state, - ) - -.. note:: These are modules we will need in the implementation and will be explained as we go along. You will notice that we import `numpy` (for numerical operations), `AxisArray` (this is our class for handling multi-dimensional arrays with named axes), and from `ezmsg-sigproc`, we import the `BaseStatefulTransformer` class and the `BaseTransformerUnit` (for wrapping our processor into an ezmsg unit). - - -DownsampleSettings class -==================================== - -To create a `Downsample` signal processor, we first define the settings for the processor. The parameters that we need to know for the transformer to operate include: - -- the axis along which to downsample. -- desired rate after downsampling has occurred, or -- the desired factor by which to downsample. - -Thus, your settings class will look like this: - -.. code-block:: python - - class DownsampleSettings(ez.Settings): - """ - Settings for :obj:`Downsample` node. - """ - - axis: str = "time" - """The name of the axis along which to downsample.""" - - target_rate: float | None = None - """Desired rate after downsampling. The actual rate will be the nearest integer factor of the input rate that is the same or higher than the target rate.""" - - factor: int | None = None - """Explicitly specify downsample factor. If specified, target_rate is ignored.""" - -There are no ``__init__`` methods that you might expect because we are inheriting from ``ez.Settings``, which uses Python's dataclass functionality to automatically generate the ``__init__`` method based on the class attributes. - -.. tip:: It is very good practice to name your settings class with the name of your processor followed by `Settings`. This makes it easy to identify the settings class for a given processor. - -The fact that we will not ever need to change these settings implies we do not need use of an adaptive transformer. - -DownsampleState class -======================== - -For the general operation of the `Downsample` processor, we need to keep track of the downsampling factor (since this could change per message) and the index of the next message's first sample (for maintaining continuity in the downsampled output), especially when processing a stream of data. - -The fact that we need to maintain state information implies that we will need to use a stateful transformer. - -Your state class will look like this: - -.. code-block:: python - - @processor_state - class DownsampleState: - q: int = 0 - """The integer downsampling factor. It will be determined based on the target rate.""" - - s_idx: int = 0 - """Index of the next msg's first sample into the virtual rotating ds_factor counter.""" - -Again, our class seems to be missing an ``__init__`` method, but this is because we are using the ``@processor_state`` decorator from `ezmsg-sigproc`, which automatically generates the ``__init__`` method for us. Just another way to make our code cleaner and more maintainable. - -.. note:: It is very good practice to name your state class with the name of your processor followed by `State`. This makes it easy to identify the state class for a given processor. - -.. note:: Finally, our transformer is **not async first** as we do not need to prioritise asynchronous processing, which is usually more relevant for processors that interface with IO operations whose timing is unpredictable. - -|ezmsg_logo_small| DownsampleTransformer Class -******************************************************* - -We have already identified that we will be using a stateful transformer, so we will inherit from the ``BaseStatefulTransformer`` class. Create the class definition as follows: - -.. code-block:: python - - class DownsampleTransformer( - BaseStatefulTransformer[DownsampleSettings, AxisArray, AxisArray, DownsampleState] - ): - """ - Downsampled data simply comprise every `factor`th sample. - This should only be used following appropriate lowpass filtering. - If your pipeline does not already have lowpass filtering then consider - using the :obj:`Decimate` collection instead. - """ - - def _hash_message(self, message: AxisArray) -> int: ... - - def _reset_state(self, message: AxisArray) -> None: ... - - def _process(self, message: AxisArray) -> AxisArray: ... - -.. note:: The `BaseStatefulTransformer` class is a generic class that takes four type parameters: the settings type, the input message type, the output message type, and the state type. In our case, the settings type is `DownsampleSettings`, the input and output message types are both `AxisArray`, and the state type is `DownsampleState`. - - -As can be seen above we must implement the following methods: - -- ``_hash_message``: This method is used to generate a hash for the input message. This is useful for caching and avoiding redundant processing. -- ``_reset_state``: This method is used to reset the internal state of the processor. This is useful when starting a new processing session or when the input data changes significantly. -- ``_process``: This is the main processing method where the downsampling logic will be implemented. - -The first two methods deal with the state of the processor (and are only required for stateful processors), while the third method is where the actual downsampling logic will be implemented. - -.. important:: ``_process`` is a necessary method for all transformers and consumers. The equivalent method for producers is called ``_produce``. For non-stateful processors, this will be the only method you need to implement if you inherit from the relevant base class. All other methods are preimplemented for you, but you can override them if needed. - -In order to implement these methods, we need to understand our preferred message format: `AxisArray`. This is a flexible and powerful class for handling multi-dimensional arrays with named axes, which is particularly useful for signal processing applications. I have already used `AxisArray` in our code as the input message and output message types. - -A detailed explanation of the `AxisArray` class is beyond the scope of this tutorial, but you can refer to the :doc:`AxisArray explainer <../explanations/axisarray>` as well as the :doc:`API reference <../reference/API/axisarray>` for more information. - -Brief Aside on AxisArray -================================= - -An ``AxisArray`` is a multi-dimensional array with named axes. Each axis can have a name and a set of labels for its elements. This allows for more intuitive indexing and manipulation of the data. - -An `AxisArray` has the following attributes: - -- ``data``: a numpy ndarray containing the actual data. -- ``dims``: a list of axis names. -- ``axes``: a dictionary mapping axis names to their label information. -- ``attrs``: a dictionary for storing additional metadata. -- ``key``: a unique identifier for the array. - -Unsurprisingly, all of this must be self-consistent: the number of axis names in ``dims`` must match the number of dimensions in ``data``, and the axis names in ``axes`` should match the ones in ``dims``. The label information in ``axes`` refers to the 'value' of each axis index, e.g., for a time axis, the labels might be timestamps. We provide three commonly used axes type objects: - -- A ``LinearAxis``: represents a linear axis with evenly spaced values - you just need the ``offset`` (start value) and the ``gain`` (step size). An example of this would be simple numerical index (offset=0, gain=1) or regularly spaced time samples (offset=start time, gain=1/sampling rate). -- A ``TimeAxis``: this is a `LinearAxis` that represents a time axis. Its ``unit`` attribute is by default set to seconds (s). -- A ``CoordinateAxis``: this is our continuous/dense axis, which can represent any continuous variable, such as frequency or spatial coordinates. You provide the actual values for each index in a ``data`` array of values. - -The `AxisArray` class provides several methods for manipulating and accessing the data, and the one we will be using in our `Downsample` processor is ``slice_along_axis``. This method allows us to slice the array along a specified axis, which is essential for downsampling. - -Hashing the State -=========================== - -We can generate a unique hash for the input message using the `key` attribute of the `AxisArray` which we tend to use for identifying what device our data has come from as well as an identifier of the message structure (in this case, the `gain` of the axes containing the data). Since downsampling requires messages to come with linearly spaced data, our axes will either be a `LinearAxis` or a `TimeAxis`, so this attribute will exist. - -Our implementation of the ``_hash_message`` method will look like this: - -.. code-block:: python - - def _hash_message(self, message: AxisArray) -> int: - return hash((message.axes[self.settings.axis].gain, message.key)) - -.. note:: The idea here is that if either the gain of the axis or the key of the message changes, we are dealing with different data, so we need to reevaluate our state. Importantly, the `DownsampleTransformer` *can* be implemented in a stateless way, but this would require computing the downsampling factor and first sample index every time, and hence a much less efficient implementation. - - -Resetting the State -================================= - -The ``_reset_state`` method is used to reset the internal state of the processor when a message is received with a hash different than that stored by the `DownsampleTransformer`. We need to reset the downsampling factor and the index of the next message's first sample. This is important when starting a new processing session or when the input data changes shape (like a different sampling rate). - -We set the downsampling factor either to the one in `DownsampleSettings` if specified, else we compute it based on the target rate and the input message rate. If target rate is not specified, we default to a downsampling factor of 1 (no downsampling). If a target rate is specified, we compute the downsampling factor as the nearest integer that is the same or higher than the ratio of the input rate to the target rate. If the final downsampling factor is less than 1 (not a valid value), we set it to 1 (no downsampling). - -Finally, we reset the index of the next message's first sample to 0. - -.. code-block:: python - - def _reset_state(self, message: AxisArray) -> None: - axis = message.get_axis(self.settings.axis) - - if self.settings.factor is not None: - q = self.settings.factor - elif self.settings.target_rate is None: - q = 1 - else: - q = int(1 / (axis.gain * self.settings.target_rate)) - if q < 1: - ez.logger.warning( - f"Target rate {self.settings.target_rate} cannot be achieved with input rate of {1 / axis.gain}." - "Setting factor to 1." - ) - q = 1 - self._state.q = q - self._state.s_idx = 0 - - -.. _processing_data_tutorial: - -|ezmsg_logo_small| Processing the Data -*********************************************** - -To finish the `DownsampleTransformer` class, we need to actually process the data by downsampling. -This is done in the ``_process`` method. We will use some of the methods provided by the `AxisArray` class to help us with this. - -Step 1: Getting the indices to slice the data -========================================================= - -We first get the index of the axis (`axis_idx`) and the axis itself (`axis`) along which we want to downsample. We then determine the number of samples in the input message along that axis: - -.. code-block:: python - - downsample_axis = self.settings.axis - axis = message.get_axis(downsample_axis) - axis_idx = message.get_axis_idx(downsample_axis) - n_samples = message.data.shape[axis_idx] - -Next, create a linear range of indices starting from the current index of the next message's first sample (`self._state.s_idx`) to the current index plus the number of samples in the input message. We use modulo operation with the downsampling factor (`self._state.q`) to create a virtual rotating counter. If the number of samples is greater than 0, we update the index of the next message's first sample for the next iteration. Our slice object is the indices where the virtual counter is 0, which corresponds to the samples we want to keep after downsampling: - -.. code-block:: python - - samples = ( - np.arange(self.state.s_idx, self.state.s_idx + n_samples) % self._state.q - ) - if n_samples > 0: - # Update state for next iteration. - self._state.s_idx = samples[-1] + 1 - - pub_samples = np.where(samples == 0)[0] - if len(pub_samples) > 0: - n_step = pub_samples[0].item() - data_slice = pub_samples - else: - n_step = 0 - data_slice = slice(None, 0, None) - -Here `pub_samples` corresponds to the samples we want to keep after downsampling - they are the zeros in our virtual counter. If there are any samples to publish, we set `n_step` to the first index in `pub_samples` (ie. the first zero) and `data_slice` to `pub_samples`. If there are no samples to publish, we set `n_step` to 0 and `data_slice` to an empty slice. - -Step 2: Slicing the data and updating the axis -========================================================= - -We will create the output message by first creating our new numpy ndarray by slicing the input message's data along the specified axis using the `slice_along_axis` function from the `AxisArray` class. Then we will update the axis information to reflect the downsampling. Finally, we create a new `AxisArray` message with the downsampled data and updated axes using the ``replace`` function from the `AxisArray` class. - -The slicing of the data is done as follows: - -.. code-block:: python - - slice_along_axis(message.data, sl=data_slice, axis=axis_idx) - - -We also need to update the axis information to reflect the downsampling. All other axes stay as before, but the one we downsampled on (`downsample_axis`) needs to be updated. The gain of the axis is multiplied by the downsampling factor, and the offset is updated based on the number of steps taken in the virtual counter: - -.. code-block:: python - - from ezmsg.util.messages.axisarray import replace - - new_axes={ - **message.axes, - downsample_axis: replace( - axis, - gain=axis.gain * self._state.q, - offset=axis.offset + axis.gain * n_step, - ), - } - -.. important:: The ``replace`` function is a utility function provided by the `AxisArray` class that allows us to create a new object with updated attributes while keeping the other attributes unchanged. It is very fast by avoiding deep copies of the entire object and safety checks that usually occur at object creation time. Its signature is ``replace(obj: T, **changes) -> T``, where `obj` is the object to be updated and `**changes` are the attributes to be updated with their new values. For performance reasons, we **strongly suggest** using the ``replace`` function whenever you are transforming an `AxisArray` message and do not need its previous state. - -.. tip:: If, on the contrary, you would prefer a safer (but slower) implementation, you can set the environment variable ``EZMSG_DISABLE_FAST_REPLACE=1`` before running your code. It will then use the Python `dataclasses` implementation of ``replace`` with consistency checks. - - -Step 3: Creating the output message -========================================================= - -Finally, we create the output message: - -.. code-block:: python - - msg_out = replace( - message, - data=slice_along_axis(message.data, data_slice, axis=axis_idx), - axes={ - **message.axes, - downsample_axis: replace( - axis, - gain=axis.gain * self._state.q, - offset=axis.offset + axis.gain * n_step, - ), - }, - ) - -.. note:: We used ``replace`` to create the output message, updating only the `data` and `axes` attributes while keeping the other attributes (like `dims`, `attrs`, and `key`) unchanged. - -Step 4: Putting it all together -========================================================= - -The final implementation of the ``_process`` method looks like this: - -.. code-block:: python - - def _process(self, message: AxisArray) -> AxisArray: - downsample_axis = self.settings.axis - axis = message.get_axis(downsample_axis) - axis_idx = message.get_axis_idx(downsample_axis) - - n_samples = message.data.shape[axis_idx] - samples = ( - np.arange(self.state.s_idx, self.state.s_idx + n_samples) % self._state.q - ) - if n_samples > 0: - # Update state for next iteration. - self._state.s_idx = samples[-1] + 1 - - pub_samples = np.where(samples == 0)[0] - if len(pub_samples) > 0: - n_step = pub_samples[0].item() - data_slice = pub_samples - else: - n_step = 0 - data_slice = slice(None, 0, None) - msg_out = replace( - message, - data=slice_along_axis(message.data, data_slice, axis=axis_idx), - axes={ - **message.axes, - downsample_axis: replace( - axis, - gain=axis.gain * self._state.q, - offset=axis.offset + axis.gain * n_step, - ), - }, - ) - return msg_out - - -|ezmsg_logo_small| Final DownsampleTransformer Class -******************************************************* - -Confirm that your final `DownsampleTransformer` class looks like this: - -.. code-block:: python - - class DownsampleTransformer( - BaseStatefulTransformer[DownsampleSettings, AxisArray, AxisArray, DownsampleState] - ): - """ - Downsampled data simply comprise every `factor`th sample. - This should only be used following appropriate lowpass filtering. - If your pipeline does not already have lowpass filtering then consider - using the :obj:`Decimate` collection instead. - """ - - def _hash_message(self, message: AxisArray) -> int: - return hash((message.axes[self.settings.axis].gain, message.key)) - - def _reset_state(self, message: AxisArray) -> None: - axis = message.get_axis(self.settings.axis) - - if self.settings.factor is not None: - q = self.settings.factor - elif self.settings.target_rate is None: - q = 1 - else: - q = int(1 / (axis.gain * self.settings.target_rate)) - if q < 1: - ez.logger.warning( - f"Target rate {self.settings.target_rate} cannot be achieved with input rate of {1 / axis.gain}." - "Setting factor to 1." - ) - q = 1 - self._state.q = q - self._state.s_idx = 0 - - def _process(self, message: AxisArray) -> AxisArray: - downsample_axis = self.settings.axis - axis = message.get_axis(downsample_axis) - axis_idx = message.get_axis_idx(downsample_axis) - - n_samples = message.data.shape[axis_idx] - samples = ( - np.arange(self.state.s_idx, self.state.s_idx + n_samples) % self._state.q - ) - if n_samples > 0: - # Update state for next iteration. - self._state.s_idx = samples[-1] + 1 - - pub_samples = np.where(samples == 0)[0] - if len(pub_samples) > 0: - n_step = pub_samples[0].item() - data_slice = pub_samples - else: - n_step = 0 - data_slice = slice(None, 0, None) - msg_out = replace( - message, - data=slice_along_axis(message.data, data_slice, axis=axis_idx), - axes={ - **message.axes, - downsample_axis: replace( - axis, - gain=axis.gain * self._state.q, - offset=axis.offset + axis.gain * n_step, - ), - }, - ) - return msg_out - - -|ezmsg_logo_small| Using the DownsampleTransformer -********************************************************** - -The `Downsample` class is now fully implemented and ready for use in signal processing pipelines. -You can even use it outside of an ezmsg context by instantiating it directly and calling its ``_process`` method with an `AxisArray` message. - -.. important:: The preferred way to call the ``_process`` method is to call the instance directly; below you will see that in the line: ``msg_out = downsampler(msg_in)``. This is possible because all of the processor base classes implement the ``__call__`` method, to call the ``_process`` method internally (or ``_produce`` in the case of `Producers`). - -In a separate Python file in the same directory, you can test the `DownsampleTransformer` class as follows: - -.. code-block:: python - - # test_downsample.py - from downsample import DownsampleTransformer, DownsampleSettings - import ezmsg.core as ez - from ezmsg.util.messages.axisarray import AxisArray, LinearAxis - import numpy as np - - # Create a DownsampleTransformer instance with desired settings. - settings = DownsampleSettings(axis="time", target_rate=50) # Target rate of 50 Hz. - downsampler = DownsampleTransformer(settings) - - # Create a sample AxisArray message with a time axis and some data. - time_axis = LinearAxis(offset=0.0, gain=0.01) # 100 Hz sampling rate. - data = np.random.rand(1000) # 1000 samples of random data. - msg_in = AxisArray( - data=data, - dims=["time"], - axes={"time": time_axis}, - key="example_device", - ) - - # Process the message to downsample it. - msg_out = downsampler(msg_in) - - print(f"Input shape: {msg_in.data.shape}, Output shape: {msg_out.data.shape}") - print(f"Input time axis gain: {msg_in.axes['time'].gain}, Output time axis gain: {msg_out.axes['time'].gain}") - -Doing the above is very handy for unit testing your processor as well as for offline processing of data. - -.. note:: The `downsample` module in `ezmsg-sigproc` has a utility function for creating a `DownsampleTransformer` instance with the desired settings: - - .. code-block:: python - - def downsample( - axis: str = "time", - target_rate: float | None = None, - factor: int | None = None, - ) -> DownsampleTransformer: - return DownsampleTransformer( - DownsampleSettings(axis=axis, target_rate=target_rate, factor=factor) - ) - - After importing this utility function, lines 8 and 9 in our code above could now read: - - .. code-block:: python - - downsampler = downsample(axis="time", target_rate=50) - -Of course, the real power of `ezmsg` comes from integrating your processor into an `ezmsg` Unit and using it in a processing pipeline. We will see how to do this next. - - -|ezmsg_logo_small| Creating the `Downsample ezmsg` Unit -*********************************************************** - -`ezmsg-sigproc` provides convenient ezmsg `Unit` wrappers for all the signal processor base classes. To do this inherit from the appropriate `ezmsg-sigproc` unit class. These are: - -- `BaseProducerUnit` -- `BaseConsumerUnit` -- `BaseTransformerUnit` - -The names correspond to the type of base processor class you are using. Importantly, these unit classes are agnostic to whether your processor is stateful/adaptive/async first - they will work with any of the processor base classes. - -Our `Downsample` processor is a stateful transformer, so we will inherit from the `BaseTransformerUnit` class. - -A lot of the behind-the-scenes work is done for you by the `BaseTransformerUnit` class, so we only need to write the following: - -.. code-block:: python - - class DownsampleUnit( - BaseTransformerUnit[DownsampleSettings, AxisArray, AxisArray, DownsampleTransformer] - ): - SETTINGS = DownsampleSettings - - -Connecting it to other `Component`\ s and initialising the transformer are accomplished in the same way that we did in the :doc:`pipeline tutorial `. - - -|ezmsg_logo_small| See Also -************************************ - -- `Further examples `_ can be found in the examples directory in `ezmsg`. These are examples of creating and using `ezmsg` Units and pipelines. -- `ezmsg-sigproc` has a large number of already implemented signal processors. More information can be found at the :doc:`ezmsg-sigproc reference <../extensions/sigproc/content-sigproc>`. -- `Downsample` class reference - -.. |ezmsg_logo_small| image:: ../_static/_images/ezmsg_logo.png - :width: 40 - :alt: ezmsg logo \ No newline at end of file diff --git a/docs/source/tutorials/start.rst b/docs/source/tutorials/start.rst deleted file mode 100644 index 15292aec..00000000 --- a/docs/source/tutorials/start.rst +++ /dev/null @@ -1,197 +0,0 @@ -Installation and Configuration -############################### - -This page contains information about installing ezmsg and its extensions. It also provides instructions for developers who want to contribute to the ezmsg project. - - -|ezmsg_logo_small| System Requirements -*************************************** - -ezmsg is written in and for **Python**, so it requires a Python interpreter to run. Currently, ezmsg requires at minimum Python 3.10. Beyond that, ezmsg is a pure Python library with no external dependencies. - -This also means that ezmsg is cross-platform and should run on any operating system that supports Python, including Windows, macOS, and Linux. - -In Windows and macOS, Python can be downloaded from the official website: `python.org `_. For newer users, we recommend using the `Anaconda `_ distribution of Python, which comes with a package manager and many useful libraries pre-installed. - -On Linux, Python is usually pre-installed, but you can also install it using your package manager in your :term:`terminal`: - -.. code-block:: bash - - # For Debian/Ubuntu-based systems - sudo apt install python3. python3-pip -y - - # For Red Hat/CentOS-based systems - sudo dnf install python3. python3-pip -y - - # For Arch Linux-based systems - sudo pacman -S python3. python-pipx - -where ``3.`` is the version of Python you want to install (e.g. 3.12). - -Whichever you choose, ensure that you have Python 3.8 or later installed on your system. You can verify your Python version by running in your terminal: - -.. code-block:: bash - - python3 --version - - -|ezmsg_logo_small| Installing ezmsg -************************************ - -We can install ezmsg using :term:`pip`, which is the package manager for Python. It will handle all dependencies and ensure that you have the latest version. It will download the package from the Python Package Index (:term:`PyPI`) and install it on your system. - -You can alternatively run ezmsg from source by cloning the repository, but this is only recommended for those planning to do some ezmsg development or if you have specific customization needs. - -Regardless of the method you choose, you will need to use the :term:`terminal`. - -From PyPI (using pip) -====================== - -To install the ezmsg framework, you can use pip in your terminal: - -.. code-block:: bash - - pip install ezmsg - -Once installed, you can start using ezmsg in your projects. You can skip straight to the section on :ref:`updating-ezmsg`. - -From source (using git) -========================== - -You can also install ezmsg from the source (GitHub respository). This requires a lot more steps, but it is useful if you want to contribute to the project or if you want to run the latest development version of ezmsg. - -.. important:: If you plan to make changes to the code or contribute to the project, please see the :doc:`Developer Guide <../developer/content-developer>` for more information on how to develop with ezmsg. - -Step 1. Clone the project from GitHub -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. note:: This step assumes you have a GitHub account. We generally recommend having a username or email associated with your account that can be tied to you. If you plan on developing for ezmsg, this, along with SSH keys and 2FA will be strictly necessary. - -.. image:: ../_static/_images/cloning.png - :width: 200 - :align: center - :alt: GitHub Clone URL image - -This is done by visiting the `ezmsg repository `_. There is a green button called "Code" (see the above image) which, when clicked, will show you the URL to clone the repository. If you simply plan to build from source, you can choose the HTTPS URL. - -If you plan on contributing to the project, follow the instructions in the :doc:`Developer section <../developer/install>` to first fork the repository to your own account and then clone using the SSH URL. You will then be required to set up SSH keys as explained in `this link `_. - -Once you have the URL, you can clone the respository using the following commands in your terminal, replacing ```` with the path where you want to clone the repository and ```` with the URL you copied from GitHub: - -.. code-block:: bash - - cd - git clone - -.. note:: Once you clone the repository there will be a new directory called ``ezmsg`` in that location. So, don't create a new directory called ``ezmsg`` in that location, just clone it directly into the parent directory. - -Step 2. Create a virtual environment for this repository -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: bash - - cd ezmsg - python3 -m venv env - -Activating the virtual environment is necessary to ensure that the packages you install do not interfere with your system Python packages. This is done differently depending on your operating system. - -If on **Windows**, you can use the following command: - -.. code-block:: powershell - - env\Scripts\activate - -If on **Unix-based systems** (Linux, macOS), you can use the following command: - -.. code-block:: bash - - source env/bin/activate - -Now that the virtual environment is activated, your terminal prompt should change to indicate that you are now in the virtual environment (it will likely have something akin to ``(env)`` appended to the prompt). - -Step 3. Install ezmsg and its dependencies -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: bash - - pip install --upgrade pip - pip install wheel # Optional, may be useful depending on your platform - pip install -e ".[test]" - -Step 4. (Optionally) Run the repository tests -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Running the tests ensures everything is working correctly. - -.. code-block:: bash - - python -m pytest -v tests - -.. _updating-ezmsg: - -|ezmsg_logo_small| How to update ezmsg -*************************************** - -Updating ezmsg ensures you have the latest features, improvements, and bug fixes. - -If you installed ezmsg using pip, you can easily update it using the following command in your terminal: - -.. code-block:: bash - - pip install --upgrade ezmsg - -If you installed ezmsg by cloning the ezmsg repository, you can pull the latest changes from the main branch: - -.. code-block:: bash - - git pull origin main - - -|ezmsg_logo_small| Confirming installation -******************************************* - -To confirm that ezmsg is installed correctly, you can run the following command: - -.. code-block:: bash - - pip show ezmsg - -This will display information about the installed package, including its version and location. -You can also run a simple test script: - -.. code-block:: python - - # test_ezmsg.py - import ezmsg - - print("ezmsg is installed and working correctly!") - -with the following code (in the terminal) to check if ezmsg is functioning as expected: - -.. code-block:: bash - - python3 test_ezmsg.py - - -|ezmsg_logo_small| Installing Extensions -***************************************** - -ezmsg comes with a whole host of extensions that can be installed to add extra functionality. All of these are optional and can be installed as needed. - -To install an extension, you can use pip: - -.. code-block:: bash - - pip install ezmsg[extension_name] - -For more information on available extensions, please refer to the :doc:`Extensions page <../extensions/content-extensions>`. - - -|ezmsg_logo_small| Ready to build your first ezmsg pipeline? -************************************************************** - -You are now ready to start building your first ezmsg pipeline! Click Next below or head to :doc:`pipeline` to get started. - -.. |ezmsg_logo_small| image:: ../_static/_images/ezmsg_logo.png - :width: 40 - :alt: ezmsg logo diff --git a/pyproject.toml b/pyproject.toml index d201a5b7..cbe88ce7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,20 +34,19 @@ test = [ "pytest>=8.4.1", "pytest-asyncio>=1.1.0", "pytest-cov>=6.2.1", - "xarray>=2023.1.0;python_version<'3.13'" + "xarray>=2025.6.1", ] docs = [ + {include-group = "axisarray"}, "sphinx>=8.1.3", "pydata-sphinx-theme", - "ezmsg-sigproc>=2.2.0", "sphinxext.rediraffe", - "myst_parser", "sphinx_autodoc_typehints>=3.0.0", "sphinx_copybutton", "sphinx_design", ] axisarray = [ - "numpy>=1.24.4", + "numpy>=2.2.6", ] [project.scripts] diff --git a/src/ezmsg/core/backend.py b/src/ezmsg/core/backend.py index 37f6cd24..2d988d33 100644 --- a/src/ezmsg/core/backend.py +++ b/src/ezmsg/core/backend.py @@ -186,9 +186,9 @@ def run( execution environment, initializes components, and manages the message-passing infrastructure. - On initialization, ``ezmsg`` will call ``initialize()`` for each :obj:`Unit` and - ``configure()`` for each :obj:`Collection`, if defined. On initialization, ``ezmsg`` - will create a directed acyclic graph using the contents of ``connections``. + On initialization, ezmsg will call ``initialize()`` for each :obj:`Unit` and + ``configure()`` for each :obj:`Collection`, if defined. On initialization, ezmsg + will create a directed acyclic graph using the contents of ``connections``. :param components: Dictionary mapping component names to Component objects. The components are the nodes in the ezmsg (directed acyclic) graph. @@ -203,15 +203,16 @@ def run( :param backend_process: Backend process class to use for execution. Currently under development. :type backend_process: type[BackendProcess] :param graph_address: Address (hostname and port) of graph server which ezmsg should connect to. - If not defined, ``ezmsg`` will start a new graph server at 127.0.0.1:25978. + If not defined, ezmsg will start a new graph server at 127.0.0.1:25978. :type graph_address: AddressType | None :param force_single_process: Whether to force all components into a single process :type force_single_process: bool :param components_kwargs: Additional components specified as keyword arguments :type components_kwargs: Component - .. note:: + .. note:: Since jupyter notebooks run in a single process, you must set `force_single_process=True`. + .. note:: The old method :obj:`run_system` has been deprecated and uses ``run()`` instead. """ diff --git a/src/ezmsg/core/collection.py b/src/ezmsg/core/collection.py index 4e038326..4ac6e8f5 100644 --- a/src/ezmsg/core/collection.py +++ b/src/ezmsg/core/collection.py @@ -40,7 +40,7 @@ def __init__( class Collection(Component, metaclass=CollectionMeta): """ - Connects :obj:`Unit`\ s together by defining a graph which connects OutputStreams to InputStreams. + Connects :obj:`Units ` together by defining a graph which connects OutputStreams to InputStreams. Collections are composite components that contain and coordinate multiple Units, defining how they communicate through stream connections. diff --git a/src/ezmsg/core/component.py b/src/ezmsg/core/component.py index fb3a2325..d9e42902 100644 --- a/src/ezmsg/core/component.py +++ b/src/ezmsg/core/component.py @@ -107,7 +107,8 @@ class Component(Addressable, metaclass=ComponentMeta): :type settings: Settings | None .. note:: - When creating ezmsg nodes, inherit directly from :obj:`Unit` or :obj:`Collection`. + + When creating ezmsg nodes, inherit directly from :obj:`Unit` or :obj:`Collection`. """ _tasks: dict[str, Callable] # Only Units will have tasks @@ -194,7 +195,7 @@ def tasks(self) -> dict[str, Callable]: Get the dictionary of tasks for this component. :return: Dictionary mapping task names to their callable functions - :rtype: dict[str, Callable] + :rtype: dict[str, collections.abc.Callable] """ return self._tasks @@ -224,7 +225,7 @@ def main(self) -> Callable[..., None] | None: Get the main function for this component. :return: The main callable function, or None if not set - :rtype: Callable[..., None] | None + :rtype: collections.abc.Callable[..., None] | None """ return self._main @@ -234,6 +235,6 @@ def threads(self) -> dict[str, Callable]: Get the dictionary of thread functions for this component. :return: Dictionary mapping thread names to their callable functions - :rtype: dict[str, Callable] + :rtype: dict[str, collections.abc.Callable] """ return self._threads diff --git a/src/ezmsg/core/message.py b/src/ezmsg/core/message.py index 80f78615..ca3225d0 100644 --- a/src/ezmsg/core/message.py +++ b/src/ezmsg/core/message.py @@ -28,7 +28,7 @@ class Message(ABC, metaclass=MessageMeta): .. deprecated:: Message is deprecated. Use @dataclass decorators instead of inheriting - from ez.Message. For data arrays, use :obj:`ezmsg.core.util.messages.AxisArray`. + from ez.Message. For data arrays, use :obj:`ezmsg.util.messages.AxisArray`. .. note:: This class will issue a DeprecationWarning when instantiated. diff --git a/src/ezmsg/core/state.py b/src/ezmsg/core/state.py index 1d0323d0..385f3520 100644 --- a/src/ezmsg/core/state.py +++ b/src/ezmsg/core/state.py @@ -48,7 +48,7 @@ class YourState(State): state2: float To use, declare the ``State`` object for a ``Component`` as a member variable called (all-caps!) ``STATE``. - ``ezmsg`` will monitor the variable called ``STATE`` in the background, so it is important to name it correctly. + ezmsg will monitor the variable called ``STATE`` in the background, so it is important to name it correctly. Member functions can then access and mutate ``STATE`` as needed during function execution. It is recommended to initialize state values inside the ``initialize()`` or ``configure()`` lifecycle hooks if diff --git a/src/ezmsg/core/unit.py b/src/ezmsg/core/unit.py index 5fd727c4..045019a1 100644 --- a/src/ezmsg/core/unit.py +++ b/src/ezmsg/core/unit.py @@ -79,7 +79,7 @@ def __init__(self, *args, settings: Settings | None = None, **kwargs): self._main = self.__class__.__main__ self._threads = self.__class__.__threads__ - async def setup(self): + async def setup(self) -> None: """This is called from within the same process this unit will live""" self._instantiate_state() if not self._settings_applied: @@ -131,9 +131,11 @@ def publisher(stream: OutputStream): :param stream: The output stream to publish messages to :type stream: OutputStream :return: Decorated function that can publish to the stream - :rtype: Callable + :rtype: collections.abc.Callable :raises ValueError: If stream is not an OutputStream + Example usage: + .. code-block:: python from collections.abc import AsyncGenerator @@ -170,9 +172,11 @@ def subscriber(stream: InputStream, zero_copy: bool = False): :param zero_copy: Whether to use zero-copy message passing (default: False) :type zero_copy: bool :return: Decorated function that can subscribe to the stream - :rtype: Callable + :rtype: collections.abc.Callable :raises ValueError: If stream is not an InputStream + Example usage: + .. code-block:: python INPUT = ez.InputStream(Message) @@ -205,9 +209,9 @@ def main(func: Callable): processing, or cleanup tasks. :param func: The function to designate as main - :type func: Callable + :type func: collections.abc.Callable :return: The decorated function - :rtype: Callable + :rtype: collections.abc.Callable """ setattr(func, MAIN_ATTR, True) return func @@ -222,13 +226,13 @@ def timeit(func: Callable): optimization. The execution time is logged in milliseconds. .. note:: Use the ``@profile_subpub`` or ``@profile_method`` decorators - from ezmsg-sigproc for more detailed profiling that is stored in a - dedicated profiling log file. + from ezmsg-sigproc for more detailed profiling that is stored in a + dedicated profiling log file. :param func: The function to time - :type func: Callable + :type func: collections.abc.Callable :return: The decorated function with timing functionality - :rtype: Callable + :rtype: collections.abc.Callable """ setattr(func, TIMEIT_ATTR, True) @@ -253,9 +257,9 @@ def thread(func: Callable): for background tasks, monitoring, or other concurrent operations. :param func: The function to run as a background thread - :type func: Callable + :type func: collections.abc.Callable :return: The decorated function - :rtype: Callable + :rtype: collections.abc.Callable """ setattr(func, THREAD_ATTR, True) return func @@ -269,9 +273,9 @@ def task(func: Callable): within the unit's primary execution context. :param func: The function to run as a task - :type func: Callable + :type func: collections.abc.Callable :return: The decorated function - :rtype: Callable + :rtype: collections.abc.Callable """ setattr(func, TASK_ATTR, True) return func @@ -285,9 +289,9 @@ def process(func: Callable): for CPU-intensive operations or when process isolation is required. :param func: The function to run in its own process - :type func: Callable + :type func: collections.abc.Callable :return: The decorated function - :rtype: Callable + :rtype: collections.abc.Callable """ setattr(func, PROCESS_ATTR, True) return func diff --git a/src/ezmsg/util/messagecodec.py b/src/ezmsg/util/messagecodec.py index 3e408cc9..779be16a 100644 --- a/src/ezmsg/util/messagecodec.py +++ b/src/ezmsg/util/messagecodec.py @@ -2,6 +2,7 @@ Message encoding and decoding utilities for ezmsg logging. This module provides JSON serialization support for complex objects including: + - Dataclass objects with type preservation - NumPy arrays with efficient binary encoding - Arbitrary objects via pickle fallback @@ -79,6 +80,7 @@ class MessageEncoder(json.JSONEncoder): JSON encoder for ezmsg messages with support for dataclasses, numpy arrays, and arbitrary objects. This encoder extends the standard JSON encoder to handle: + - Dataclass objects (serialized as dictionaries with type information) - NumPy arrays (serialized as base64-encoded data with metadata) - Other objects via pickle (as fallback) diff --git a/src/ezmsg/util/messages/axisarray.py b/src/ezmsg/util/messages/axisarray.py index 32e0939d..6d627538 100644 --- a/src/ezmsg/util/messages/axisarray.py +++ b/src/ezmsg/util/messages/axisarray.py @@ -53,8 +53,9 @@ class LinearAxis(AxisBase): An axis implementation for sparse axes with regular intervals between elements. It is called "linear" because it provides a simple linear mapping between - indices and element values: value = (index * gain) + offset - A typical example is a time axis, with regular sampling rate. + indices and element values: value = (index * gain) + offset. + + A typical example is a time axis (TimeAxis), with regular sampling rate. :param gain: Step size (scaling factor) for the linear axis :type gain: float @@ -75,9 +76,9 @@ def value(self, x): Convert index(es) to axis value(s) using the linear transformation. :param x: Index or array of indices to convert - :type x: int | npt.NDArray[np.int_] + :type x: int | npt.NDArray[:class:`numpy.int_`] :return: Corresponding axis value(s) - :rtype: float | npt.NDArray[np.float64] + :rtype: float | npt.NDArray[:class:`np.float64`] """ return (x * self.gain) + self.offset @@ -92,11 +93,11 @@ def index(self, v, fn=np.rint): Convert axis value(s) to index(es) using the inverse linear transformation. :param v: Axis value or array of values to convert to indices - :type v: float | npt.NDArray[np.float64] + :type v: float | npt.NDArray[:class:`np.float64`] :param fn: Function to apply for rounding (default: np.rint) :type fn: collections.abc.Callable :return: Corresponding index or array of indices - :rtype: int | npt.NDArray[np.int_] + :rtype: int | npt.NDArray[:class:`numpy.int_`] """ return fn((v - self.offset) / self.gain).astype(int) @@ -176,7 +177,7 @@ def value(self, x): Get coordinate value(s) at the given index(es). :param x: Index or array of indices to lookup - :type x: int | npt.NDArray[np.int_] + :type x: int | npt.NDArray[:class:`numpy.int_`] :return: Coordinate value(s) at the specified index(es) :rtype: typing.Any | npt.NDArray """ @@ -230,7 +231,12 @@ def __eq__(self, other): @dataclass class Axis(LinearAxis): - # deprecated backward compatibility + """ + Deprecated alias for LinearAxis. + + .. deprecated:: 3.6.0 + Use :class:`LinearAxis` instead. + """ def __post_init__(self) -> None: warnings.warn( "AxisArray.Axis is a deprecated alias for LinearAxis", @@ -296,7 +302,7 @@ def indices(self) -> npt.NDArray[np.int_]: Get array of all valid indices for this axis. :return: Array of indices from 0 to len(self)-1 - :rtype: npt.NDArray[np.int_] + :rtype: npt.NDArray[:class:`numpy.int_`] """ return np.arange(len(self)) diff --git a/uv.lock b/uv.lock deleted file mode 100644 index defc6f6e..00000000 --- a/uv.lock +++ /dev/null @@ -1,1602 +0,0 @@ -version = 1 -revision = 1 -requires-python = ">=3.10.15" -resolution-markers = [ - "python_full_version >= '3.11'", - "python_full_version < '3.11'", -] - -[[package]] -name = "accessible-pygments" -version = "0.0.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pygments" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/bc/c1/bbac6a50d02774f91572938964c582fff4270eee73ab822a4aeea4d8b11b/accessible_pygments-0.0.5.tar.gz", hash = "sha256:40918d3e6a2b619ad424cb91e556bd3bd8865443d9f22f1dcdf79e33c8046872", size = 1377899 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/3f/95338030883d8c8b91223b4e21744b04d11b161a3ef117295d8241f50ab4/accessible_pygments-0.0.5-py3-none-any.whl", hash = "sha256:88ae3211e68a1d0b011504b2ffc1691feafce124b845bd072ab6f9f66f34d4b7", size = 1395903 }, -] - -[[package]] -name = "alabaster" -version = "1.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929 }, -] - -[[package]] -name = "array-api-compat" -version = "1.12.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8d/bd/9fa5c7c5621698d5632cc852a79fbbdc28024462c9396698e5fdcb395f37/array_api_compat-1.12.0.tar.gz", hash = "sha256:585bc615f650de53ac24b7c012baecfcdd810f50df3573be47e6dd9fa20df974", size = 99883 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/b1/0542e0cab6f49f151a2d7a42400f84f706fc0b64e85dc1f56708b2e9fd37/array_api_compat-1.12.0-py3-none-any.whl", hash = "sha256:a0b4795b6944a9507fde54679f9350e2ad2b1e2acf4a2408a098cdc27f890a8b", size = 58156 }, -] - -[[package]] -name = "babel" -version = "2.16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2a/74/f1bc80f23eeba13393b7222b11d95ca3af2c1e28edca18af487137eefed9/babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316", size = 9348104 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/20/bc79bc575ba2e2a7f70e8a1155618bb1301eaa5132a8271373a6903f73f8/babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b", size = 9587599 }, -] - -[[package]] -name = "backports-asyncio-runner" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313 }, -] - -[[package]] -name = "beautifulsoup4" -version = "4.13.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "soupsieve" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/d8/e4/0c4c39e18fd76d6a628d4dd8da40543d136ce2d1752bd6eeeab0791f4d6b/beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195", size = 621067 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/50/cd/30110dc0ffcf3b131156077b90e9f60ed75711223f306da4db08eff8403b/beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b", size = 187285 }, -] - -[[package]] -name = "certifi" -version = "2024.8.30" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b0/ee/9b19140fe824b367c04c5e1b369942dd754c4c5462d5674002f75c4dedc1/certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9", size = 168507 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321 }, -] - -[[package]] -name = "cfgv" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249 }, -] - -[[package]] -name = "charset-normalizer" -version = "3.3.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2b/61/095a0aa1a84d1481998b534177c8566fdc50bb1233ea9a0478cd3cc075bd/charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", size = 194219 }, - { url = "https://files.pythonhosted.org/packages/cc/94/f7cf5e5134175de79ad2059edf2adce18e0685ebdb9227ff0139975d0e93/charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", size = 122521 }, - { url = "https://files.pythonhosted.org/packages/46/6a/d5c26c41c49b546860cc1acabdddf48b0b3fb2685f4f5617ac59261b44ae/charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", size = 120383 }, - { url = "https://files.pythonhosted.org/packages/b8/60/e2f67915a51be59d4539ed189eb0a2b0d292bf79270410746becb32bc2c3/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", size = 138223 }, - { url = "https://files.pythonhosted.org/packages/05/8c/eb854996d5fef5e4f33ad56927ad053d04dc820e4a3d39023f35cad72617/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", size = 148101 }, - { url = "https://files.pythonhosted.org/packages/f6/93/bb6cbeec3bf9da9b2eba458c15966658d1daa8b982c642f81c93ad9b40e1/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", size = 140699 }, - { url = "https://files.pythonhosted.org/packages/da/f1/3702ba2a7470666a62fd81c58a4c40be00670e5006a67f4d626e57f013ae/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", size = 142065 }, - { url = "https://files.pythonhosted.org/packages/3f/ba/3f5e7be00b215fa10e13d64b1f6237eb6ebea66676a41b2bcdd09fe74323/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", size = 144505 }, - { url = "https://files.pythonhosted.org/packages/33/c3/3b96a435c5109dd5b6adc8a59ba1d678b302a97938f032e3770cc84cd354/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", size = 139425 }, - { url = "https://files.pythonhosted.org/packages/43/05/3bf613e719efe68fb3a77f9c536a389f35b95d75424b96b426a47a45ef1d/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", size = 145287 }, - { url = "https://files.pythonhosted.org/packages/58/78/a0bc646900994df12e07b4ae5c713f2b3e5998f58b9d3720cce2aa45652f/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", size = 149929 }, - { url = "https://files.pythonhosted.org/packages/eb/5c/97d97248af4920bc68687d9c3b3c0f47c910e21a8ff80af4565a576bd2f0/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", size = 141605 }, - { url = "https://files.pythonhosted.org/packages/a8/31/47d018ef89f95b8aded95c589a77c072c55e94b50a41aa99c0a2008a45a4/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", size = 142646 }, - { url = "https://files.pythonhosted.org/packages/ae/d5/4fecf1d58bedb1340a50f165ba1c7ddc0400252d6832ff619c4568b36cc0/charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", size = 92846 }, - { url = "https://files.pythonhosted.org/packages/a2/a0/4af29e22cb5942488cf45630cbdd7cefd908768e69bdd90280842e4e8529/charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", size = 100343 }, - { url = "https://files.pythonhosted.org/packages/68/77/02839016f6fbbf808e8b38601df6e0e66c17bbab76dff4613f7511413597/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", size = 191647 }, - { url = "https://files.pythonhosted.org/packages/3e/33/21a875a61057165e92227466e54ee076b73af1e21fe1b31f1e292251aa1e/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", size = 121434 }, - { url = "https://files.pythonhosted.org/packages/dd/51/68b61b90b24ca35495956b718f35a9756ef7d3dd4b3c1508056fa98d1a1b/charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", size = 118979 }, - { url = "https://files.pythonhosted.org/packages/e4/a6/7ee57823d46331ddc37dd00749c95b0edec2c79b15fc0d6e6efb532e89ac/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", size = 136582 }, - { url = "https://files.pythonhosted.org/packages/74/f1/0d9fe69ac441467b737ba7f48c68241487df2f4522dd7246d9426e7c690e/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", size = 146645 }, - { url = "https://files.pythonhosted.org/packages/05/31/e1f51c76db7be1d4aef220d29fbfa5dbb4a99165d9833dcbf166753b6dc0/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", size = 139398 }, - { url = "https://files.pythonhosted.org/packages/40/26/f35951c45070edc957ba40a5b1db3cf60a9dbb1b350c2d5bef03e01e61de/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", size = 140273 }, - { url = "https://files.pythonhosted.org/packages/07/07/7e554f2bbce3295e191f7e653ff15d55309a9ca40d0362fcdab36f01063c/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", size = 142577 }, - { url = "https://files.pythonhosted.org/packages/d8/b5/eb705c313100defa57da79277d9207dc8d8e45931035862fa64b625bfead/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", size = 137747 }, - { url = "https://files.pythonhosted.org/packages/19/28/573147271fd041d351b438a5665be8223f1dd92f273713cb882ddafe214c/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", size = 143375 }, - { url = "https://files.pythonhosted.org/packages/cf/7c/f3b682fa053cc21373c9a839e6beba7705857075686a05c72e0f8c4980ca/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", size = 148474 }, - { url = "https://files.pythonhosted.org/packages/1e/49/7ab74d4ac537ece3bc3334ee08645e231f39f7d6df6347b29a74b0537103/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", size = 140232 }, - { url = "https://files.pythonhosted.org/packages/2d/dc/9dacba68c9ac0ae781d40e1a0c0058e26302ea0660e574ddf6797a0347f7/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", size = 140859 }, - { url = "https://files.pythonhosted.org/packages/6c/c2/4a583f800c0708dd22096298e49f887b49d9746d0e78bfc1d7e29816614c/charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", size = 92509 }, - { url = "https://files.pythonhosted.org/packages/57/ec/80c8d48ac8b1741d5b963797b7c0c869335619e13d4744ca2f67fc11c6fc/charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", size = 99870 }, - { url = "https://files.pythonhosted.org/packages/d1/b2/fcedc8255ec42afee97f9e6f0145c734bbe104aac28300214593eb326f1d/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", size = 192892 }, - { url = "https://files.pythonhosted.org/packages/2e/7d/2259318c202f3d17f3fe6438149b3b9e706d1070fe3fcbb28049730bb25c/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", size = 122213 }, - { url = "https://files.pythonhosted.org/packages/3a/52/9f9d17c3b54dc238de384c4cb5a2ef0e27985b42a0e5cc8e8a31d918d48d/charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", size = 119404 }, - { url = "https://files.pythonhosted.org/packages/99/b0/9c365f6d79a9f0f3c379ddb40a256a67aa69c59609608fe7feb6235896e1/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", size = 137275 }, - { url = "https://files.pythonhosted.org/packages/91/33/749df346e93d7a30cdcb90cbfdd41a06026317bfbfb62cd68307c1a3c543/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", size = 147518 }, - { url = "https://files.pythonhosted.org/packages/72/1a/641d5c9f59e6af4c7b53da463d07600a695b9824e20849cb6eea8a627761/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", size = 140182 }, - { url = "https://files.pythonhosted.org/packages/ee/fb/14d30eb4956408ee3ae09ad34299131fb383c47df355ddb428a7331cfa1e/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", size = 141869 }, - { url = "https://files.pythonhosted.org/packages/df/3e/a06b18788ca2eb6695c9b22325b6fde7dde0f1d1838b1792a0076f58fe9d/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", size = 144042 }, - { url = "https://files.pythonhosted.org/packages/45/59/3d27019d3b447a88fe7e7d004a1e04be220227760264cc41b405e863891b/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", size = 138275 }, - { url = "https://files.pythonhosted.org/packages/7b/ef/5eb105530b4da8ae37d506ccfa25057961b7b63d581def6f99165ea89c7e/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", size = 144819 }, - { url = "https://files.pythonhosted.org/packages/a2/51/e5023f937d7f307c948ed3e5c29c4b7a3e42ed2ee0b8cdf8f3a706089bf0/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", size = 149415 }, - { url = "https://files.pythonhosted.org/packages/24/9d/2e3ef673dfd5be0154b20363c5cdcc5606f35666544381bee15af3778239/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", size = 141212 }, - { url = "https://files.pythonhosted.org/packages/5b/ae/ce2c12fcac59cb3860b2e2d76dc405253a4475436b1861d95fe75bdea520/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", size = 142167 }, - { url = "https://files.pythonhosted.org/packages/ed/3a/a448bf035dce5da359daf9ae8a16b8a39623cc395a2ffb1620aa1bce62b0/charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", size = 93041 }, - { url = "https://files.pythonhosted.org/packages/b6/7c/8debebb4f90174074b827c63242c23851bdf00a532489fba57fef3416e40/charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", size = 100397 }, - { url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543 }, -] - -[[package]] -name = "colorama" -version = "0.4.6" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, -] - -[[package]] -name = "coverage" -version = "7.11.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1c/38/ee22495420457259d2f3390309505ea98f98a5eed40901cf62196abad006/coverage-7.11.0.tar.gz", hash = "sha256:167bd504ac1ca2af7ff3b81d245dfea0292c5032ebef9d66cc08a7d28c1b8050", size = 811905 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/12/95/c49df0aceb5507a80b9fe5172d3d39bf23f05be40c23c8d77d556df96cec/coverage-7.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eb53f1e8adeeb2e78962bade0c08bfdc461853c7969706ed901821e009b35e31", size = 215800 }, - { url = "https://files.pythonhosted.org/packages/dc/c6/7bb46ce01ed634fff1d7bb53a54049f539971862cc388b304ff3c51b4f66/coverage-7.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d9a03ec6cb9f40a5c360f138b88266fd8f58408d71e89f536b4f91d85721d075", size = 216198 }, - { url = "https://files.pythonhosted.org/packages/94/b2/75d9d8fbf2900268aca5de29cd0a0fe671b0f69ef88be16767cc3c828b85/coverage-7.11.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0d7f0616c557cbc3d1c2090334eddcbb70e1ae3a40b07222d62b3aa47f608fab", size = 242953 }, - { url = "https://files.pythonhosted.org/packages/65/ac/acaa984c18f440170525a8743eb4b6c960ace2dbad80dc22056a437fc3c6/coverage-7.11.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e44a86a47bbdf83b0a3ea4d7df5410d6b1a0de984fbd805fa5101f3624b9abe0", size = 244766 }, - { url = "https://files.pythonhosted.org/packages/d8/0d/938d0bff76dfa4a6b228c3fc4b3e1c0e2ad4aa6200c141fcda2bd1170227/coverage-7.11.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:596763d2f9a0ee7eec6e643e29660def2eef297e1de0d334c78c08706f1cb785", size = 246625 }, - { url = "https://files.pythonhosted.org/packages/38/54/8f5f5e84bfa268df98f46b2cb396b1009734cfb1e5d6adb663d284893b32/coverage-7.11.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ef55537ff511b5e0a43edb4c50a7bf7ba1c3eea20b4f49b1490f1e8e0e42c591", size = 243568 }, - { url = "https://files.pythonhosted.org/packages/68/30/8ba337c2877fe3f2e1af0ed7ff4be0c0c4aca44d6f4007040f3ca2255e99/coverage-7.11.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9cbabd8f4d0d3dc571d77ae5bdbfa6afe5061e679a9d74b6797c48d143307088", size = 244665 }, - { url = "https://files.pythonhosted.org/packages/cc/fb/c6f1d6d9a665536b7dde2333346f0cc41dc6a60bd1ffc10cd5c33e7eb000/coverage-7.11.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e24045453384e0ae2a587d562df2a04d852672eb63051d16096d3f08aa4c7c2f", size = 242681 }, - { url = "https://files.pythonhosted.org/packages/be/38/1b532319af5f991fa153c20373291dc65c2bf532af7dbcffdeef745c8f79/coverage-7.11.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:7161edd3426c8d19bdccde7d49e6f27f748f3c31cc350c5de7c633fea445d866", size = 242912 }, - { url = "https://files.pythonhosted.org/packages/67/3d/f39331c60ef6050d2a861dc1b514fa78f85f792820b68e8c04196ad733d6/coverage-7.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3d4ed4de17e692ba6415b0587bc7f12bc80915031fc9db46a23ce70fc88c9841", size = 243559 }, - { url = "https://files.pythonhosted.org/packages/4b/55/cb7c9df9d0495036ce582a8a2958d50c23cd73f84a23284bc23bd4711a6f/coverage-7.11.0-cp310-cp310-win32.whl", hash = "sha256:765c0bc8fe46f48e341ef737c91c715bd2a53a12792592296a095f0c237e09cf", size = 218266 }, - { url = "https://files.pythonhosted.org/packages/68/a8/b79cb275fa7bd0208767f89d57a1b5f6ba830813875738599741b97c2e04/coverage-7.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:24d6f3128f1b2d20d84b24f4074475457faedc3d4613a7e66b5e769939c7d969", size = 219169 }, - { url = "https://files.pythonhosted.org/packages/49/3a/ee1074c15c408ddddddb1db7dd904f6b81bc524e01f5a1c5920e13dbde23/coverage-7.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d58ecaa865c5b9fa56e35efc51d1014d4c0d22838815b9fce57a27dd9576847", size = 215912 }, - { url = "https://files.pythonhosted.org/packages/70/c4/9f44bebe5cb15f31608597b037d78799cc5f450044465bcd1ae8cb222fe1/coverage-7.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b679e171f1c104a5668550ada700e3c4937110dbdd153b7ef9055c4f1a1ee3cc", size = 216310 }, - { url = "https://files.pythonhosted.org/packages/42/01/5e06077cfef92d8af926bdd86b84fb28bf9bc6ad27343d68be9b501d89f2/coverage-7.11.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ca61691ba8c5b6797deb221a0d09d7470364733ea9c69425a640f1f01b7c5bf0", size = 246706 }, - { url = "https://files.pythonhosted.org/packages/40/b8/7a3f1f33b35cc4a6c37e759137533119560d06c0cc14753d1a803be0cd4a/coverage-7.11.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:aef1747ede4bd8ca9cfc04cc3011516500c6891f1b33a94add3253f6f876b7b7", size = 248634 }, - { url = "https://files.pythonhosted.org/packages/7a/41/7f987eb33de386bc4c665ab0bf98d15fcf203369d6aacae74f5dd8ec489a/coverage-7.11.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1839d08406e4cba2953dcc0ffb312252f14d7c4c96919f70167611f4dee2623", size = 250741 }, - { url = "https://files.pythonhosted.org/packages/23/c1/a4e0ca6a4e83069fb8216b49b30a7352061ca0cb38654bd2dc96b7b3b7da/coverage-7.11.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e0eb0a2dcc62478eb5b4cbb80b97bdee852d7e280b90e81f11b407d0b81c4287", size = 246837 }, - { url = "https://files.pythonhosted.org/packages/5d/03/ced062a17f7c38b4728ff76c3acb40d8465634b20b4833cdb3cc3a74e115/coverage-7.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bc1fbea96343b53f65d5351d8fd3b34fd415a2670d7c300b06d3e14a5af4f552", size = 248429 }, - { url = "https://files.pythonhosted.org/packages/97/af/a7c6f194bb8c5a2705ae019036b8fe7f49ea818d638eedb15fdb7bed227c/coverage-7.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:214b622259dd0cf435f10241f1333d32caa64dbc27f8790ab693428a141723de", size = 246490 }, - { url = "https://files.pythonhosted.org/packages/ab/c3/aab4df02b04a8fde79068c3c41ad7a622b0ef2b12e1ed154da986a727c3f/coverage-7.11.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:258d9967520cca899695d4eb7ea38be03f06951d6ca2f21fb48b1235f791e601", size = 246208 }, - { url = "https://files.pythonhosted.org/packages/30/d8/e282ec19cd658238d60ed404f99ef2e45eed52e81b866ab1518c0d4163cf/coverage-7.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:cf9e6ff4ca908ca15c157c409d608da77a56a09877b97c889b98fb2c32b6465e", size = 247126 }, - { url = "https://files.pythonhosted.org/packages/d1/17/a635fa07fac23adb1a5451ec756216768c2767efaed2e4331710342a3399/coverage-7.11.0-cp311-cp311-win32.whl", hash = "sha256:fcc15fc462707b0680cff6242c48625da7f9a16a28a41bb8fd7a4280920e676c", size = 218314 }, - { url = "https://files.pythonhosted.org/packages/2a/29/2ac1dfcdd4ab9a70026edc8d715ece9b4be9a1653075c658ee6f271f394d/coverage-7.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:865965bf955d92790f1facd64fe7ff73551bd2c1e7e6b26443934e9701ba30b9", size = 219203 }, - { url = "https://files.pythonhosted.org/packages/03/21/5ce8b3a0133179115af4c041abf2ee652395837cb896614beb8ce8ddcfd9/coverage-7.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:5693e57a065760dcbeb292d60cc4d0231a6d4b6b6f6a3191561e1d5e8820b745", size = 217879 }, - { url = "https://files.pythonhosted.org/packages/c4/db/86f6906a7c7edc1a52b2c6682d6dd9be775d73c0dfe2b84f8923dfea5784/coverage-7.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9c49e77811cf9d024b95faf86c3f059b11c0c9be0b0d61bc598f453703bd6fd1", size = 216098 }, - { url = "https://files.pythonhosted.org/packages/21/54/e7b26157048c7ba555596aad8569ff903d6cd67867d41b75287323678ede/coverage-7.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a61e37a403a778e2cda2a6a39abcc895f1d984071942a41074b5c7ee31642007", size = 216331 }, - { url = "https://files.pythonhosted.org/packages/b9/19/1ce6bf444f858b83a733171306134a0544eaddf1ca8851ede6540a55b2ad/coverage-7.11.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:c79cae102bb3b1801e2ef1511fb50e91ec83a1ce466b2c7c25010d884336de46", size = 247825 }, - { url = "https://files.pythonhosted.org/packages/71/0b/d3bcbbc259fcced5fb67c5d78f6e7ee965f49760c14afd931e9e663a83b2/coverage-7.11.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:16ce17ceb5d211f320b62df002fa7016b7442ea0fd260c11cec8ce7730954893", size = 250573 }, - { url = "https://files.pythonhosted.org/packages/58/8d/b0ff3641a320abb047258d36ed1c21d16be33beed4152628331a1baf3365/coverage-7.11.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:80027673e9d0bd6aef86134b0771845e2da85755cf686e7c7c59566cf5a89115", size = 251706 }, - { url = "https://files.pythonhosted.org/packages/59/c8/5a586fe8c7b0458053d9c687f5cff515a74b66c85931f7fe17a1c958b4ac/coverage-7.11.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4d3ffa07a08657306cd2215b0da53761c4d73cb54d9143b9303a6481ec0cd415", size = 248221 }, - { url = "https://files.pythonhosted.org/packages/d0/ff/3a25e3132804ba44cfa9a778cdf2b73dbbe63ef4b0945e39602fc896ba52/coverage-7.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a3b6a5f8b2524fd6c1066bc85bfd97e78709bb5e37b5b94911a6506b65f47186", size = 249624 }, - { url = "https://files.pythonhosted.org/packages/c5/12/ff10c8ce3895e1b17a73485ea79ebc1896a9e466a9d0f4aef63e0d17b718/coverage-7.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fcc0a4aa589de34bc56e1a80a740ee0f8c47611bdfb28cd1849de60660f3799d", size = 247744 }, - { url = "https://files.pythonhosted.org/packages/16/02/d500b91f5471b2975947e0629b8980e5e90786fe316b6d7299852c1d793d/coverage-7.11.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:dba82204769d78c3fd31b35c3d5f46e06511936c5019c39f98320e05b08f794d", size = 247325 }, - { url = "https://files.pythonhosted.org/packages/77/11/dee0284fbbd9cd64cfce806b827452c6df3f100d9e66188e82dfe771d4af/coverage-7.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:81b335f03ba67309a95210caf3eb43bd6fe75a4e22ba653ef97b4696c56c7ec2", size = 249180 }, - { url = "https://files.pythonhosted.org/packages/59/1b/cdf1def928f0a150a057cab03286774e73e29c2395f0d30ce3d9e9f8e697/coverage-7.11.0-cp312-cp312-win32.whl", hash = "sha256:037b2d064c2f8cc8716fe4d39cb705779af3fbf1ba318dc96a1af858888c7bb5", size = 218479 }, - { url = "https://files.pythonhosted.org/packages/ff/55/e5884d55e031da9c15b94b90a23beccc9d6beee65e9835cd6da0a79e4f3a/coverage-7.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:d66c0104aec3b75e5fd897e7940188ea1892ca1d0235316bf89286d6a22568c0", size = 219290 }, - { url = "https://files.pythonhosted.org/packages/23/a8/faa930cfc71c1d16bc78f9a19bb73700464f9c331d9e547bfbc1dbd3a108/coverage-7.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:d91ebeac603812a09cf6a886ba6e464f3bbb367411904ae3790dfe28311b15ad", size = 217924 }, - { url = "https://files.pythonhosted.org/packages/60/7f/85e4dfe65e400645464b25c036a26ac226cf3a69d4a50c3934c532491cdd/coverage-7.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:cc3f49e65ea6e0d5d9bd60368684fe52a704d46f9e7fc413918f18d046ec40e1", size = 216129 }, - { url = "https://files.pythonhosted.org/packages/96/5d/dc5fa98fea3c175caf9d360649cb1aa3715e391ab00dc78c4c66fabd7356/coverage-7.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f39ae2f63f37472c17b4990f794035c9890418b1b8cca75c01193f3c8d3e01be", size = 216380 }, - { url = "https://files.pythonhosted.org/packages/b2/f5/3da9cc9596708273385189289c0e4d8197d37a386bdf17619013554b3447/coverage-7.11.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7db53b5cdd2917b6eaadd0b1251cf4e7d96f4a8d24e174bdbdf2f65b5ea7994d", size = 247375 }, - { url = "https://files.pythonhosted.org/packages/65/6c/f7f59c342359a235559d2bc76b0c73cfc4bac7d61bb0df210965cb1ecffd/coverage-7.11.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10ad04ac3a122048688387828b4537bc9cf60c0bf4869c1e9989c46e45690b82", size = 249978 }, - { url = "https://files.pythonhosted.org/packages/e7/8c/042dede2e23525e863bf1ccd2b92689692a148d8b5fd37c37899ba882645/coverage-7.11.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4036cc9c7983a2b1f2556d574d2eb2154ac6ed55114761685657e38782b23f52", size = 251253 }, - { url = "https://files.pythonhosted.org/packages/7b/a9/3c58df67bfa809a7bddd786356d9c5283e45d693edb5f3f55d0986dd905a/coverage-7.11.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7ab934dd13b1c5e94b692b1e01bd87e4488cb746e3a50f798cb9464fd128374b", size = 247591 }, - { url = "https://files.pythonhosted.org/packages/26/5b/c7f32efd862ee0477a18c41e4761305de6ddd2d49cdeda0c1116227570fd/coverage-7.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59a6e5a265f7cfc05f76e3bb53eca2e0dfe90f05e07e849930fecd6abb8f40b4", size = 249411 }, - { url = "https://files.pythonhosted.org/packages/76/b5/78cb4f1e86c1611431c990423ec0768122905b03837e1b4c6a6f388a858b/coverage-7.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:df01d6c4c81e15a7c88337b795bb7595a8596e92310266b5072c7e301168efbd", size = 247303 }, - { url = "https://files.pythonhosted.org/packages/87/c9/23c753a8641a330f45f221286e707c427e46d0ffd1719b080cedc984ec40/coverage-7.11.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8c934bd088eed6174210942761e38ee81d28c46de0132ebb1801dbe36a390dcc", size = 247157 }, - { url = "https://files.pythonhosted.org/packages/c5/42/6e0cc71dc8a464486e944a4fa0d85bdec031cc2969e98ed41532a98336b9/coverage-7.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a03eaf7ec24078ad64a07f02e30060aaf22b91dedf31a6b24d0d98d2bba7f48", size = 248921 }, - { url = "https://files.pythonhosted.org/packages/e8/1c/743c2ef665e6858cccb0f84377dfe3a4c25add51e8c7ef19249be92465b6/coverage-7.11.0-cp313-cp313-win32.whl", hash = "sha256:695340f698a5f56f795b2836abe6fb576e7c53d48cd155ad2f80fd24bc63a040", size = 218526 }, - { url = "https://files.pythonhosted.org/packages/ff/d5/226daadfd1bf8ddbccefbd3aa3547d7b960fb48e1bdac124e2dd13a2b71a/coverage-7.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:2727d47fce3ee2bac648528e41455d1b0c46395a087a229deac75e9f88ba5a05", size = 219317 }, - { url = "https://files.pythonhosted.org/packages/97/54/47db81dcbe571a48a298f206183ba8a7ba79200a37cd0d9f4788fcd2af4a/coverage-7.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:0efa742f431529699712b92ecdf22de8ff198df41e43aeaaadf69973eb93f17a", size = 217948 }, - { url = "https://files.pythonhosted.org/packages/e5/8b/cb68425420154e7e2a82fd779a8cc01549b6fa83c2ad3679cd6c088ebd07/coverage-7.11.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:587c38849b853b157706407e9ebdca8fd12f45869edb56defbef2daa5fb0812b", size = 216837 }, - { url = "https://files.pythonhosted.org/packages/33/55/9d61b5765a025685e14659c8d07037247de6383c0385757544ffe4606475/coverage-7.11.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b971bdefdd75096163dd4261c74be813c4508477e39ff7b92191dea19f24cd37", size = 217061 }, - { url = "https://files.pythonhosted.org/packages/52/85/292459c9186d70dcec6538f06ea251bc968046922497377bf4a1dc9a71de/coverage-7.11.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:269bfe913b7d5be12ab13a95f3a76da23cf147be7fa043933320ba5625f0a8de", size = 258398 }, - { url = "https://files.pythonhosted.org/packages/1f/e2/46edd73fb8bf51446c41148d81944c54ed224854812b6ca549be25113ee0/coverage-7.11.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:dadbcce51a10c07b7c72b0ce4a25e4b6dcb0c0372846afb8e5b6307a121eb99f", size = 260574 }, - { url = "https://files.pythonhosted.org/packages/07/5e/1df469a19007ff82e2ca8fe509822820a31e251f80ee7344c34f6cd2ec43/coverage-7.11.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9ed43fa22c6436f7957df036331f8fe4efa7af132054e1844918866cd228af6c", size = 262797 }, - { url = "https://files.pythonhosted.org/packages/f9/50/de216b31a1434b94d9b34a964c09943c6be45069ec704bfc379d8d89a649/coverage-7.11.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9516add7256b6713ec08359b7b05aeff8850c98d357784c7205b2e60aa2513fa", size = 257361 }, - { url = "https://files.pythonhosted.org/packages/82/1e/3f9f8344a48111e152e0fd495b6fff13cc743e771a6050abf1627a7ba918/coverage-7.11.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb92e47c92fcbcdc692f428da67db33337fa213756f7adb6a011f7b5a7a20740", size = 260349 }, - { url = "https://files.pythonhosted.org/packages/65/9b/3f52741f9e7d82124272f3070bbe316006a7de1bad1093f88d59bfc6c548/coverage-7.11.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:d06f4fc7acf3cabd6d74941d53329e06bab00a8fe10e4df2714f0b134bfc64ef", size = 258114 }, - { url = "https://files.pythonhosted.org/packages/0b/8b/918f0e15f0365d50d3986bbd3338ca01178717ac5678301f3f547b6619e6/coverage-7.11.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:6fbcee1a8f056af07ecd344482f711f563a9eb1c2cad192e87df00338ec3cdb0", size = 256723 }, - { url = "https://files.pythonhosted.org/packages/44/9e/7776829f82d3cf630878a7965a7d70cc6ca94f22c7d20ec4944f7148cb46/coverage-7.11.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dbbf012be5f32533a490709ad597ad8a8ff80c582a95adc8d62af664e532f9ca", size = 259238 }, - { url = "https://files.pythonhosted.org/packages/9a/b8/49cf253e1e7a3bedb85199b201862dd7ca4859f75b6cf25ffa7298aa0760/coverage-7.11.0-cp313-cp313t-win32.whl", hash = "sha256:cee6291bb4fed184f1c2b663606a115c743df98a537c969c3c64b49989da96c2", size = 219180 }, - { url = "https://files.pythonhosted.org/packages/ac/e1/1a541703826be7ae2125a0fb7f821af5729d56bb71e946e7b933cc7a89a4/coverage-7.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a386c1061bf98e7ea4758e4313c0ab5ecf57af341ef0f43a0bf26c2477b5c268", size = 220241 }, - { url = "https://files.pythonhosted.org/packages/d5/d1/5ee0e0a08621140fd418ec4020f595b4d52d7eb429ae6a0c6542b4ba6f14/coverage-7.11.0-cp313-cp313t-win_arm64.whl", hash = "sha256:f9ea02ef40bb83823b2b04964459d281688fe173e20643870bb5d2edf68bc836", size = 218510 }, - { url = "https://files.pythonhosted.org/packages/f4/06/e923830c1985ce808e40a3fa3eb46c13350b3224b7da59757d37b6ce12b8/coverage-7.11.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c770885b28fb399aaf2a65bbd1c12bf6f307ffd112d6a76c5231a94276f0c497", size = 216110 }, - { url = "https://files.pythonhosted.org/packages/42/82/cdeed03bfead45203fb651ed756dfb5266028f5f939e7f06efac4041dad5/coverage-7.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a3d0e2087dba64c86a6b254f43e12d264b636a39e88c5cc0a01a7c71bcfdab7e", size = 216395 }, - { url = "https://files.pythonhosted.org/packages/fc/ba/e1c80caffc3199aa699813f73ff097bc2df7b31642bdbc7493600a8f1de5/coverage-7.11.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:73feb83bb41c32811973b8565f3705caf01d928d972b72042b44e97c71fd70d1", size = 247433 }, - { url = "https://files.pythonhosted.org/packages/80/c0/5b259b029694ce0a5bbc1548834c7ba3db41d3efd3474489d7efce4ceb18/coverage-7.11.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c6f31f281012235ad08f9a560976cc2fc9c95c17604ff3ab20120fe480169bca", size = 249970 }, - { url = "https://files.pythonhosted.org/packages/8c/86/171b2b5e1aac7e2fd9b43f7158b987dbeb95f06d1fbecad54ad8163ae3e8/coverage-7.11.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9570ad567f880ef675673992222746a124b9595506826b210fbe0ce3f0499cd", size = 251324 }, - { url = "https://files.pythonhosted.org/packages/1a/7e/7e10414d343385b92024af3932a27a1caf75c6e27ee88ba211221ff1a145/coverage-7.11.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8badf70446042553a773547a61fecaa734b55dc738cacf20c56ab04b77425e43", size = 247445 }, - { url = "https://files.pythonhosted.org/packages/c4/3b/e4f966b21f5be8c4bf86ad75ae94efa0de4c99c7bbb8114476323102e345/coverage-7.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a09c1211959903a479e389685b7feb8a17f59ec5a4ef9afde7650bd5eabc2777", size = 249324 }, - { url = "https://files.pythonhosted.org/packages/00/a2/8479325576dfcd909244d0df215f077f47437ab852ab778cfa2f8bf4d954/coverage-7.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:5ef83b107f50db3f9ae40f69e34b3bd9337456c5a7fe3461c7abf8b75dd666a2", size = 247261 }, - { url = "https://files.pythonhosted.org/packages/7b/d8/3a9e2db19d94d65771d0f2e21a9ea587d11b831332a73622f901157cc24b/coverage-7.11.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f91f927a3215b8907e214af77200250bb6aae36eca3f760f89780d13e495388d", size = 247092 }, - { url = "https://files.pythonhosted.org/packages/b3/b1/bbca3c472544f9e2ad2d5116b2379732957048be4b93a9c543fcd0207e5f/coverage-7.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cdbcd376716d6b7fbfeedd687a6c4be019c5a5671b35f804ba76a4c0a778cba4", size = 248755 }, - { url = "https://files.pythonhosted.org/packages/89/49/638d5a45a6a0f00af53d6b637c87007eb2297042186334e9923a61aa8854/coverage-7.11.0-cp314-cp314-win32.whl", hash = "sha256:bab7ec4bb501743edc63609320aaec8cd9188b396354f482f4de4d40a9d10721", size = 218793 }, - { url = "https://files.pythonhosted.org/packages/30/cc/b675a51f2d068adb3cdf3799212c662239b0ca27f4691d1fff81b92ea850/coverage-7.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:3d4ba9a449e9364a936a27322b20d32d8b166553bfe63059bd21527e681e2fad", size = 219587 }, - { url = "https://files.pythonhosted.org/packages/93/98/5ac886876026de04f00820e5094fe22166b98dcb8b426bf6827aaf67048c/coverage-7.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:ce37f215223af94ef0f75ac68ea096f9f8e8c8ec7d6e8c346ee45c0d363f0479", size = 218168 }, - { url = "https://files.pythonhosted.org/packages/14/d1/b4145d35b3e3ecf4d917e97fc8895bcf027d854879ba401d9ff0f533f997/coverage-7.11.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:f413ce6e07e0d0dc9c433228727b619871532674b45165abafe201f200cc215f", size = 216850 }, - { url = "https://files.pythonhosted.org/packages/ca/d1/7f645fc2eccd318369a8a9948acc447bb7c1ade2911e31d3c5620544c22b/coverage-7.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:05791e528a18f7072bf5998ba772fe29db4da1234c45c2087866b5ba4dea710e", size = 217071 }, - { url = "https://files.pythonhosted.org/packages/54/7d/64d124649db2737ceced1dfcbdcb79898d5868d311730f622f8ecae84250/coverage-7.11.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cacb29f420cfeb9283b803263c3b9a068924474ff19ca126ba9103e1278dfa44", size = 258570 }, - { url = "https://files.pythonhosted.org/packages/6c/3f/6f5922f80dc6f2d8b2c6f974835c43f53eb4257a7797727e6ca5b7b2ec1f/coverage-7.11.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:314c24e700d7027ae3ab0d95fbf8d53544fca1f20345fd30cd219b737c6e58d3", size = 260738 }, - { url = "https://files.pythonhosted.org/packages/0e/5f/9e883523c4647c860b3812b417a2017e361eca5b635ee658387dc11b13c1/coverage-7.11.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:630d0bd7a293ad2fc8b4b94e5758c8b2536fdf36c05f1681270203e463cbfa9b", size = 262994 }, - { url = "https://files.pythonhosted.org/packages/07/bb/43b5a8e94c09c8bf51743ffc65c4c841a4ca5d3ed191d0a6919c379a1b83/coverage-7.11.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e89641f5175d65e2dbb44db15fe4ea48fade5d5bbb9868fdc2b4fce22f4a469d", size = 257282 }, - { url = "https://files.pythonhosted.org/packages/aa/e5/0ead8af411411330b928733e1d201384b39251a5f043c1612970310e8283/coverage-7.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c9f08ea03114a637dab06cedb2e914da9dc67fa52c6015c018ff43fdde25b9c2", size = 260430 }, - { url = "https://files.pythonhosted.org/packages/ae/66/03dd8bb0ba5b971620dcaac145461950f6d8204953e535d2b20c6b65d729/coverage-7.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce9f3bde4e9b031eaf1eb61df95c1401427029ea1bfddb8621c1161dcb0fa02e", size = 258190 }, - { url = "https://files.pythonhosted.org/packages/45/ae/28a9cce40bf3174426cb2f7e71ee172d98e7f6446dff936a7ccecee34b14/coverage-7.11.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:e4dc07e95495923d6fd4d6c27bf70769425b71c89053083843fd78f378558996", size = 256658 }, - { url = "https://files.pythonhosted.org/packages/5c/7c/3a44234a8599513684bfc8684878fd7b126c2760f79712bb78c56f19efc4/coverage-7.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:424538266794db2861db4922b05d729ade0940ee69dcf0591ce8f69784db0e11", size = 259342 }, - { url = "https://files.pythonhosted.org/packages/e1/e6/0108519cba871af0351725ebdb8660fd7a0fe2ba3850d56d32490c7d9b4b/coverage-7.11.0-cp314-cp314t-win32.whl", hash = "sha256:4c1eeb3fb8eb9e0190bebafd0462936f75717687117339f708f395fe455acc73", size = 219568 }, - { url = "https://files.pythonhosted.org/packages/c9/76/44ba876e0942b4e62fdde23ccb029ddb16d19ba1bef081edd00857ba0b16/coverage-7.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b56efee146c98dbf2cf5cffc61b9829d1e94442df4d7398b26892a53992d3547", size = 220687 }, - { url = "https://files.pythonhosted.org/packages/b9/0c/0df55ecb20d0d0ed5c322e10a441775e1a3a5d78c60f0c4e1abfe6fcf949/coverage-7.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:b5c2705afa83f49bd91962a4094b6b082f94aef7626365ab3f8f4bd159c5acf3", size = 218711 }, - { url = "https://files.pythonhosted.org/packages/5f/04/642c1d8a448ae5ea1369eac8495740a79eb4e581a9fb0cbdce56bbf56da1/coverage-7.11.0-py3-none-any.whl", hash = "sha256:4b7589765348d78fb4e5fb6ea35d07564e387da2fc5efff62e0222971f155f68", size = 207761 }, -] - -[package.optional-dependencies] -toml = [ - { name = "tomli", marker = "python_full_version <= '3.11'" }, -] - -[[package]] -name = "distlib" -version = "0.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047 }, -] - -[[package]] -name = "docutils" -version = "0.20.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/1f/53/a5da4f2c5739cf66290fac1431ee52aff6851c7c8ffd8264f13affd7bcdd/docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b", size = 2058365 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/26/87/f238c0670b94533ac0353a4e2a1a771a0cc73277b88bff23d3ae35a256c1/docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6", size = 572666 }, -] - -[[package]] -name = "exceptiongroup" -version = "1.2.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 }, -] - -[[package]] -name = "ezmsg" -version = "3.6.2" -source = { editable = "." } -dependencies = [ - { name = "array-api-compat" }, - { name = "typing-extensions" }, -] - -[package.optional-dependencies] -axisarray = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] - -[package.dev-dependencies] -axisarray = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -dev = [ - { name = "flake8" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "pre-commit" }, - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "pytest-cov" }, - { name = "ruff" }, - { name = "xarray", marker = "python_full_version < '3.13'" }, -] -docs = [ - { name = "ezmsg-sigproc" }, - { name = "myst-parser" }, - { name = "pydata-sphinx-theme" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "sphinx-autodoc-typehints", version = "3.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx-autodoc-typehints", version = "3.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "sphinx-copybutton" }, - { name = "sphinx-design" }, - { name = "sphinxext-rediraffe" }, -] -lint = [ - { name = "flake8" }, - { name = "ruff" }, -] -test = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "pytest" }, - { name = "pytest-asyncio" }, - { name = "pytest-cov" }, - { name = "xarray", marker = "python_full_version < '3.13'" }, -] - -[package.metadata] -requires-dist = [ - { name = "array-api-compat", specifier = ">=1.12.0" }, - { name = "numpy", marker = "extra == 'axisarray'", specifier = ">=2.2.6" }, - { name = "typing-extensions", specifier = ">=4.14.1" }, -] -provides-extras = ["axisarray"] - -[package.metadata.requires-dev] -axisarray = [{ name = "numpy", specifier = ">=1.24.4" }] -dev = [ - { name = "flake8", specifier = ">=7.3.0" }, - { name = "numpy", specifier = ">=2.2.6" }, - { name = "pre-commit", specifier = ">=4.3.0" }, - { name = "pytest", specifier = ">=8.4.1" }, - { name = "pytest-asyncio", specifier = ">=1.1.0" }, - { name = "pytest-cov", specifier = ">=6.2.1" }, - { name = "ruff", specifier = ">=0.12.9" }, - { name = "xarray", marker = "python_full_version < '3.13'", specifier = ">=2023.1.0" }, -] -docs = [ - { name = "ezmsg-sigproc", specifier = ">=2.2.0" }, - { name = "myst-parser" }, - { name = "pydata-sphinx-theme" }, - { name = "sphinx", specifier = ">=8.1.3" }, - { name = "sphinx-autodoc-typehints", specifier = ">=3.0.0" }, - { name = "sphinx-copybutton" }, - { name = "sphinx-design" }, - { name = "sphinxext-rediraffe" }, -] -lint = [ - { name = "flake8", specifier = ">=7.3.0" }, - { name = "ruff", specifier = ">=0.12.9" }, -] -test = [ - { name = "numpy", specifier = ">=2.2.6" }, - { name = "pytest", specifier = ">=8.4.1" }, - { name = "pytest-asyncio", specifier = ">=1.1.0" }, - { name = "pytest-cov", specifier = ">=6.2.1" }, - { name = "xarray", marker = "python_full_version < '3.13'", specifier = ">=2023.1.0" }, -] - -[[package]] -name = "ezmsg-sigproc" -version = "2.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "array-api-compat" }, - { name = "ezmsg" }, - { name = "numba" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "pywavelets", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "pywavelets", version = "1.9.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "scipy", version = "1.16.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "sparse" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/18/d3/7be5cc938267ac704072a47c0be1d6909c1c1888ccecd7c9cddaee80ceae/ezmsg_sigproc-2.2.0.tar.gz", hash = "sha256:d6549b097638dcaba0bb932904867a314d9a53b18107069dd6cc7674db6d2078", size = 128743 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/38/df/499b111f420aaf19c2cf427e4a2ccea464a38397e7a6b475af82af4de00d/ezmsg_sigproc-2.2.0-py3-none-any.whl", hash = "sha256:2055064b5c6536ffdf3f8def15d7051768a7cba519960822df03e5b85638a20b", size = 87975 }, -] - -[[package]] -name = "filelock" -version = "3.20.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/58/46/0028a82567109b5ef6e4d2a1f04a583fb513e6cf9527fcdd09afd817deeb/filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4", size = 18922 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054 }, -] - -[[package]] -name = "flake8" -version = "7.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mccabe" }, - { name = "pycodestyle" }, - { name = "pyflakes" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9b/af/fbfe3c4b5a657d79e5c47a2827a362f9e1b763336a52f926126aa6dc7123/flake8-7.3.0.tar.gz", hash = "sha256:fe044858146b9fc69b551a4b490d69cf960fcb78ad1edcb84e7fbb1b4a8e3872", size = 48326 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/56/13ab06b4f93ca7cac71078fbe37fcea175d3216f31f85c3168a6bbd0bb9a/flake8-7.3.0-py2.py3-none-any.whl", hash = "sha256:b9696257b9ce8beb888cdbe31cf885c90d31928fe202be0889a7cdafad32f01e", size = 57922 }, -] - -[[package]] -name = "identify" -version = "2.6.15" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ff/e7/685de97986c916a6d93b3876139e00eef26ad5bbbd61925d670ae8013449/identify-2.6.15.tar.gz", hash = "sha256:e4f4864b96c6557ef2a1e1c951771838f4edc9df3a72ec7118b338801b11c7bf", size = 99311 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-py2.py3-none-any.whl", hash = "sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757", size = 99183 }, -] - -[[package]] -name = "idna" -version = "3.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, -] - -[[package]] -name = "imagesize" -version = "1.4.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a", size = 1280026 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769 }, -] - -[[package]] -name = "iniconfig" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, -] - -[[package]] -name = "jinja2" -version = "3.1.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markupsafe" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271 }, -] - -[[package]] -name = "llvmlite" -version = "0.45.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/99/8d/5baf1cef7f9c084fb35a8afbde88074f0d6a727bc63ef764fe0e7543ba40/llvmlite-0.45.1.tar.gz", hash = "sha256:09430bb9d0bb58fc45a45a57c7eae912850bedc095cd0810a57de109c69e1c32", size = 185600 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/6d/585c84ddd9d2a539a3c3487792b3cf3f988e28ec4fa281bf8b0e055e1166/llvmlite-0.45.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:1b1af0c910af0978aa55fa4f60bbb3e9f39b41e97c2a6d94d199897be62ba07a", size = 43043523 }, - { url = "https://files.pythonhosted.org/packages/ae/34/992bd12d3ff245e0801bcf6013961daa8c19c9b9c2e61cb4b8bce94566f9/llvmlite-0.45.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02a164db2d79088bbd6e0d9633b4fe4021d6379d7e4ac7cc85ed5f44b06a30c5", size = 37253122 }, - { url = "https://files.pythonhosted.org/packages/a6/7b/6d7585998a5991fa74dc925aae57913ba8c7c2efff909de9d34cc1cd3c27/llvmlite-0.45.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f2d47f34e4029e6df3395de34cc1c66440a8d72712993a6e6168db228686711b", size = 56288210 }, - { url = "https://files.pythonhosted.org/packages/b5/e2/a4abea058633bfc82eb08fd69ce242c118fdb9b0abad1fdcbe0bc6aedab5/llvmlite-0.45.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f7319e5f9f90720578a7f56fbc805bdfb4bc071b507c7611f170d631c3c0f1e0", size = 55140958 }, - { url = "https://files.pythonhosted.org/packages/74/c0/233468e96ed287b953239c3b24b1d69df47c6ba9262bfdca98eda7e83a04/llvmlite-0.45.1-cp310-cp310-win_amd64.whl", hash = "sha256:4edb62e685867799e336723cb9787ec6598d51d0b1ed9af0f38e692aa757e898", size = 38132232 }, - { url = "https://files.pythonhosted.org/packages/04/ad/9bdc87b2eb34642c1cfe6bcb4f5db64c21f91f26b010f263e7467e7536a3/llvmlite-0.45.1-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:60f92868d5d3af30b4239b50e1717cb4e4e54f6ac1c361a27903b318d0f07f42", size = 43043526 }, - { url = "https://files.pythonhosted.org/packages/a5/ea/c25c6382f452a943b4082da5e8c1665ce29a62884e2ec80608533e8e82d5/llvmlite-0.45.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:98baab513e19beb210f1ef39066288784839a44cd504e24fff5d17f1b3cf0860", size = 37253118 }, - { url = "https://files.pythonhosted.org/packages/fe/af/85fc237de98b181dbbe8647324331238d6c52a3554327ccdc83ced28efba/llvmlite-0.45.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3adc2355694d6a6fbcc024d59bb756677e7de506037c878022d7b877e7613a36", size = 56288209 }, - { url = "https://files.pythonhosted.org/packages/0a/df/3daf95302ff49beff4230065e3178cd40e71294968e8d55baf4a9e560814/llvmlite-0.45.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2f3377a6db40f563058c9515dedcc8a3e562d8693a106a28f2ddccf2c8fcf6ca", size = 55140958 }, - { url = "https://files.pythonhosted.org/packages/a4/56/4c0d503fe03bac820ecdeb14590cf9a248e120f483bcd5c009f2534f23f0/llvmlite-0.45.1-cp311-cp311-win_amd64.whl", hash = "sha256:f9c272682d91e0d57f2a76c6d9ebdfccc603a01828cdbe3d15273bdca0c3363a", size = 38132232 }, - { url = "https://files.pythonhosted.org/packages/e2/7c/82cbd5c656e8991bcc110c69d05913be2229302a92acb96109e166ae31fb/llvmlite-0.45.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:28e763aba92fe9c72296911e040231d486447c01d4f90027c8e893d89d49b20e", size = 43043524 }, - { url = "https://files.pythonhosted.org/packages/9d/bc/5314005bb2c7ee9f33102c6456c18cc81745d7055155d1218f1624463774/llvmlite-0.45.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1a53f4b74ee9fd30cb3d27d904dadece67a7575198bd80e687ee76474620735f", size = 37253123 }, - { url = "https://files.pythonhosted.org/packages/96/76/0f7154952f037cb320b83e1c952ec4a19d5d689cf7d27cb8a26887d7bbc1/llvmlite-0.45.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b3796b1b1e1c14dcae34285d2f4ea488402fbd2c400ccf7137603ca3800864f", size = 56288211 }, - { url = "https://files.pythonhosted.org/packages/00/b1/0b581942be2683ceb6862d558979e87387e14ad65a1e4db0e7dd671fa315/llvmlite-0.45.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:779e2f2ceefef0f4368548685f0b4adde34e5f4b457e90391f570a10b348d433", size = 55140958 }, - { url = "https://files.pythonhosted.org/packages/33/94/9ba4ebcf4d541a325fd8098ddc073b663af75cc8b065b6059848f7d4dce7/llvmlite-0.45.1-cp312-cp312-win_amd64.whl", hash = "sha256:9e6c9949baf25d9aa9cd7cf0f6d011b9ca660dd17f5ba2b23bdbdb77cc86b116", size = 38132231 }, - { url = "https://files.pythonhosted.org/packages/1d/e2/c185bb7e88514d5025f93c6c4092f6120c6cea8fe938974ec9860fb03bbb/llvmlite-0.45.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:d9ea9e6f17569a4253515cc01dade70aba536476e3d750b2e18d81d7e670eb15", size = 43043524 }, - { url = "https://files.pythonhosted.org/packages/09/b8/b5437b9ecb2064e89ccf67dccae0d02cd38911705112dd0dcbfa9cd9a9de/llvmlite-0.45.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:c9f3cadee1630ce4ac18ea38adebf2a4f57a89bd2740ce83746876797f6e0bfb", size = 37253121 }, - { url = "https://files.pythonhosted.org/packages/f7/97/ad1a907c0173a90dd4df7228f24a3ec61058bc1a9ff8a0caec20a0cc622e/llvmlite-0.45.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:57c48bf2e1083eedbc9406fb83c4e6483017879714916fe8be8a72a9672c995a", size = 56288210 }, - { url = "https://files.pythonhosted.org/packages/32/d8/c99c8ac7a326e9735401ead3116f7685a7ec652691aeb2615aa732b1fc4a/llvmlite-0.45.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3aa3dfceda4219ae39cf18806c60eeb518c1680ff834b8b311bd784160b9ce40", size = 55140957 }, - { url = "https://files.pythonhosted.org/packages/09/56/ed35668130e32dbfad2eb37356793b0a95f23494ab5be7d9bf5cb75850ee/llvmlite-0.45.1-cp313-cp313-win_amd64.whl", hash = "sha256:080e6f8d0778a8239cd47686d402cb66eb165e421efa9391366a9b7e5810a38b", size = 38132232 }, -] - -[[package]] -name = "markdown-it-py" -version = "3.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mdurl" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, -] - -[[package]] -name = "markupsafe" -version = "2.1.5" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/87/5b/aae44c6655f3801e81aa3eef09dbbf012431987ba564d7231722f68df02d/MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b", size = 19384 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/54/ad5eb37bf9d51800010a74e4665425831a9db4e7c4e0fde4352e391e808e/MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc", size = 18206 }, - { url = "https://files.pythonhosted.org/packages/6a/4a/a4d49415e600bacae038c67f9fecc1d5433b9d3c71a4de6f33537b89654c/MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5", size = 14079 }, - { url = "https://files.pythonhosted.org/packages/0a/7b/85681ae3c33c385b10ac0f8dd025c30af83c78cec1c37a6aa3b55e67f5ec/MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46", size = 26620 }, - { url = "https://files.pythonhosted.org/packages/7c/52/2b1b570f6b8b803cef5ac28fdf78c0da318916c7d2fe9402a84d591b394c/MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f", size = 25818 }, - { url = "https://files.pythonhosted.org/packages/29/fe/a36ba8c7ca55621620b2d7c585313efd10729e63ef81e4e61f52330da781/MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900", size = 25493 }, - { url = "https://files.pythonhosted.org/packages/60/ae/9c60231cdfda003434e8bd27282b1f4e197ad5a710c14bee8bea8a9ca4f0/MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff", size = 30630 }, - { url = "https://files.pythonhosted.org/packages/65/dc/1510be4d179869f5dafe071aecb3f1f41b45d37c02329dfba01ff59e5ac5/MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad", size = 29745 }, - { url = "https://files.pythonhosted.org/packages/30/39/8d845dd7d0b0613d86e0ef89549bfb5f61ed781f59af45fc96496e897f3a/MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd", size = 30021 }, - { url = "https://files.pythonhosted.org/packages/c7/5c/356a6f62e4f3c5fbf2602b4771376af22a3b16efa74eb8716fb4e328e01e/MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4", size = 16659 }, - { url = "https://files.pythonhosted.org/packages/69/48/acbf292615c65f0604a0c6fc402ce6d8c991276e16c80c46a8f758fbd30c/MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5", size = 17213 }, - { url = "https://files.pythonhosted.org/packages/11/e7/291e55127bb2ae67c64d66cef01432b5933859dfb7d6949daa721b89d0b3/MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f", size = 18219 }, - { url = "https://files.pythonhosted.org/packages/6b/cb/aed7a284c00dfa7c0682d14df85ad4955a350a21d2e3b06d8240497359bf/MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2", size = 14098 }, - { url = "https://files.pythonhosted.org/packages/1c/cf/35fe557e53709e93feb65575c93927942087e9b97213eabc3fe9d5b25a55/MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced", size = 29014 }, - { url = "https://files.pythonhosted.org/packages/97/18/c30da5e7a0e7f4603abfc6780574131221d9148f323752c2755d48abad30/MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5", size = 28220 }, - { url = "https://files.pythonhosted.org/packages/0c/40/2e73e7d532d030b1e41180807a80d564eda53babaf04d65e15c1cf897e40/MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c", size = 27756 }, - { url = "https://files.pythonhosted.org/packages/18/46/5dca760547e8c59c5311b332f70605d24c99d1303dd9a6e1fc3ed0d73561/MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f", size = 33988 }, - { url = "https://files.pythonhosted.org/packages/6d/c5/27febe918ac36397919cd4a67d5579cbbfa8da027fa1238af6285bb368ea/MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a", size = 32718 }, - { url = "https://files.pythonhosted.org/packages/f8/81/56e567126a2c2bc2684d6391332e357589a96a76cb9f8e5052d85cb0ead8/MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f", size = 33317 }, - { url = "https://files.pythonhosted.org/packages/00/0b/23f4b2470accb53285c613a3ab9ec19dc944eaf53592cb6d9e2af8aa24cc/MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906", size = 16670 }, - { url = "https://files.pythonhosted.org/packages/b7/a2/c78a06a9ec6d04b3445a949615c4c7ed86a0b2eb68e44e7541b9d57067cc/MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617", size = 17224 }, - { url = "https://files.pythonhosted.org/packages/53/bd/583bf3e4c8d6a321938c13f49d44024dbe5ed63e0a7ba127e454a66da974/MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1", size = 18215 }, - { url = "https://files.pythonhosted.org/packages/48/d6/e7cd795fc710292c3af3a06d80868ce4b02bfbbf370b7cee11d282815a2a/MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4", size = 14069 }, - { url = "https://files.pythonhosted.org/packages/51/b5/5d8ec796e2a08fc814a2c7d2584b55f889a55cf17dd1a90f2beb70744e5c/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee", size = 29452 }, - { url = "https://files.pythonhosted.org/packages/0a/0d/2454f072fae3b5a137c119abf15465d1771319dfe9e4acbb31722a0fff91/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5", size = 28462 }, - { url = "https://files.pythonhosted.org/packages/2d/75/fd6cb2e68780f72d47e6671840ca517bda5ef663d30ada7616b0462ad1e3/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b", size = 27869 }, - { url = "https://files.pythonhosted.org/packages/b0/81/147c477391c2750e8fc7705829f7351cf1cd3be64406edcf900dc633feb2/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a", size = 33906 }, - { url = "https://files.pythonhosted.org/packages/8b/ff/9a52b71839d7a256b563e85d11050e307121000dcebc97df120176b3ad93/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f", size = 32296 }, - { url = "https://files.pythonhosted.org/packages/88/07/2dc76aa51b481eb96a4c3198894f38b480490e834479611a4053fbf08623/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169", size = 33038 }, - { url = "https://files.pythonhosted.org/packages/96/0c/620c1fb3661858c0e37eb3cbffd8c6f732a67cd97296f725789679801b31/MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad", size = 16572 }, - { url = "https://files.pythonhosted.org/packages/3f/14/c3554d512d5f9100a95e737502f4a2323a1959f6d0d01e0d0997b35f7b10/MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb", size = 17127 }, -] - -[[package]] -name = "mccabe" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350 }, -] - -[[package]] -name = "mdit-py-plugins" -version = "0.4.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "markdown-it-py" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/19/03/a2ecab526543b152300717cf232bb4bb8605b6edb946c845016fa9c9c9fd/mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5", size = 43542 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/f7/7782a043553ee469c1ff49cfa1cdace2d6bf99a1f333cf38676b3ddf30da/mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636", size = 55316 }, -] - -[[package]] -name = "mdurl" -version = "0.1.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, -] - -[[package]] -name = "myst-parser" -version = "4.0.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "docutils" }, - { name = "jinja2" }, - { name = "markdown-it-py" }, - { name = "mdit-py-plugins" }, - { name = "pyyaml" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/a5/9626ba4f73555b3735ad86247a8077d4603aa8628537687c839ab08bfe44/myst_parser-4.0.1.tar.gz", hash = "sha256:5cfea715e4f3574138aecbf7d54132296bfd72bb614d31168f48c477a830a7c4", size = 93985 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/df/76d0321c3797b54b60fef9ec3bd6f4cfd124b9e422182156a1dd418722cf/myst_parser-4.0.1-py3-none-any.whl", hash = "sha256:9134e88959ec3b5780aedf8a99680ea242869d012e8821db3126d427edc9c95d", size = 84579 }, -] - -[[package]] -name = "nodeenv" -version = "1.9.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 }, -] - -[[package]] -name = "numba" -version = "0.62.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "llvmlite" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a3/20/33dbdbfe60e5fd8e3dbfde299d106279a33d9f8308346022316781368591/numba-0.62.1.tar.gz", hash = "sha256:7b774242aa890e34c21200a1fc62e5b5757d5286267e71103257f4e2af0d5161", size = 2749817 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f5/27/a5a9a58f267ec3b72f609789b2a8eefd6156bd7117e41cc9b7cf5de30490/numba-0.62.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:a323df9d36a0da1ca9c592a6baaddd0176d9f417ef49a65bb81951dce69d941a", size = 2684281 }, - { url = "https://files.pythonhosted.org/packages/3a/9d/ffc091c0bfd7b80f66df3887a7061b6af80c8c2649902444026ee1454391/numba-0.62.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1e1f4781d3f9f7c23f16eb04e76ca10b5a3516e959634bd226fc48d5d8e7a0a", size = 2687311 }, - { url = "https://files.pythonhosted.org/packages/a1/13/9a27bcd0baeea236116070c7df458414336f25e9dd5a872b066cf36b74bf/numba-0.62.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:14432af305ea68627a084cd702124fd5d0c1f5b8a413b05f4e14757202d1cf6c", size = 3734548 }, - { url = "https://files.pythonhosted.org/packages/a7/00/17a1ac4a60253c784ce59549375e047da98330b82de7df6ac7f4ecc90902/numba-0.62.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f180922adf159ae36c2fe79fb94ffaa74cf5cb3688cb72dba0a904b91e978507", size = 3441277 }, - { url = "https://files.pythonhosted.org/packages/86/94/20ae0ff78612c4697eaf942a639db01dd4e2d90f634ac41fa3e015c961fc/numba-0.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:f41834909d411b4b8d1c68f745144136f21416547009c1e860cc2098754b4ca7", size = 2745647 }, - { url = "https://files.pythonhosted.org/packages/dd/5f/8b3491dd849474f55e33c16ef55678ace1455c490555337899c35826836c/numba-0.62.1-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:f43e24b057714e480fe44bc6031de499e7cf8150c63eb461192caa6cc8530bc8", size = 2684279 }, - { url = "https://files.pythonhosted.org/packages/bf/18/71969149bfeb65a629e652b752b80167fe8a6a6f6e084f1f2060801f7f31/numba-0.62.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:57cbddc53b9ee02830b828a8428757f5c218831ccc96490a314ef569d8342b7b", size = 2687330 }, - { url = "https://files.pythonhosted.org/packages/0e/7d/403be3fecae33088027bc8a95dc80a2fda1e3beff3e0e5fc4374ada3afbe/numba-0.62.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:604059730c637c7885386521bb1b0ddcbc91fd56131a6dcc54163d6f1804c872", size = 3739727 }, - { url = "https://files.pythonhosted.org/packages/e0/c3/3d910d08b659a6d4c62ab3cd8cd93c4d8b7709f55afa0d79a87413027ff6/numba-0.62.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6c540880170bee817011757dc9049dba5a29db0c09b4d2349295991fe3ee55f", size = 3445490 }, - { url = "https://files.pythonhosted.org/packages/5b/82/9d425c2f20d9f0a37f7cb955945a553a00fa06a2b025856c3550227c5543/numba-0.62.1-cp311-cp311-win_amd64.whl", hash = "sha256:03de6d691d6b6e2b76660ba0f38f37b81ece8b2cc524a62f2a0cfae2bfb6f9da", size = 2745550 }, - { url = "https://files.pythonhosted.org/packages/5e/fa/30fa6873e9f821c0ae755915a3ca444e6ff8d6a7b6860b669a3d33377ac7/numba-0.62.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:1b743b32f8fa5fff22e19c2e906db2f0a340782caf024477b97801b918cf0494", size = 2685346 }, - { url = "https://files.pythonhosted.org/packages/a9/d5/504ce8dc46e0dba2790c77e6b878ee65b60fe3e7d6d0006483ef6fde5a97/numba-0.62.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:90fa21b0142bcf08ad8e32a97d25d0b84b1e921bc9423f8dda07d3652860eef6", size = 2688139 }, - { url = "https://files.pythonhosted.org/packages/50/5f/6a802741176c93f2ebe97ad90751894c7b0c922b52ba99a4395e79492205/numba-0.62.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6ef84d0ac19f1bf80431347b6f4ce3c39b7ec13f48f233a48c01e2ec06ecbc59", size = 3796453 }, - { url = "https://files.pythonhosted.org/packages/7e/df/efd21527d25150c4544eccc9d0b7260a5dec4b7e98b5a581990e05a133c0/numba-0.62.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9315cc5e441300e0ca07c828a627d92a6802bcbf27c5487f31ae73783c58da53", size = 3496451 }, - { url = "https://files.pythonhosted.org/packages/80/44/79bfdab12a02796bf4f1841630355c82b5a69933b1d50eb15c7fa37dabe8/numba-0.62.1-cp312-cp312-win_amd64.whl", hash = "sha256:44e3aa6228039992f058f5ebfcfd372c83798e9464297bdad8cc79febcf7891e", size = 2745552 }, - { url = "https://files.pythonhosted.org/packages/22/76/501ea2c07c089ef1386868f33dff2978f43f51b854e34397b20fc55e0a58/numba-0.62.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:b72489ba8411cc9fdcaa2458d8f7677751e94f0109eeb53e5becfdc818c64afb", size = 2685766 }, - { url = "https://files.pythonhosted.org/packages/80/68/444986ed95350c0611d5c7b46828411c222ce41a0c76707c36425d27ce29/numba-0.62.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:44a1412095534a26fb5da2717bc755b57da5f3053965128fe3dc286652cc6a92", size = 2688741 }, - { url = "https://files.pythonhosted.org/packages/78/7e/bf2e3634993d57f95305c7cee4c9c6cb3c9c78404ee7b49569a0dfecfe33/numba-0.62.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8c9460b9e936c5bd2f0570e20a0a5909ee6e8b694fd958b210e3bde3a6dba2d7", size = 3804576 }, - { url = "https://files.pythonhosted.org/packages/e8/b6/8a1723fff71f63bbb1354bdc60a1513a068acc0f5322f58da6f022d20247/numba-0.62.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:728f91a874192df22d74e3fd42c12900b7ce7190b1aad3574c6c61b08313e4c5", size = 3503367 }, - { url = "https://files.pythonhosted.org/packages/9c/ec/9d414e7a80d6d1dc4af0e07c6bfe293ce0b04ea4d0ed6c45dad9bd6e72eb/numba-0.62.1-cp313-cp313-win_amd64.whl", hash = "sha256:bbf3f88b461514287df66bc8d0307e949b09f2b6f67da92265094e8fa1282dd8", size = 2745529 }, -] - -[[package]] -name = "numpy" -version = "2.2.6" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245 }, - { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048 }, - { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542 }, - { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301 }, - { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320 }, - { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050 }, - { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034 }, - { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185 }, - { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149 }, - { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620 }, - { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963 }, - { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743 }, - { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616 }, - { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579 }, - { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005 }, - { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570 }, - { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548 }, - { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521 }, - { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866 }, - { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455 }, - { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348 }, - { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362 }, - { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103 }, - { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382 }, - { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462 }, - { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618 }, - { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511 }, - { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783 }, - { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506 }, - { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190 }, - { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828 }, - { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006 }, - { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765 }, - { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736 }, - { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719 }, - { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072 }, - { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213 }, - { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632 }, - { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532 }, - { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885 }, - { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467 }, - { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144 }, - { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217 }, - { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014 }, - { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935 }, - { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122 }, - { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143 }, - { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260 }, - { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225 }, - { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374 }, - { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391 }, - { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754 }, - { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476 }, - { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666 }, -] - -[[package]] -name = "numpy" -version = "2.3.4" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.11'", -] -sdist = { url = "https://files.pythonhosted.org/packages/b5/f4/098d2270d52b41f1bd7db9fc288aaa0400cb48c2a3e2af6fa365d9720947/numpy-2.3.4.tar.gz", hash = "sha256:a7d018bfedb375a8d979ac758b120ba846a7fe764911a64465fd87b8729f4a6a", size = 20582187 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/60/e7/0e07379944aa8afb49a556a2b54587b828eb41dc9adc56fb7615b678ca53/numpy-2.3.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e78aecd2800b32e8347ce49316d3eaf04aed849cd5b38e0af39f829a4e59f5eb", size = 21259519 }, - { url = "https://files.pythonhosted.org/packages/d0/cb/5a69293561e8819b09e34ed9e873b9a82b5f2ade23dce4c51dc507f6cfe1/numpy-2.3.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7fd09cc5d65bda1e79432859c40978010622112e9194e581e3415a3eccc7f43f", size = 14452796 }, - { url = "https://files.pythonhosted.org/packages/e4/04/ff11611200acd602a1e5129e36cfd25bf01ad8e5cf927baf2e90236eb02e/numpy-2.3.4-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:1b219560ae2c1de48ead517d085bc2d05b9433f8e49d0955c82e8cd37bd7bf36", size = 5381639 }, - { url = "https://files.pythonhosted.org/packages/ea/77/e95c757a6fe7a48d28a009267408e8aa382630cc1ad1db7451b3bc21dbb4/numpy-2.3.4-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:bafa7d87d4c99752d07815ed7a2c0964f8ab311eb8168f41b910bd01d15b6032", size = 6914296 }, - { url = "https://files.pythonhosted.org/packages/a3/d2/137c7b6841c942124eae921279e5c41b1c34bab0e6fc60c7348e69afd165/numpy-2.3.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36dc13af226aeab72b7abad501d370d606326a0029b9f435eacb3b8c94b8a8b7", size = 14591904 }, - { url = "https://files.pythonhosted.org/packages/bb/32/67e3b0f07b0aba57a078c4ab777a9e8e6bc62f24fb53a2337f75f9691699/numpy-2.3.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7b2f9a18b5ff9824a6af80de4f37f4ec3c2aab05ef08f51c77a093f5b89adda", size = 16939602 }, - { url = "https://files.pythonhosted.org/packages/95/22/9639c30e32c93c4cee3ccdb4b09c2d0fbff4dcd06d36b357da06146530fb/numpy-2.3.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9984bd645a8db6ca15d850ff996856d8762c51a2239225288f08f9050ca240a0", size = 16372661 }, - { url = "https://files.pythonhosted.org/packages/12/e9/a685079529be2b0156ae0c11b13d6be647743095bb51d46589e95be88086/numpy-2.3.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:64c5825affc76942973a70acf438a8ab618dbd692b84cd5ec40a0a0509edc09a", size = 18884682 }, - { url = "https://files.pythonhosted.org/packages/cf/85/f6f00d019b0cc741e64b4e00ce865a57b6bed945d1bbeb1ccadbc647959b/numpy-2.3.4-cp311-cp311-win32.whl", hash = "sha256:ed759bf7a70342f7817d88376eb7142fab9fef8320d6019ef87fae05a99874e1", size = 6570076 }, - { url = "https://files.pythonhosted.org/packages/7d/10/f8850982021cb90e2ec31990291f9e830ce7d94eef432b15066e7cbe0bec/numpy-2.3.4-cp311-cp311-win_amd64.whl", hash = "sha256:faba246fb30ea2a526c2e9645f61612341de1a83fb1e0c5edf4ddda5a9c10996", size = 13089358 }, - { url = "https://files.pythonhosted.org/packages/d1/ad/afdd8351385edf0b3445f9e24210a9c3971ef4de8fd85155462fc4321d79/numpy-2.3.4-cp311-cp311-win_arm64.whl", hash = "sha256:4c01835e718bcebe80394fd0ac66c07cbb90147ebbdad3dcecd3f25de2ae7e2c", size = 10462292 }, - { url = "https://files.pythonhosted.org/packages/96/7a/02420400b736f84317e759291b8edaeee9dc921f72b045475a9cbdb26b17/numpy-2.3.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ef1b5a3e808bc40827b5fa2c8196151a4c5abe110e1726949d7abddfe5c7ae11", size = 20957727 }, - { url = "https://files.pythonhosted.org/packages/18/90/a014805d627aa5750f6f0e878172afb6454552da929144b3c07fcae1bb13/numpy-2.3.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c2f91f496a87235c6aaf6d3f3d89b17dba64996abadccb289f48456cff931ca9", size = 14187262 }, - { url = "https://files.pythonhosted.org/packages/c7/e4/0a94b09abe89e500dc748e7515f21a13e30c5c3fe3396e6d4ac108c25fca/numpy-2.3.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:f77e5b3d3da652b474cc80a14084927a5e86a5eccf54ca8ca5cbd697bf7f2667", size = 5115992 }, - { url = "https://files.pythonhosted.org/packages/88/dd/db77c75b055c6157cbd4f9c92c4458daef0dd9cbe6d8d2fe7f803cb64c37/numpy-2.3.4-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:8ab1c5f5ee40d6e01cbe96de5863e39b215a4d24e7d007cad56c7184fdf4aeef", size = 6648672 }, - { url = "https://files.pythonhosted.org/packages/e1/e6/e31b0d713719610e406c0ea3ae0d90760465b086da8783e2fd835ad59027/numpy-2.3.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77b84453f3adcb994ddbd0d1c5d11db2d6bda1a2b7fd5ac5bd4649d6f5dc682e", size = 14284156 }, - { url = "https://files.pythonhosted.org/packages/f9/58/30a85127bfee6f108282107caf8e06a1f0cc997cb6b52cdee699276fcce4/numpy-2.3.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4121c5beb58a7f9e6dfdee612cb24f4df5cd4db6e8261d7f4d7450a997a65d6a", size = 16641271 }, - { url = "https://files.pythonhosted.org/packages/06/f2/2e06a0f2adf23e3ae29283ad96959267938d0efd20a2e25353b70065bfec/numpy-2.3.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:65611ecbb00ac9846efe04db15cbe6186f562f6bb7e5e05f077e53a599225d16", size = 16059531 }, - { url = "https://files.pythonhosted.org/packages/b0/e7/b106253c7c0d5dc352b9c8fab91afd76a93950998167fa3e5afe4ef3a18f/numpy-2.3.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dabc42f9c6577bcc13001b8810d300fe814b4cfbe8a92c873f269484594f9786", size = 18578983 }, - { url = "https://files.pythonhosted.org/packages/73/e3/04ecc41e71462276ee867ccbef26a4448638eadecf1bc56772c9ed6d0255/numpy-2.3.4-cp312-cp312-win32.whl", hash = "sha256:a49d797192a8d950ca59ee2d0337a4d804f713bb5c3c50e8db26d49666e351dc", size = 6291380 }, - { url = "https://files.pythonhosted.org/packages/3d/a8/566578b10d8d0e9955b1b6cd5db4e9d4592dd0026a941ff7994cedda030a/numpy-2.3.4-cp312-cp312-win_amd64.whl", hash = "sha256:985f1e46358f06c2a09921e8921e2c98168ed4ae12ccd6e5e87a4f1857923f32", size = 12787999 }, - { url = "https://files.pythonhosted.org/packages/58/22/9c903a957d0a8071b607f5b1bff0761d6e608b9a965945411f867d515db1/numpy-2.3.4-cp312-cp312-win_arm64.whl", hash = "sha256:4635239814149e06e2cb9db3dd584b2fa64316c96f10656983b8026a82e6e4db", size = 10197412 }, - { url = "https://files.pythonhosted.org/packages/57/7e/b72610cc91edf138bc588df5150957a4937221ca6058b825b4725c27be62/numpy-2.3.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c090d4860032b857d94144d1a9976b8e36709e40386db289aaf6672de2a81966", size = 20950335 }, - { url = "https://files.pythonhosted.org/packages/3e/46/bdd3370dcea2f95ef14af79dbf81e6927102ddf1cc54adc0024d61252fd9/numpy-2.3.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a13fc473b6db0be619e45f11f9e81260f7302f8d180c49a22b6e6120022596b3", size = 14179878 }, - { url = "https://files.pythonhosted.org/packages/ac/01/5a67cb785bda60f45415d09c2bc245433f1c68dd82eef9c9002c508b5a65/numpy-2.3.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:3634093d0b428e6c32c3a69b78e554f0cd20ee420dcad5a9f3b2a63762ce4197", size = 5108673 }, - { url = "https://files.pythonhosted.org/packages/c2/cd/8428e23a9fcebd33988f4cb61208fda832800ca03781f471f3727a820704/numpy-2.3.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:043885b4f7e6e232d7df4f51ffdef8c36320ee9d5f227b380ea636722c7ed12e", size = 6641438 }, - { url = "https://files.pythonhosted.org/packages/3e/d1/913fe563820f3c6b079f992458f7331278dcd7ba8427e8e745af37ddb44f/numpy-2.3.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4ee6a571d1e4f0ea6d5f22d6e5fbd6ed1dc2b18542848e1e7301bd190500c9d7", size = 14281290 }, - { url = "https://files.pythonhosted.org/packages/9e/7e/7d306ff7cb143e6d975cfa7eb98a93e73495c4deabb7d1b5ecf09ea0fd69/numpy-2.3.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fc8a63918b04b8571789688b2780ab2b4a33ab44bfe8ccea36d3eba51228c953", size = 16636543 }, - { url = "https://files.pythonhosted.org/packages/47/6a/8cfc486237e56ccfb0db234945552a557ca266f022d281a2f577b98e955c/numpy-2.3.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:40cc556d5abbc54aabe2b1ae287042d7bdb80c08edede19f0c0afb36ae586f37", size = 16056117 }, - { url = "https://files.pythonhosted.org/packages/b1/0e/42cb5e69ea901e06ce24bfcc4b5664a56f950a70efdcf221f30d9615f3f3/numpy-2.3.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ecb63014bb7f4ce653f8be7f1df8cbc6093a5a2811211770f6606cc92b5a78fd", size = 18577788 }, - { url = "https://files.pythonhosted.org/packages/86/92/41c3d5157d3177559ef0a35da50f0cda7fa071f4ba2306dd36818591a5bc/numpy-2.3.4-cp313-cp313-win32.whl", hash = "sha256:e8370eb6925bb8c1c4264fec52b0384b44f675f191df91cbe0140ec9f0955646", size = 6282620 }, - { url = "https://files.pythonhosted.org/packages/09/97/fd421e8bc50766665ad35536c2bb4ef916533ba1fdd053a62d96cc7c8b95/numpy-2.3.4-cp313-cp313-win_amd64.whl", hash = "sha256:56209416e81a7893036eea03abcb91c130643eb14233b2515c90dcac963fe99d", size = 12784672 }, - { url = "https://files.pythonhosted.org/packages/ad/df/5474fb2f74970ca8eb978093969b125a84cc3d30e47f82191f981f13a8a0/numpy-2.3.4-cp313-cp313-win_arm64.whl", hash = "sha256:a700a4031bc0fd6936e78a752eefb79092cecad2599ea9c8039c548bc097f9bc", size = 10196702 }, - { url = "https://files.pythonhosted.org/packages/11/83/66ac031464ec1767ea3ed48ce40f615eb441072945e98693bec0bcd056cc/numpy-2.3.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:86966db35c4040fdca64f0816a1c1dd8dbd027d90fca5a57e00e1ca4cd41b879", size = 21049003 }, - { url = "https://files.pythonhosted.org/packages/5f/99/5b14e0e686e61371659a1d5bebd04596b1d72227ce36eed121bb0aeab798/numpy-2.3.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:838f045478638b26c375ee96ea89464d38428c69170360b23a1a50fa4baa3562", size = 14302980 }, - { url = "https://files.pythonhosted.org/packages/2c/44/e9486649cd087d9fc6920e3fc3ac2aba10838d10804b1e179fb7cbc4e634/numpy-2.3.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d7315ed1dab0286adca467377c8381cd748f3dc92235f22a7dfc42745644a96a", size = 5231472 }, - { url = "https://files.pythonhosted.org/packages/3e/51/902b24fa8887e5fe2063fd61b1895a476d0bbf46811ab0c7fdf4bd127345/numpy-2.3.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:84f01a4d18b2cc4ade1814a08e5f3c907b079c847051d720fad15ce37aa930b6", size = 6739342 }, - { url = "https://files.pythonhosted.org/packages/34/f1/4de9586d05b1962acdcdb1dc4af6646361a643f8c864cef7c852bf509740/numpy-2.3.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:817e719a868f0dacde4abdfc5c1910b301877970195db9ab6a5e2c4bd5b121f7", size = 14354338 }, - { url = "https://files.pythonhosted.org/packages/1f/06/1c16103b425de7969d5a76bdf5ada0804b476fed05d5f9e17b777f1cbefd/numpy-2.3.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85e071da78d92a214212cacea81c6da557cab307f2c34b5f85b628e94803f9c0", size = 16702392 }, - { url = "https://files.pythonhosted.org/packages/34/b2/65f4dc1b89b5322093572b6e55161bb42e3e0487067af73627f795cc9d47/numpy-2.3.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2ec646892819370cf3558f518797f16597b4e4669894a2ba712caccc9da53f1f", size = 16134998 }, - { url = "https://files.pythonhosted.org/packages/d4/11/94ec578896cdb973aaf56425d6c7f2aff4186a5c00fac15ff2ec46998b46/numpy-2.3.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:035796aaaddfe2f9664b9a9372f089cfc88bd795a67bd1bfe15e6e770934cf64", size = 18651574 }, - { url = "https://files.pythonhosted.org/packages/62/b7/7efa763ab33dbccf56dade36938a77345ce8e8192d6b39e470ca25ff3cd0/numpy-2.3.4-cp313-cp313t-win32.whl", hash = "sha256:fea80f4f4cf83b54c3a051f2f727870ee51e22f0248d3114b8e755d160b38cfb", size = 6413135 }, - { url = "https://files.pythonhosted.org/packages/43/70/aba4c38e8400abcc2f345e13d972fb36c26409b3e644366db7649015f291/numpy-2.3.4-cp313-cp313t-win_amd64.whl", hash = "sha256:15eea9f306b98e0be91eb344a94c0e630689ef302e10c2ce5f7e11905c704f9c", size = 12928582 }, - { url = "https://files.pythonhosted.org/packages/67/63/871fad5f0073fc00fbbdd7232962ea1ac40eeaae2bba66c76214f7954236/numpy-2.3.4-cp313-cp313t-win_arm64.whl", hash = "sha256:b6c231c9c2fadbae4011ca5e7e83e12dc4a5072f1a1d85a0a7b3ed754d145a40", size = 10266691 }, - { url = "https://files.pythonhosted.org/packages/72/71/ae6170143c115732470ae3a2d01512870dd16e0953f8a6dc89525696069b/numpy-2.3.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:81c3e6d8c97295a7360d367f9f8553973651b76907988bb6066376bc2252f24e", size = 20955580 }, - { url = "https://files.pythonhosted.org/packages/af/39/4be9222ffd6ca8a30eda033d5f753276a9c3426c397bb137d8e19dedd200/numpy-2.3.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7c26b0b2bf58009ed1f38a641f3db4be8d960a417ca96d14e5b06df1506d41ff", size = 14188056 }, - { url = "https://files.pythonhosted.org/packages/6c/3d/d85f6700d0a4aa4f9491030e1021c2b2b7421b2b38d01acd16734a2bfdc7/numpy-2.3.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:62b2198c438058a20b6704351b35a1d7db881812d8512d67a69c9de1f18ca05f", size = 5116555 }, - { url = "https://files.pythonhosted.org/packages/bf/04/82c1467d86f47eee8a19a464c92f90a9bb68ccf14a54c5224d7031241ffb/numpy-2.3.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:9d729d60f8d53a7361707f4b68a9663c968882dd4f09e0d58c044c8bf5faee7b", size = 6643581 }, - { url = "https://files.pythonhosted.org/packages/0c/d3/c79841741b837e293f48bd7db89d0ac7a4f2503b382b78a790ef1dc778a5/numpy-2.3.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd0c630cf256b0a7fd9d0a11c9413b42fef5101219ce6ed5a09624f5a65392c7", size = 14299186 }, - { url = "https://files.pythonhosted.org/packages/e8/7e/4a14a769741fbf237eec5a12a2cbc7a4c4e061852b6533bcb9e9a796c908/numpy-2.3.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5e081bc082825f8b139f9e9fe42942cb4054524598aaeb177ff476cc76d09d2", size = 16638601 }, - { url = "https://files.pythonhosted.org/packages/93/87/1c1de269f002ff0a41173fe01dcc925f4ecff59264cd8f96cf3b60d12c9b/numpy-2.3.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:15fb27364ed84114438fff8aaf998c9e19adbeba08c0b75409f8c452a8692c52", size = 16074219 }, - { url = "https://files.pythonhosted.org/packages/cd/28/18f72ee77408e40a76d691001ae599e712ca2a47ddd2c4f695b16c65f077/numpy-2.3.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:85d9fb2d8cd998c84d13a79a09cc0c1091648e848e4e6249b0ccd7f6b487fa26", size = 18576702 }, - { url = "https://files.pythonhosted.org/packages/c3/76/95650169b465ececa8cf4b2e8f6df255d4bf662775e797ade2025cc51ae6/numpy-2.3.4-cp314-cp314-win32.whl", hash = "sha256:e73d63fd04e3a9d6bc187f5455d81abfad05660b212c8804bf3b407e984cd2bc", size = 6337136 }, - { url = "https://files.pythonhosted.org/packages/dc/89/a231a5c43ede5d6f77ba4a91e915a87dea4aeea76560ba4d2bf185c683f0/numpy-2.3.4-cp314-cp314-win_amd64.whl", hash = "sha256:3da3491cee49cf16157e70f607c03a217ea6647b1cea4819c4f48e53d49139b9", size = 12920542 }, - { url = "https://files.pythonhosted.org/packages/0d/0c/ae9434a888f717c5ed2ff2393b3f344f0ff6f1c793519fa0c540461dc530/numpy-2.3.4-cp314-cp314-win_arm64.whl", hash = "sha256:6d9cd732068e8288dbe2717177320723ccec4fb064123f0caf9bbd90ab5be868", size = 10480213 }, - { url = "https://files.pythonhosted.org/packages/83/4b/c4a5f0841f92536f6b9592694a5b5f68c9ab37b775ff342649eadf9055d3/numpy-2.3.4-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:22758999b256b595cf0b1d102b133bb61866ba5ceecf15f759623b64c020c9ec", size = 21052280 }, - { url = "https://files.pythonhosted.org/packages/3e/80/90308845fc93b984d2cc96d83e2324ce8ad1fd6efea81b324cba4b673854/numpy-2.3.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9cb177bc55b010b19798dc5497d540dea67fd13a8d9e882b2dae71de0cf09eb3", size = 14302930 }, - { url = "https://files.pythonhosted.org/packages/3d/4e/07439f22f2a3b247cec4d63a713faae55e1141a36e77fb212881f7cda3fb/numpy-2.3.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:0f2bcc76f1e05e5ab58893407c63d90b2029908fa41f9f1cc51eecce936c3365", size = 5231504 }, - { url = "https://files.pythonhosted.org/packages/ab/de/1e11f2547e2fe3d00482b19721855348b94ada8359aef5d40dd57bfae9df/numpy-2.3.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:8dc20bde86802df2ed8397a08d793da0ad7a5fd4ea3ac85d757bf5dd4ad7c252", size = 6739405 }, - { url = "https://files.pythonhosted.org/packages/3b/40/8cd57393a26cebe2e923005db5134a946c62fa56a1087dc7c478f3e30837/numpy-2.3.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e199c087e2aa71c8f9ce1cb7a8e10677dc12457e7cc1be4798632da37c3e86e", size = 14354866 }, - { url = "https://files.pythonhosted.org/packages/93/39/5b3510f023f96874ee6fea2e40dfa99313a00bf3ab779f3c92978f34aace/numpy-2.3.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85597b2d25ddf655495e2363fe044b0ae999b75bc4d630dc0d886484b03a5eb0", size = 16703296 }, - { url = "https://files.pythonhosted.org/packages/41/0d/19bb163617c8045209c1996c4e427bccbc4bbff1e2c711f39203c8ddbb4a/numpy-2.3.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:04a69abe45b49c5955923cf2c407843d1c85013b424ae8a560bba16c92fe44a0", size = 16136046 }, - { url = "https://files.pythonhosted.org/packages/e2/c1/6dba12fdf68b02a21ac411c9df19afa66bed2540f467150ca64d246b463d/numpy-2.3.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e1708fac43ef8b419c975926ce1eaf793b0c13b7356cfab6ab0dc34c0a02ac0f", size = 18652691 }, - { url = "https://files.pythonhosted.org/packages/f8/73/f85056701dbbbb910c51d846c58d29fd46b30eecd2b6ba760fc8b8a1641b/numpy-2.3.4-cp314-cp314t-win32.whl", hash = "sha256:863e3b5f4d9915aaf1b8ec79ae560ad21f0b8d5e3adc31e73126491bb86dee1d", size = 6485782 }, - { url = "https://files.pythonhosted.org/packages/17/90/28fa6f9865181cb817c2471ee65678afa8a7e2a1fb16141473d5fa6bacc3/numpy-2.3.4-cp314-cp314t-win_amd64.whl", hash = "sha256:962064de37b9aef801d33bc579690f8bfe6c5e70e29b61783f60bcba838a14d6", size = 13113301 }, - { url = "https://files.pythonhosted.org/packages/54/23/08c002201a8e7e1f9afba93b97deceb813252d9cfd0d3351caed123dcf97/numpy-2.3.4-cp314-cp314t-win_arm64.whl", hash = "sha256:8b5a9a39c45d852b62693d9b3f3e0fe052541f804296ff401a72a1b60edafb29", size = 10547532 }, - { url = "https://files.pythonhosted.org/packages/b1/b6/64898f51a86ec88ca1257a59c1d7fd077b60082a119affefcdf1dd0df8ca/numpy-2.3.4-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6e274603039f924c0fe5cb73438fa9246699c78a6df1bd3decef9ae592ae1c05", size = 21131552 }, - { url = "https://files.pythonhosted.org/packages/ce/4c/f135dc6ebe2b6a3c77f4e4838fa63d350f85c99462012306ada1bd4bc460/numpy-2.3.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d149aee5c72176d9ddbc6803aef9c0f6d2ceeea7626574fc68518da5476fa346", size = 14377796 }, - { url = "https://files.pythonhosted.org/packages/d0/a4/f33f9c23fcc13dd8412fc8614559b5b797e0aba9d8e01dfa8bae10c84004/numpy-2.3.4-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:6d34ed9db9e6395bb6cd33286035f73a59b058169733a9db9f85e650b88df37e", size = 5306904 }, - { url = "https://files.pythonhosted.org/packages/28/af/c44097f25f834360f9fb960fa082863e0bad14a42f36527b2a121abdec56/numpy-2.3.4-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:fdebe771ca06bb8d6abce84e51dca9f7921fe6ad34a0c914541b063e9a68928b", size = 6819682 }, - { url = "https://files.pythonhosted.org/packages/c5/8c/cd283b54c3c2b77e188f63e23039844f56b23bba1712318288c13fe86baf/numpy-2.3.4-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e92defe6c08211eb77902253b14fe5b480ebc5112bc741fd5e9cd0608f847", size = 14422300 }, - { url = "https://files.pythonhosted.org/packages/b0/f0/8404db5098d92446b3e3695cf41c6f0ecb703d701cb0b7566ee2177f2eee/numpy-2.3.4-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13b9062e4f5c7ee5c7e5be96f29ba71bc5a37fed3d1d77c37390ae00724d296d", size = 16760806 }, - { url = "https://files.pythonhosted.org/packages/95/8e/2844c3959ce9a63acc7c8e50881133d86666f0420bcde695e115ced0920f/numpy-2.3.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:81b3a59793523e552c4a96109dde028aa4448ae06ccac5a76ff6532a85558a7f", size = 12973130 }, -] - -[[package]] -name = "packaging" -version = "24.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/51/65/50db4dda066951078f0a96cf12f4b9ada6e4b811516bf0262c0f4f7064d4/packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", size = 148788 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/08/aa/cc0199a5f0ad350994d660967a8efb233fe0416e4639146c089643407ce6/packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124", size = 53985 }, -] - -[[package]] -name = "pandas" -version = "2.0.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "python-dateutil" }, - { name = "pytz" }, - { name = "tzdata" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/b1/a7/824332581e258b5aa4f3763ecb2a797e5f9a54269044ba2e50ac19936b32/pandas-2.0.3.tar.gz", hash = "sha256:c02f372a88e0d17f36d3093a644c73cfc1788e876a7c4bcb4020a77512e2043c", size = 5284455 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/b2/0d4a5729ce1ce11630c4fc5d5522a33b967b3ca146c210f58efde7c40e99/pandas-2.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e4c7c9f27a4185304c7caf96dc7d91bc60bc162221152de697c98eb0b2648dd8", size = 11760908 }, - { url = "https://files.pythonhosted.org/packages/4a/f6/f620ca62365d83e663a255a41b08d2fc2eaf304e0b8b21bb6d62a7390fe3/pandas-2.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f167beed68918d62bffb6ec64f2e1d8a7d297a038f86d4aed056b9493fca407f", size = 10823486 }, - { url = "https://files.pythonhosted.org/packages/c2/59/cb4234bc9b968c57e81861b306b10cd8170272c57b098b724d3de5eda124/pandas-2.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce0c6f76a0f1ba361551f3e6dceaff06bde7514a374aa43e33b588ec10420183", size = 11571897 }, - { url = "https://files.pythonhosted.org/packages/e3/59/35a2892bf09ded9c1bf3804461efe772836a5261ef5dfb4e264ce813ff99/pandas-2.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba619e410a21d8c387a1ea6e8a0e49bb42216474436245718d7f2e88a2f8d7c0", size = 12306421 }, - { url = "https://files.pythonhosted.org/packages/94/71/3a0c25433c54bb29b48e3155b959ac78f4c4f2f06f94d8318aac612cb80f/pandas-2.0.3-cp310-cp310-win32.whl", hash = "sha256:3ef285093b4fe5058eefd756100a367f27029913760773c8bf1d2d8bebe5d210", size = 9540792 }, - { url = "https://files.pythonhosted.org/packages/ed/30/b97456e7063edac0e5a405128065f0cd2033adfe3716fb2256c186bd41d0/pandas-2.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:9ee1a69328d5c36c98d8e74db06f4ad518a1840e8ccb94a4ba86920986bb617e", size = 10664333 }, - { url = "https://files.pythonhosted.org/packages/b3/92/a5e5133421b49e901a12e02a6a7ef3a0130e10d13db8cb657fdd0cba3b90/pandas-2.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b084b91d8d66ab19f5bb3256cbd5ea661848338301940e17f4492b2ce0801fe8", size = 11645672 }, - { url = "https://files.pythonhosted.org/packages/8f/bb/aea1fbeed5b474cb8634364718abe9030d7cc7a30bf51f40bd494bbc89a2/pandas-2.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:37673e3bdf1551b95bf5d4ce372b37770f9529743d2498032439371fc7b7eb26", size = 10693229 }, - { url = "https://files.pythonhosted.org/packages/d6/90/e7d387f1a416b14e59290baa7a454a90d719baebbf77433ff1bdcc727800/pandas-2.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9cb1e14fdb546396b7e1b923ffaeeac24e4cedd14266c3497216dd4448e4f2d", size = 11581591 }, - { url = "https://files.pythonhosted.org/packages/d0/28/88b81881c056376254618fad622a5e94b5126db8c61157ea1910cd1c040a/pandas-2.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9cd88488cceb7635aebb84809d087468eb33551097d600c6dad13602029c2df", size = 12219370 }, - { url = "https://files.pythonhosted.org/packages/e4/a5/212b9039e25bf8ebb97e417a96660e3dc925dacd3f8653d531b8f7fd9be4/pandas-2.0.3-cp311-cp311-win32.whl", hash = "sha256:694888a81198786f0e164ee3a581df7d505024fbb1f15202fc7db88a71d84ebd", size = 9482935 }, - { url = "https://files.pythonhosted.org/packages/9e/71/756a1be6bee0209d8c0d8c5e3b9fc72c00373f384a4017095ec404aec3ad/pandas-2.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:6a21ab5c89dcbd57f78d0ae16630b090eec626360085a4148693def5452d8a6b", size = 10607692 }, -] - -[[package]] -name = "platformdirs" -version = "4.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/61/33/9611380c2bdb1225fdef633e2a9610622310fed35ab11dac9620972ee088/platformdirs-4.5.0.tar.gz", hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312", size = 21632 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/73/cb/ac7874b3e5d58441674fb70742e6c374b28b0c7cb988d37d991cde47166c/platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3", size = 18651 }, -] - -[[package]] -name = "pluggy" -version = "1.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, -] - -[[package]] -name = "pre-commit" -version = "4.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "cfgv" }, - { name = "identify" }, - { name = "nodeenv" }, - { name = "pyyaml" }, - { name = "virtualenv" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ff/29/7cf5bbc236333876e4b41f56e06857a87937ce4bf91e117a6991a2dbb02a/pre_commit-4.3.0.tar.gz", hash = "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16", size = 193792 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/a5/987a405322d78a73b66e39e4a90e4ef156fd7141bf71df987e50717c321b/pre_commit-4.3.0-py2.py3-none-any.whl", hash = "sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8", size = 220965 }, -] - -[[package]] -name = "pycodestyle" -version = "2.14.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/11/e0/abfd2a0d2efe47670df87f3e3a0e2edda42f055053c85361f19c0e2c1ca8/pycodestyle-2.14.0.tar.gz", hash = "sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783", size = 39472 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/27/a58ddaf8c588a3ef080db9d0b7e0b97215cee3a45df74f3a94dbbf5c893a/pycodestyle-2.14.0-py2.py3-none-any.whl", hash = "sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d", size = 31594 }, -] - -[[package]] -name = "pydata-sphinx-theme" -version = "0.16.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "accessible-pygments" }, - { name = "babel" }, - { name = "beautifulsoup4" }, - { name = "docutils" }, - { name = "pygments" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "typing-extensions" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/00/20/bb50f9de3a6de69e6abd6b087b52fa2418a0418b19597601605f855ad044/pydata_sphinx_theme-0.16.1.tar.gz", hash = "sha256:a08b7f0b7f70387219dc659bff0893a7554d5eb39b59d3b8ef37b8401b7642d7", size = 2412693 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/0d/8ba33fa83a7dcde13eb3c1c2a0c1cc29950a048bfed6d9b0d8b6bd710b4c/pydata_sphinx_theme-0.16.1-py3-none-any.whl", hash = "sha256:225331e8ac4b32682c18fcac5a57a6f717c4e632cea5dd0e247b55155faeccde", size = 6723264 }, -] - -[[package]] -name = "pyflakes" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/45/dc/fd034dc20b4b264b3d015808458391acbf9df40b1e54750ef175d39180b1/pyflakes-3.4.0.tar.gz", hash = "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58", size = 64669 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/2f/81d580a0fb83baeb066698975cb14a618bdbed7720678566f1b046a95fe8/pyflakes-3.4.0-py2.py3-none-any.whl", hash = "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f", size = 63551 }, -] - -[[package]] -name = "pygments" -version = "2.18.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8e/62/8336eff65bcbc8e4cb5d05b55faf041285951b6e80f33e2bff2024788f31/pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", size = 4891905 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", size = 1205513 }, -] - -[[package]] -name = "pytest" -version = "8.4.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, - { name = "iniconfig" }, - { name = "packaging" }, - { name = "pluggy" }, - { name = "pygments" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750 }, -] - -[[package]] -name = "pytest-asyncio" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" }, - { name = "pytest" }, - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/86/9e3c5f48f7b7b638b216e4b9e645f54d199d7abbbab7a64a13b4e12ba10f/pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57", size = 50119 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/93/2fa34714b7a4ae72f2f8dad66ba17dd9a2c793220719e736dda28b7aec27/pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99", size = 15095 }, -] - -[[package]] -name = "pytest-cov" -version = "7.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "coverage", extra = ["toml"] }, - { name = "pluggy" }, - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424 }, -] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "six" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, -] - -[[package]] -name = "pytz" -version = "2024.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3a/31/3c70bf7603cc2dca0f19bdc53b4537a797747a58875b552c8c413d963a3f/pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a", size = 319692 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/11/c3/005fcca25ce078d2cc29fd559379817424e94885510568bc1bc53d7d5846/pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725", size = 508002 }, -] - -[[package]] -name = "pywavelets" -version = "1.8.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/48/45/bfaaab38545a33a9f06c61211fc3bea2e23e8a8e00fedeb8e57feda722ff/pywavelets-1.8.0.tar.gz", hash = "sha256:f3800245754840adc143cbc29534a1b8fc4b8cff6e9d403326bd52b7bb5c35aa", size = 3935274 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/65/7e/c5e398f25c70558ca195dd4144ee004666401f6167084c1e76059d7e68d8/pywavelets-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f5c86fcb203c8e61d1f3d4afbfc08d626c64e4e3708207315577264c724632bf", size = 4323291 }, - { url = "https://files.pythonhosted.org/packages/d0/d7/2fc8067c3520ce25f7632b0f47b89d1b75653cab804a42700e95126f2679/pywavelets-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fafb5fa126277e1690c3d6329287122fc08e4d25a262ce126e3d81b1f5709308", size = 4291864 }, - { url = "https://files.pythonhosted.org/packages/2f/17/a868aa26e45c104613d9069f9d8ec0123687cb6945062d274f20a3992436/pywavelets-1.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dec23dfe6d5a3f4312b12456b8c546aa90a11c1138e425a885987505f0658ae0", size = 4447532 }, - { url = "https://files.pythonhosted.org/packages/53/7a/7f5889a57177e2b1182080fc2c52236d1e03a0fad5e0b3d7c5312070c0be/pywavelets-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:880a0197e9fa108939af50a95e97c1bf9b7d3e148e0fad92ea60a9ed8c8947c0", size = 4487695 }, - { url = "https://files.pythonhosted.org/packages/f9/e6/04d76d93c158919ef0d8e1d40d1d453168305031eca6733fdc844f7cbb07/pywavelets-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8bfa833d08b60d0bf53a7939fbbf3d98015dd34efe89cbe4e53ced880d085fc1", size = 4473752 }, - { url = "https://files.pythonhosted.org/packages/3b/a7/42ea5bbb6055abd312e45b27d931200fd6eed5414a87ec5d62020a4c651b/pywavelets-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e10c3fc7f4a796e94da4bca9871be2186a7bb7a3b3536a0ca9376d84263140f0", size = 4504191 }, - { url = "https://files.pythonhosted.org/packages/8c/7e/52df87a9e77adfb12c1b8be79a2053f2eb4c2507dec96ebfd2333b15ff03/pywavelets-1.8.0-cp310-cp310-win32.whl", hash = "sha256:31baf4be6940fde72cc85663154360857ac1b93c251822deaf72bb804da95031", size = 4143794 }, - { url = "https://files.pythonhosted.org/packages/01/e2/06e08230c26049740b2773952fbb12cc7186e5df655a73b1c30ba493e864/pywavelets-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:560c39f1ff8cb37f8b8ea4b7b6eb8a14f6926c11f5cf8c09f013a58f895ed5bc", size = 4214262 }, - { url = "https://files.pythonhosted.org/packages/6c/8a/9f8e794120b55caa1c4ae8d72696111bc408251615f351a8e54a5d8c4d4e/pywavelets-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e8dd5be4faed994581a8a4b3c0169be20567a9346e523f0b57f903c8f6722bce", size = 4324170 }, - { url = "https://files.pythonhosted.org/packages/3e/b8/f6246be5c78e9fa73fcbba9ab4cbfe0d4dcb79ea5491f28d673a53466134/pywavelets-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8d8abaf7c120b151ef309c9ff57e0a44ba9febf49045056dbc1577526ecec6c8", size = 4294254 }, - { url = "https://files.pythonhosted.org/packages/2c/dc/ba1f212e9b43117ed28e0fd092e72e817790427400f88937ea742d260153/pywavelets-1.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b43a4c58707b1e8d941bec7f1d83e67c482278575ff0db3189d5c0dfae23a57", size = 4447178 }, - { url = "https://files.pythonhosted.org/packages/58/10/e59c162a11d2fedb4454abbf7b74a52390aba5edc9605bf829bfa8708dac/pywavelets-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1aad0b97714e3079a2bfe48e4fb8ccd60778d0427e9ee5e0a9ff922e6c61e4", size = 4486799 }, - { url = "https://files.pythonhosted.org/packages/03/ee/90c3d0a0a3bda74e6e097e4c06bff9446ff2a4c90b8617aaf4902c46966b/pywavelets-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a0e1db96dcf3ce08156859df8b359e9ff66fa15061a1b90e70e020bf4cd077a0", size = 4486403 }, - { url = "https://files.pythonhosted.org/packages/05/54/58b87f8b636a9f044f3f9814d2ec696cf25f3b33af97c11811f13c364085/pywavelets-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e62c8fb52ab0e8ff212fff9acae681a8f12d68b76c36fe24cc48809d5b6825ba", size = 4515011 }, - { url = "https://files.pythonhosted.org/packages/a1/d0/f755cee11ff20668114942d0e777e2b502a8e4665e1fdb2553b587aac637/pywavelets-1.8.0-cp311-cp311-win32.whl", hash = "sha256:bf327528d10de471b04bb725c4e10677fac5a49e13d41bf0d0b3a1f6d7097abf", size = 4139934 }, - { url = "https://files.pythonhosted.org/packages/7b/0b/f4b92d4f00565280ea3e62a8e3dc81a667d67ed7bd59232f2f18d55f9aff/pywavelets-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:3814d354dd109e244ffaac3d480d29a5202212fe24570c920268237c8d276f95", size = 4214321 }, - { url = "https://files.pythonhosted.org/packages/2d/8b/4870f11559307416470158a5aa6f61e5c2a910f1645a7a836ffae580b7ad/pywavelets-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3f431c9e2aff1a2240765eff5e804975d0fcc24c82d6f3d4271243f228e5963b", size = 4326187 }, - { url = "https://files.pythonhosted.org/packages/c4/35/66835d889fd7fbf3119c7a9bd9d9bd567fc0bb603dfba408e9226db7cb44/pywavelets-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e39b0e2314e928cb850ee89b9042733a10ea044176a495a54dc84d2c98407a51", size = 4295428 }, - { url = "https://files.pythonhosted.org/packages/63/1c/42e5130226538c70d4bbbaee00eb1bc06ec3287f7ea43d5fcf85bfc761ce/pywavelets-1.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cae701117f5c7244b7c8d48b9e92a0289637cdc02a9c205e8be83361f0c11fae", size = 4421259 }, - { url = "https://files.pythonhosted.org/packages/6f/c5/1ce93657432e22a5debc21e8b52ec6980f819ecb7fa727bb86744224d967/pywavelets-1.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:649936baee933e80083788e0adc4d8bc2da7cdd8b10464d3b113475be2cc5308", size = 4447650 }, - { url = "https://files.pythonhosted.org/packages/b9/d6/b54ef30daca71824f811f9d2322a978b0a58d27674b8e3af6520f67e9ec6/pywavelets-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8c68e9d072c536bc646e8bdce443bb1826eeb9aa21b2cb2479a43954dea692a3", size = 4448538 }, - { url = "https://files.pythonhosted.org/packages/ce/8c/1688b790e55674667ad644262f174405c2c9873cb13e773432e78b1b33e4/pywavelets-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:63f67fa2ee1610445de64f746fb9c1df31980ad13d896ea2331fc3755f49b3ae", size = 4485228 }, - { url = "https://files.pythonhosted.org/packages/c9/9b/69de31c3b663dadd76d1da6bf8af68d8cefff55df8e880fe96a94bb8c9ac/pywavelets-1.8.0-cp312-cp312-win32.whl", hash = "sha256:4b3c2ab669c91e3474fd63294355487b7dd23f0b51d32f811327ddf3546f4f3d", size = 4134850 }, - { url = "https://files.pythonhosted.org/packages/1c/88/9e2aa9d5fde08bfc0fb18ffb1b5307c1ed49c24930b4147e5f48571a7251/pywavelets-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:810a23a631da596fef7196ddec49b345b1aab13525bb58547eeebe1769edbbc1", size = 4210786 }, - { url = "https://files.pythonhosted.org/packages/94/73/7ff347d77c6bda11330565050c3346c54bc210086380abeb84e402c1c9cd/pywavelets-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:441ba45c8dff8c6916dbe706958d0d7f91da675695ca0c0d75e483f6f52d0a12", size = 4321474 }, - { url = "https://files.pythonhosted.org/packages/b0/70/c58937ebbca1aba3475ca5ee63c7bcebf09f3c93891ae5942eaec7e95707/pywavelets-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:24bb282bab09349d9d128ed0536fa50fff5c2147891971a69c2c36155dfeeeac", size = 4291502 }, - { url = "https://files.pythonhosted.org/packages/da/55/87b4ad6128b2e85985908e958e856e0b680cdcc03cc490e2cc995164b13a/pywavelets-1.8.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:426ff3799446cb4da1db04c2084e6e58edfe24225596805665fd39c14f53dece", size = 4412669 }, - { url = "https://files.pythonhosted.org/packages/bf/1a/bfca9eab23bd7b27843b0ce95c47796033a7b2c93048315f5fc5d6ac6428/pywavelets-1.8.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa0607a9c085b8285bc0d04e33d461a6c80f8c325389221ffb1a45141861138e", size = 4454604 }, - { url = "https://files.pythonhosted.org/packages/c3/23/9ce38829f57159e812c469c4f9d7b5a16c1ba922c1802985e8c504468206/pywavelets-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d31c36a39110e8fcc7b1a4a11cfed7d22b610c285d3e7f4fe73ec777aa49fa39", size = 4445507 }, - { url = "https://files.pythonhosted.org/packages/e5/d2/e78a976b0600a6ef7a70f4430122d6ad11b3e1cbda3c8b3565661d094678/pywavelets-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa7c68ed1e5bab23b1bafe60ccbcf709b878652d03de59e961baefa5210fcd0a", size = 4479078 }, - { url = "https://files.pythonhosted.org/packages/85/4d/1c4f870010368f3aeb0bdd72929376a1988e4a122e76545bd8c56e549c96/pywavelets-1.8.0-cp313-cp313-win32.whl", hash = "sha256:2c6b359b55d713ef683e9da1529181b865a80d759881ceb9adc1c5742e4da4d8", size = 4133763 }, - { url = "https://files.pythonhosted.org/packages/c9/4f/0a709a5732e6cf9297fc87bf545cb879997cde204115f8c0cbc296c5bdd3/pywavelets-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:4dbebcfd55ea8a85b7fc8802d411e75337170422abf6e96019d7e46c394e80e5", size = 4209548 }, - { url = "https://files.pythonhosted.org/packages/de/2a/4cac0bba67d3bc0ad697d0680539864db0a6964c7ad953d8d9d887f360b3/pywavelets-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:2e1c79784bebeafd3715c1bea6621daa2e2e6ed37b687719322e2078fb35bb70", size = 4335183 }, - { url = "https://files.pythonhosted.org/packages/58/d1/3abe4cf34a35b09ad847f0e9a85f340c1988611222926d295fa8710659e7/pywavelets-1.8.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f489380c95013cc8fb3ef338f6d8c1a907125db453cc4dc739e2cca06fcd8b6", size = 4454723 }, - { url = "https://files.pythonhosted.org/packages/d5/62/f05dd191232ae94e0b48509bb0ee65c9d45abf5e8f3612b09fd309b41384/pywavelets-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:06786201a91b5e74540f4f3c115c49a29190de2eb424823abbd3a1fd75ea3e28", size = 4472192 }, - { url = "https://files.pythonhosted.org/packages/20/6a/257c95ad1e0fd395cbccd4ffec0d01cc9b51a3bb91e67d8fa10ffebc9c72/pywavelets-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:f2877fb7b58c94211257dcf364b204d6ed259146fc87d5a90bf9d93c97af6226", size = 4183968 }, - { url = "https://files.pythonhosted.org/packages/6c/58/7179fd6f87153f2e339171e8cfe9bf901398a89045eefd7a3911bb9b47ad/pywavelets-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ec5d723c3335ff8aa630fd4b14097077f12cc02893c91cafd60dd7b1730e780f", size = 4265431 }, -] - -[[package]] -name = "pywavelets" -version = "1.9.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.11'", -] -dependencies = [ - { name = "numpy", version = "2.3.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5a/75/50581633d199812205ea8cdd0f6d52f12a624886b74bf1486335b67f01ff/pywavelets-1.9.0.tar.gz", hash = "sha256:148d12203377772bea452a59211d98649c8ee4a05eff019a9021853a36babdc8", size = 3938340 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/bd/8b/ca700d0c174c3a4eec1fbb603f04374d1fed84255c2a9f487cfaa749c865/pywavelets-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:54662cce4d56f0d6beaa6ebd34b2960f3aa4a43c83c9098a24729e9dc20a4be2", size = 4323640 }, - { url = "https://files.pythonhosted.org/packages/b5/f3/0fa57b6407ea9c4452b0bc182141256b9481b479ffbfc9d7fdb73afe193b/pywavelets-1.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d8ed4b4d1eab9347e8fe0c5b45008ce5a67225ce5b05766b8b1fa923a5f8b34", size = 4294938 }, - { url = "https://files.pythonhosted.org/packages/ea/95/a998313c8459a57e488ff2b18e24be9e836aedda3aa3a1673197deeaa59a/pywavelets-1.9.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:862be65481fdfecfd84c6b0ca132ba571c12697a082068921bca5b5e039f1371", size = 4472829 }, - { url = "https://files.pythonhosted.org/packages/d8/8c/f316a153f7f89d2753df8a7371d15d0faab87e709fe02715dbc297c79385/pywavelets-1.9.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d76b7fa8fc500b09201d689b4f15bf5887e30ffbe2e1f338eb8470590eb4521a", size = 4524936 }, - { url = "https://files.pythonhosted.org/packages/24/f7/89fdc1caef4b384a341a8e149253e23f36c1702bbb986a26123348624854/pywavelets-1.9.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:aa859d0b686a697c87a47e29319aebe44125f114a4f8c7e444832b921f52de5a", size = 4481475 }, - { url = "https://files.pythonhosted.org/packages/82/53/b733fbfb71853e4a5c430da56e325a763562d65241dd785f0fadb67aed6a/pywavelets-1.9.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:20e97b84a263003e2c7348bcf72beba96edda1a6169f072dc4e4d4ee3a6c7368", size = 4527994 }, - { url = "https://files.pythonhosted.org/packages/ed/15/5f6a6e9fdad8341e42642ed622a5f3033da4ea9d426cc3e574ae418b4726/pywavelets-1.9.0-cp311-cp311-win32.whl", hash = "sha256:f8330cdbfa506000e63e79525716df888998a76414c5cd6ecd9a7e371191fb05", size = 4136109 }, - { url = "https://files.pythonhosted.org/packages/fd/33/62dbb4aea86ec9d79b283127c42cc896f4d4ff265a9aeb1337a7836dd550/pywavelets-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:ed10959a17df294ef55948dcc76367d59ec7b6aad67e38dd4e313d2fe3ad47b2", size = 4228321 }, - { url = "https://files.pythonhosted.org/packages/5c/37/3fda13fb2518fdd306528382d6b18c116ceafefff0a7dccd28f1034f4dd2/pywavelets-1.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30baa0788317d3c938560c83fe4fc43817342d06e6c9662a440f73ba3fb25c9b", size = 4320835 }, - { url = "https://files.pythonhosted.org/packages/36/65/a5549325daafc3eae4b52de076798839eaf529a07218f8fb18cccefe76a1/pywavelets-1.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:df7436a728339696a7aa955c020ae65c85b0d9d2b5ff5b4cf4551f5d4c50f2c7", size = 4290469 }, - { url = "https://files.pythonhosted.org/packages/05/85/901bb756d37dfa56baa26ef4a3577aecfe9c55f50f51366fede322f8c91d/pywavelets-1.9.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:07b26526db2476974581274c43a9c2447c917418c6bd03c8d305ad2a5cd9fac3", size = 4437717 }, - { url = "https://files.pythonhosted.org/packages/0f/34/0f54dd9c288941294898877008bcb5c07012340cc9c5db9cff1bd185d449/pywavelets-1.9.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:573b650805d2f3c981a0e5ae95191c781a722022c37a0f6eba3fa7eae8e0ee17", size = 4483843 }, - { url = "https://files.pythonhosted.org/packages/48/1f/cff6bb4ea64ff508d8cac3fe113c0aa95310a7446d9efa6829027cc2afdf/pywavelets-1.9.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3747ec804492436de6e99a7b6130480e53406d047e87dc7095ab40078a515a23", size = 4442236 }, - { url = "https://files.pythonhosted.org/packages/ce/53/a3846eeefe0fb7ca63ae045f038457aa274989a15af793c1b824138caf98/pywavelets-1.9.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5163665686219c3f43fd5bbfef2391e87146813961dad0f86c62d4aed561f547", size = 4488077 }, - { url = "https://files.pythonhosted.org/packages/f7/98/44852d2fe94455b72dece2db23562145179d63186a1c971125279a1c381f/pywavelets-1.9.0-cp312-cp312-win32.whl", hash = "sha256:80b8ab99f5326a3e724f71f23ba8b0a5b03e333fa79f66e965ea7bed21d42a2f", size = 4134094 }, - { url = "https://files.pythonhosted.org/packages/2c/a7/0d9ee3fe454d606e0f5c8e3aebf99d2ecddbfb681826a29397729538c8f1/pywavelets-1.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:92bfb8a117b8c8d3b72f2757a85395346fcbf37f50598880879ae72bd8e1c4b9", size = 4213900 }, - { url = "https://files.pythonhosted.org/packages/db/a7/dec4e450675d62946ad975f5b4d924437df42d2fae46e91dfddda2de0f5a/pywavelets-1.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:74f8455c143818e4b026fc67b27fd82f38e522701b94b8a6d1aaf3a45fcc1a25", size = 4316201 }, - { url = "https://files.pythonhosted.org/packages/aa/0c/b54b86596c0df68027e48c09210e907e628435003e77048384a2dd6767e3/pywavelets-1.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c50320fe0a4a23ddd8835b3dc9b53b09ee05c7cc6c56b81d0916f04fc1649070", size = 4286838 }, - { url = "https://files.pythonhosted.org/packages/5a/9c/333969c3baad8af2e7999e83addcb7bb1d1fd48e2d812fb27e2e89582cb1/pywavelets-1.9.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6e059265223ed659e5214ab52a84883c88ddf3decbf08d7ec6abb8e4c5ed7be", size = 4430753 }, - { url = "https://files.pythonhosted.org/packages/e5/1b/a24c6ff03b026b826ad7b9267bd63cd34ce026795a0302f8a5403840b8e7/pywavelets-1.9.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ae10ed46c139c7ddb8b1249cfe0989f8ccb610d93f2899507b1b1573a0e424b5", size = 4491315 }, - { url = "https://files.pythonhosted.org/packages/d7/c7/e3fbb502fca3469e51ced4f1e1326364c338be91edc5db5a8ddd26b303fa/pywavelets-1.9.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c8f8b1cc2df012401cb837ee6fa2f59607c7b4fe0ff409d9a4f6906daf40dc86", size = 4437654 }, - { url = "https://files.pythonhosted.org/packages/92/44/c9b25084048d9324881a19b88e0969a4141bcfdc1d218f1b4b680b7af1c1/pywavelets-1.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:db43969c7a8fbb17693ecfd14f21616edc3b29f0e47a49b32fa4127c01312a67", size = 4496435 }, - { url = "https://files.pythonhosted.org/packages/cd/b6/b27ec18c72b1dee3314e297af39c5f8136d43cc130dd93cb6c178ca820e5/pywavelets-1.9.0-cp313-cp313-win32.whl", hash = "sha256:9e7d60819d87dcd6c68a2d1bc1d37deb1f4d96607799ab6a25633ea484dcda41", size = 4132709 }, - { url = "https://files.pythonhosted.org/packages/0a/87/78ef3f9fb36cdb16ee82371d22c3a7c89eeb79ec8c9daef6222060da6c79/pywavelets-1.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:0d70da9d7858c869e24dc254f16a61dc09d8a224cad85a10c393b2eccddeb126", size = 4213377 }, - { url = "https://files.pythonhosted.org/packages/8b/cd/ca0d9db0ff29e3843f6af60c2f5eb588794e05ca8eeb872a595867b1f3f5/pywavelets-1.9.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4dc85f44c38d76a184a1aa2cb038f802c3740428c9bb877525f4be83a223b134", size = 4354336 }, - { url = "https://files.pythonhosted.org/packages/82/d6/70afefcc1139f37d02018a3b1dba3b8fc87601bb7707d9616b7f7a76e269/pywavelets-1.9.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7acf6f950c6deaecd210fbff44421f234a8ca81eb6f4da945228e498361afa9d", size = 4335721 }, - { url = "https://files.pythonhosted.org/packages/cd/3a/713f731b9ed6df0c36269c8fb62be8bb28eb343b9e26b13d6abda37bce38/pywavelets-1.9.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:144d4fc15c98da56654d0dca2d391b812b8d04127b194a37ad4a497f8e887141", size = 4418702 }, - { url = "https://files.pythonhosted.org/packages/44/e8/f801eb4b5f7a316ba20054948c5d6b27b879c77fab2674942e779974bd86/pywavelets-1.9.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1aa3729585408a979d655736f74b995b511c86b9be1544f95d4a3142f8f4b8b5", size = 4470023 }, - { url = "https://files.pythonhosted.org/packages/e9/cc/44b002cb16f2a392f2082308dd470b3f033fa4925d3efa7c46f790ce895a/pywavelets-1.9.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e0e24ad6b8eb399c49606dd1fcdcbf9749ad7f6d638be3fe6f59c1f3098821e2", size = 4426498 }, - { url = "https://files.pythonhosted.org/packages/91/fe/2b70276ede7878c5fe8356ca07574db5da63e222ce39a463e84bfad135e8/pywavelets-1.9.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:3830e6657236b53a3aae20c735cccead942bb97c54bbca9e7d07bae01645fe9c", size = 4477528 }, - { url = "https://files.pythonhosted.org/packages/e7/ed/d58b540c15e36508cfeded7b0d39493e811b0dce18d9d4e6787fb2e89685/pywavelets-1.9.0-cp313-cp313t-win32.whl", hash = "sha256:81bb65facfbd7b50dec50450516e72cdc51376ecfdd46f2e945bb89d39bfb783", size = 4186493 }, - { url = "https://files.pythonhosted.org/packages/84/b2/12a849650d618a86bbe4d8876c7e20a7afe59a8cad6f49c57eca9af26dfa/pywavelets-1.9.0-cp313-cp313t-win_amd64.whl", hash = "sha256:47d52cf35e2afded8cfe1133663f6f67106a3220b77645476ae660ad34922cb4", size = 4274821 }, - { url = "https://files.pythonhosted.org/packages/ba/1f/18c82122547c9eec2232d800b02ada1fbd30ce2136137b5738acca9d653e/pywavelets-1.9.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:53043d2f3f4e55a576f51ac594fe33181e1d096d958e01524db5070eb3825306", size = 4314440 }, - { url = "https://files.pythonhosted.org/packages/eb/e1/1c92ac6b538ef5388caf1a74af61cf6af16ea6d14115bb53357469cb38d6/pywavelets-1.9.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:56bc36b42b1b125fd9cb56e7956b22f8d0f83c1093f49c77fc042135e588c799", size = 4290162 }, - { url = "https://files.pythonhosted.org/packages/96/d3/d856a2cac8069c20144598fa30a43ca40b5df2e633230848a9a942faf04a/pywavelets-1.9.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08076eb9a182ddc6054ac86868fb71df6267c341635036dc63d20bdbacd9ad7e", size = 4437162 }, - { url = "https://files.pythonhosted.org/packages/c9/54/777e0495acd4fb008791e84889be33d6e7fc8af095b441d939390b7d2491/pywavelets-1.9.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4ee1ee7d80f88c64b8ec3b5021dd1e94545cc97f0cd479fb51aa7b10f6def08e", size = 4498169 }, - { url = "https://files.pythonhosted.org/packages/76/68/81b97f4d18491a18fbe17e06e2eee80a591ce445942f7b6f522de07813c5/pywavelets-1.9.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3226b6f62838a6ccd7782cb7449ee5d8b9d61999506c1d9b03b2baf41b01b6fd", size = 4443318 }, - { url = "https://files.pythonhosted.org/packages/92/74/5147f2f0436f7aa131cb1bc13dba32ef5f3862748ae1c7366b4cde380362/pywavelets-1.9.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9fb7f4b11d18e2db6dd8deee7b3ce8343d45f195f3f278c2af6e3724b1b93a24", size = 4503294 }, - { url = "https://files.pythonhosted.org/packages/3d/d4/af998cc71e869919e0ab45471bd43e91d055ac7bc3ce6f56cc792c9b6bc8/pywavelets-1.9.0-cp314-cp314-win32.whl", hash = "sha256:9902d9fc9812588ab2dce359a1307d8e7f002b53a835640e2c9388fe62a82fd4", size = 4144478 }, - { url = "https://files.pythonhosted.org/packages/7d/66/1d071eae5cc3e3ad0e45334462f8ce526a79767ccb759eb851aa5b78a73a/pywavelets-1.9.0-cp314-cp314-win_amd64.whl", hash = "sha256:7e57792bde40e331d6cc65458e5970fd814dba18cfc4e9add9d051e901a7b7c7", size = 4227186 }, - { url = "https://files.pythonhosted.org/packages/bf/1f/da0c03ac99bd9d20409c0acf6417806d4cf333d70621da9f535dd0cf27fa/pywavelets-1.9.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b47c72fb4b76d665c4c598a5b621b505944e5b761bf03df9d169029aafcb652f", size = 4354391 }, - { url = "https://files.pythonhosted.org/packages/95/b6/de9e225d8cc307fbb4fda88aefa79442775d5e27c58ee4d3c8a8580ceba6/pywavelets-1.9.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:969e369899e7eab546ea5d77074e4125082e6f9dad71966499bf5dee3758be55", size = 4335810 }, - { url = "https://files.pythonhosted.org/packages/33/3b/336761359d07cd44a4233ca854704ff2a9e78d285879ccc82d254b9daa57/pywavelets-1.9.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8aeffd4f35036c1fade972a61454de5709a7a8fc9a7d177eefe3ac34d76962e5", size = 4422220 }, - { url = "https://files.pythonhosted.org/packages/98/61/76ccc7ada127f14f65eda40e37407b344fd3713acfca7a94d7f0f67fe57d/pywavelets-1.9.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f63f400fcd4e7007529bd06a5886009760da35cd7e76bb6adb5a5fbee4ffeb8c", size = 4470156 }, - { url = "https://files.pythonhosted.org/packages/e0/de/142ca27ee729cf64113c2560748fcf2bd45b899ff282d6f6f3c0e7f177bb/pywavelets-1.9.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a63bcb6b5759a7eb187aeb5e8cd316b7adab7de1f4b5a0446c9a6bcebdfc22fb", size = 4430167 }, - { url = "https://files.pythonhosted.org/packages/ca/5e/90b39adff710d698c00ba9c3125e2bec99dad7c5f1a3ba37c73a78a6689f/pywavelets-1.9.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9950eb7c8b942e9bfa53d87c7e45a420dcddbd835c4c5f1aca045a3f775c6113", size = 4477378 }, - { url = "https://files.pythonhosted.org/packages/f1/1a/89f5f4ebcb9d34d9b7b2ac0a868c8b6d8c78d699a36f54407a060cea0566/pywavelets-1.9.0-cp314-cp314t-win32.whl", hash = "sha256:097f157e07858a1eb370e0d9c1bd11185acdece5cca10756d6c3c7b35b52771a", size = 4209132 }, - { url = "https://files.pythonhosted.org/packages/68/d2/a8065103f5e2e613b916489e6c85af6402a1ec64f346d1429e2d32cb8d03/pywavelets-1.9.0-cp314-cp314t-win_amd64.whl", hash = "sha256:3b6ff6ba4f625d8c955f68c2c39b0a913776d406ab31ee4057f34ad4019fb33b", size = 4306793 }, -] - -[[package]] -name = "pyyaml" -version = "6.0.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199 }, - { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758 }, - { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463 }, - { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280 }, - { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239 }, - { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802 }, - { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527 }, - { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052 }, - { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774 }, - { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 }, - { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 }, - { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 }, - { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 }, - { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 }, - { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 }, - { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 }, - { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 }, - { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 }, - { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, - { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, - { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, - { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, - { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, - { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, - { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, - { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, - { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, - { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, - { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, - { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, - { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, - { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, - { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, - { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, - { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, - { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, -] - -[[package]] -name = "requests" -version = "2.32.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "certifi" }, - { name = "charset-normalizer" }, - { name = "idna" }, - { name = "urllib3" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, -] - -[[package]] -name = "roman-numerals-py" -version = "3.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/30/76/48fd56d17c5bdbdf65609abbc67288728a98ed4c02919428d4f52d23b24b/roman_numerals_py-3.1.0.tar.gz", hash = "sha256:be4bf804f083a4ce001b5eb7e3c0862479d10f94c936f6c4e5f250aa5ff5bd2d", size = 9017 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/53/97/d2cbbaa10c9b826af0e10fdf836e1bf344d9f0abb873ebc34d1f49642d3f/roman_numerals_py-3.1.0-py3-none-any.whl", hash = "sha256:9da2ad2fb670bcf24e81070ceb3be72f6c11c440d73bd579fbeca1e9f330954c", size = 7742 }, -] - -[[package]] -name = "ruff" -version = "0.14.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ee/34/8218a19b2055b80601e8fd201ec723c74c7fe1ca06d525a43ed07b6d8e85/ruff-0.14.2.tar.gz", hash = "sha256:98da787668f239313d9c902ca7c523fe11b8ec3f39345553a51b25abc4629c96", size = 5539663 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/16/dd/23eb2db5ad9acae7c845700493b72d3ae214dce0b226f27df89216110f2b/ruff-0.14.2-py3-none-linux_armv6l.whl", hash = "sha256:7cbe4e593505bdec5884c2d0a4d791a90301bc23e49a6b1eb642dd85ef9c64f1", size = 12533390 }, - { url = "https://files.pythonhosted.org/packages/5a/8c/5f9acff43ddcf3f85130d0146d0477e28ccecc495f9f684f8f7119b74c0d/ruff-0.14.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:8d54b561729cee92f8d89c316ad7a3f9705533f5903b042399b6ae0ddfc62e11", size = 12887187 }, - { url = "https://files.pythonhosted.org/packages/99/fa/047646491479074029665022e9f3dc6f0515797f40a4b6014ea8474c539d/ruff-0.14.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5c8753dfa44ebb2cde10ce5b4d2ef55a41fb9d9b16732a2c5df64620dbda44a3", size = 11925177 }, - { url = "https://files.pythonhosted.org/packages/15/8b/c44cf7fe6e59ab24a9d939493a11030b503bdc2a16622cede8b7b1df0114/ruff-0.14.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d0bbeffb8d9f4fccf7b5198d566d0bad99a9cb622f1fc3467af96cb8773c9e3", size = 12358285 }, - { url = "https://files.pythonhosted.org/packages/45/01/47701b26254267ef40369aea3acb62a7b23e921c27372d127e0f3af48092/ruff-0.14.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7047f0c5a713a401e43a88d36843d9c83a19c584e63d664474675620aaa634a8", size = 12303832 }, - { url = "https://files.pythonhosted.org/packages/2d/5c/ae7244ca4fbdf2bee9d6405dcd5bc6ae51ee1df66eb7a9884b77b8af856d/ruff-0.14.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bf8d2f9aa1602599217d82e8e0af7fd33e5878c4d98f37906b7c93f46f9a839", size = 13036995 }, - { url = "https://files.pythonhosted.org/packages/27/4c/0860a79ce6fd4c709ac01173f76f929d53f59748d0dcdd662519835dae43/ruff-0.14.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1c505b389e19c57a317cf4b42db824e2fca96ffb3d86766c1c9f8b96d32048a7", size = 14512649 }, - { url = "https://files.pythonhosted.org/packages/7f/7f/d365de998069720a3abfc250ddd876fc4b81a403a766c74ff9bde15b5378/ruff-0.14.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a307fc45ebd887b3f26b36d9326bb70bf69b01561950cdcc6c0bdf7bb8e0f7cc", size = 14088182 }, - { url = "https://files.pythonhosted.org/packages/6c/ea/d8e3e6b209162000a7be1faa41b0a0c16a133010311edc3329753cc6596a/ruff-0.14.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:61ae91a32c853172f832c2f40bd05fd69f491db7289fb85a9b941ebdd549781a", size = 13599516 }, - { url = "https://files.pythonhosted.org/packages/fa/ea/c7810322086db68989fb20a8d5221dd3b79e49e396b01badca07b433ab45/ruff-0.14.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1967e40286f63ee23c615e8e7e98098dedc7301568bd88991f6e544d8ae096", size = 13272690 }, - { url = "https://files.pythonhosted.org/packages/a9/39/10b05acf8c45786ef501d454e00937e1b97964f846bf28883d1f9619928a/ruff-0.14.2-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:2877f02119cdebf52a632d743a2e302dea422bfae152ebe2f193d3285a3a65df", size = 13496497 }, - { url = "https://files.pythonhosted.org/packages/59/a1/1f25f8301e13751c30895092485fada29076e5e14264bdacc37202e85d24/ruff-0.14.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e681c5bc777de5af898decdcb6ba3321d0d466f4cb43c3e7cc2c3b4e7b843a05", size = 12266116 }, - { url = "https://files.pythonhosted.org/packages/5c/fa/0029bfc9ce16ae78164e6923ef392e5f173b793b26cc39aa1d8b366cf9dc/ruff-0.14.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e21be42d72e224736f0c992cdb9959a2fa53c7e943b97ef5d081e13170e3ffc5", size = 12281345 }, - { url = "https://files.pythonhosted.org/packages/a5/ab/ece7baa3c0f29b7683be868c024f0838770c16607bea6852e46b202f1ff6/ruff-0.14.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:b8264016f6f209fac16262882dbebf3f8be1629777cf0f37e7aff071b3e9b92e", size = 12629296 }, - { url = "https://files.pythonhosted.org/packages/a4/7f/638f54b43f3d4e48c6a68062794e5b367ddac778051806b9e235dfb7aa81/ruff-0.14.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5ca36b4cb4db3067a3b24444463ceea5565ea78b95fe9a07ca7cb7fd16948770", size = 13371610 }, - { url = "https://files.pythonhosted.org/packages/8d/35/3654a973ebe5b32e1fd4a08ed2d46755af7267da7ac710d97420d7b8657d/ruff-0.14.2-py3-none-win32.whl", hash = "sha256:41775927d287685e08f48d8eb3f765625ab0b7042cc9377e20e64f4eb0056ee9", size = 12415318 }, - { url = "https://files.pythonhosted.org/packages/71/30/3758bcf9e0b6a4193a6f51abf84254aba00887dfa8c20aba18aa366c5f57/ruff-0.14.2-py3-none-win_amd64.whl", hash = "sha256:0df3424aa5c3c08b34ed8ce099df1021e3adaca6e90229273496b839e5a7e1af", size = 13565279 }, - { url = "https://files.pythonhosted.org/packages/2e/5d/aa883766f8ef9ffbe6aa24f7192fb71632f31a30e77eb39aa2b0dc4290ac/ruff-0.14.2-py3-none-win_arm64.whl", hash = "sha256:ea9d635e83ba21569fbacda7e78afbfeb94911c9434aff06192d9bc23fd5495a", size = 12554956 }, -] - -[[package]] -name = "scipy" -version = "1.15.3" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/2f/4966032c5f8cc7e6a60f1b2e0ad686293b9474b65246b0c642e3ef3badd0/scipy-1.15.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a345928c86d535060c9c2b25e71e87c39ab2f22fc96e9636bd74d1dbf9de448c", size = 38702770 }, - { url = "https://files.pythonhosted.org/packages/a0/6e/0c3bf90fae0e910c274db43304ebe25a6b391327f3f10b5dcc638c090795/scipy-1.15.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:ad3432cb0f9ed87477a8d97f03b763fd1d57709f1bbde3c9369b1dff5503b253", size = 30094511 }, - { url = "https://files.pythonhosted.org/packages/ea/b1/4deb37252311c1acff7f101f6453f0440794f51b6eacb1aad4459a134081/scipy-1.15.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:aef683a9ae6eb00728a542b796f52a5477b78252edede72b8327a886ab63293f", size = 22368151 }, - { url = "https://files.pythonhosted.org/packages/38/7d/f457626e3cd3c29b3a49ca115a304cebb8cc6f31b04678f03b216899d3c6/scipy-1.15.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:1c832e1bd78dea67d5c16f786681b28dd695a8cb1fb90af2e27580d3d0967e92", size = 25121732 }, - { url = "https://files.pythonhosted.org/packages/db/0a/92b1de4a7adc7a15dcf5bddc6e191f6f29ee663b30511ce20467ef9b82e4/scipy-1.15.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:263961f658ce2165bbd7b99fa5135195c3a12d9bef045345016b8b50c315cb82", size = 35547617 }, - { url = "https://files.pythonhosted.org/packages/8e/6d/41991e503e51fc1134502694c5fa7a1671501a17ffa12716a4a9151af3df/scipy-1.15.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2abc762b0811e09a0d3258abee2d98e0c703eee49464ce0069590846f31d40", size = 37662964 }, - { url = "https://files.pythonhosted.org/packages/25/e1/3df8f83cb15f3500478c889be8fb18700813b95e9e087328230b98d547ff/scipy-1.15.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ed7284b21a7a0c8f1b6e5977ac05396c0d008b89e05498c8b7e8f4a1423bba0e", size = 37238749 }, - { url = "https://files.pythonhosted.org/packages/93/3e/b3257cf446f2a3533ed7809757039016b74cd6f38271de91682aa844cfc5/scipy-1.15.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5380741e53df2c566f4d234b100a484b420af85deb39ea35a1cc1be84ff53a5c", size = 40022383 }, - { url = "https://files.pythonhosted.org/packages/d1/84/55bc4881973d3f79b479a5a2e2df61c8c9a04fcb986a213ac9c02cfb659b/scipy-1.15.3-cp310-cp310-win_amd64.whl", hash = "sha256:9d61e97b186a57350f6d6fd72640f9e99d5a4a2b8fbf4b9ee9a841eab327dc13", size = 41259201 }, - { url = "https://files.pythonhosted.org/packages/96/ab/5cc9f80f28f6a7dff646c5756e559823614a42b1939d86dd0ed550470210/scipy-1.15.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:993439ce220d25e3696d1b23b233dd010169b62f6456488567e830654ee37a6b", size = 38714255 }, - { url = "https://files.pythonhosted.org/packages/4a/4a/66ba30abe5ad1a3ad15bfb0b59d22174012e8056ff448cb1644deccbfed2/scipy-1.15.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:34716e281f181a02341ddeaad584205bd2fd3c242063bd3423d61ac259ca7eba", size = 30111035 }, - { url = "https://files.pythonhosted.org/packages/4b/fa/a7e5b95afd80d24313307f03624acc65801846fa75599034f8ceb9e2cbf6/scipy-1.15.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3b0334816afb8b91dab859281b1b9786934392aa3d527cd847e41bb6f45bee65", size = 22384499 }, - { url = "https://files.pythonhosted.org/packages/17/99/f3aaddccf3588bb4aea70ba35328c204cadd89517a1612ecfda5b2dd9d7a/scipy-1.15.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:6db907c7368e3092e24919b5e31c76998b0ce1684d51a90943cb0ed1b4ffd6c1", size = 25152602 }, - { url = "https://files.pythonhosted.org/packages/56/c5/1032cdb565f146109212153339f9cb8b993701e9fe56b1c97699eee12586/scipy-1.15.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:721d6b4ef5dc82ca8968c25b111e307083d7ca9091bc38163fb89243e85e3889", size = 35503415 }, - { url = "https://files.pythonhosted.org/packages/bd/37/89f19c8c05505d0601ed5650156e50eb881ae3918786c8fd7262b4ee66d3/scipy-1.15.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39cb9c62e471b1bb3750066ecc3a3f3052b37751c7c3dfd0fd7e48900ed52982", size = 37652622 }, - { url = "https://files.pythonhosted.org/packages/7e/31/be59513aa9695519b18e1851bb9e487de66f2d31f835201f1b42f5d4d475/scipy-1.15.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:795c46999bae845966368a3c013e0e00947932d68e235702b5c3f6ea799aa8c9", size = 37244796 }, - { url = "https://files.pythonhosted.org/packages/10/c0/4f5f3eeccc235632aab79b27a74a9130c6c35df358129f7ac8b29f562ac7/scipy-1.15.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:18aaacb735ab38b38db42cb01f6b92a2d0d4b6aabefeb07f02849e47f8fb3594", size = 40047684 }, - { url = "https://files.pythonhosted.org/packages/ab/a7/0ddaf514ce8a8714f6ed243a2b391b41dbb65251affe21ee3077ec45ea9a/scipy-1.15.3-cp311-cp311-win_amd64.whl", hash = "sha256:ae48a786a28412d744c62fd7816a4118ef97e5be0bee968ce8f0a2fba7acf3bb", size = 41246504 }, - { url = "https://files.pythonhosted.org/packages/37/4b/683aa044c4162e10ed7a7ea30527f2cbd92e6999c10a8ed8edb253836e9c/scipy-1.15.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac6310fdbfb7aa6612408bd2f07295bcbd3fda00d2d702178434751fe48e019", size = 38766735 }, - { url = "https://files.pythonhosted.org/packages/7b/7e/f30be3d03de07f25dc0ec926d1681fed5c732d759ac8f51079708c79e680/scipy-1.15.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:185cd3d6d05ca4b44a8f1595af87f9c372bb6acf9c808e99aa3e9aa03bd98cf6", size = 30173284 }, - { url = "https://files.pythonhosted.org/packages/07/9c/0ddb0d0abdabe0d181c1793db51f02cd59e4901da6f9f7848e1f96759f0d/scipy-1.15.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:05dc6abcd105e1a29f95eada46d4a3f251743cfd7d3ae8ddb4088047f24ea477", size = 22446958 }, - { url = "https://files.pythonhosted.org/packages/af/43/0bce905a965f36c58ff80d8bea33f1f9351b05fad4beaad4eae34699b7a1/scipy-1.15.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:06efcba926324df1696931a57a176c80848ccd67ce6ad020c810736bfd58eb1c", size = 25242454 }, - { url = "https://files.pythonhosted.org/packages/56/30/a6f08f84ee5b7b28b4c597aca4cbe545535c39fe911845a96414700b64ba/scipy-1.15.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05045d8b9bfd807ee1b9f38761993297b10b245f012b11b13b91ba8945f7e45", size = 35210199 }, - { url = "https://files.pythonhosted.org/packages/0b/1f/03f52c282437a168ee2c7c14a1a0d0781a9a4a8962d84ac05c06b4c5b555/scipy-1.15.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271e3713e645149ea5ea3e97b57fdab61ce61333f97cfae392c28ba786f9bb49", size = 37309455 }, - { url = "https://files.pythonhosted.org/packages/89/b1/fbb53137f42c4bf630b1ffdfc2151a62d1d1b903b249f030d2b1c0280af8/scipy-1.15.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6cfd56fc1a8e53f6e89ba3a7a7251f7396412d655bca2aa5611c8ec9a6784a1e", size = 36885140 }, - { url = "https://files.pythonhosted.org/packages/2e/2e/025e39e339f5090df1ff266d021892694dbb7e63568edcfe43f892fa381d/scipy-1.15.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ff17c0bb1cb32952c09217d8d1eed9b53d1463e5f1dd6052c7857f83127d539", size = 39710549 }, - { url = "https://files.pythonhosted.org/packages/e6/eb/3bf6ea8ab7f1503dca3a10df2e4b9c3f6b3316df07f6c0ded94b281c7101/scipy-1.15.3-cp312-cp312-win_amd64.whl", hash = "sha256:52092bc0472cfd17df49ff17e70624345efece4e1a12b23783a1ac59a1b728ed", size = 40966184 }, - { url = "https://files.pythonhosted.org/packages/73/18/ec27848c9baae6e0d6573eda6e01a602e5649ee72c27c3a8aad673ebecfd/scipy-1.15.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c620736bcc334782e24d173c0fdbb7590a0a436d2fdf39310a8902505008759", size = 38728256 }, - { url = "https://files.pythonhosted.org/packages/74/cd/1aef2184948728b4b6e21267d53b3339762c285a46a274ebb7863c9e4742/scipy-1.15.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:7e11270a000969409d37ed399585ee530b9ef6aa99d50c019de4cb01e8e54e62", size = 30109540 }, - { url = "https://files.pythonhosted.org/packages/5b/d8/59e452c0a255ec352bd0a833537a3bc1bfb679944c4938ab375b0a6b3a3e/scipy-1.15.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8c9ed3ba2c8a2ce098163a9bdb26f891746d02136995df25227a20e71c396ebb", size = 22383115 }, - { url = "https://files.pythonhosted.org/packages/08/f5/456f56bbbfccf696263b47095291040655e3cbaf05d063bdc7c7517f32ac/scipy-1.15.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0bdd905264c0c9cfa74a4772cdb2070171790381a5c4d312c973382fc6eaf730", size = 25163884 }, - { url = "https://files.pythonhosted.org/packages/a2/66/a9618b6a435a0f0c0b8a6d0a2efb32d4ec5a85f023c2b79d39512040355b/scipy-1.15.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79167bba085c31f38603e11a267d862957cbb3ce018d8b38f79ac043bc92d825", size = 35174018 }, - { url = "https://files.pythonhosted.org/packages/b5/09/c5b6734a50ad4882432b6bb7c02baf757f5b2f256041da5df242e2d7e6b6/scipy-1.15.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9deabd6d547aee2c9a81dee6cc96c6d7e9a9b1953f74850c179f91fdc729cb7", size = 37269716 }, - { url = "https://files.pythonhosted.org/packages/77/0a/eac00ff741f23bcabd352731ed9b8995a0a60ef57f5fd788d611d43d69a1/scipy-1.15.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dde4fc32993071ac0c7dd2d82569e544f0bdaff66269cb475e0f369adad13f11", size = 36872342 }, - { url = "https://files.pythonhosted.org/packages/fe/54/4379be86dd74b6ad81551689107360d9a3e18f24d20767a2d5b9253a3f0a/scipy-1.15.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f77f853d584e72e874d87357ad70f44b437331507d1c311457bed8ed2b956126", size = 39670869 }, - { url = "https://files.pythonhosted.org/packages/87/2e/892ad2862ba54f084ffe8cc4a22667eaf9c2bcec6d2bff1d15713c6c0703/scipy-1.15.3-cp313-cp313-win_amd64.whl", hash = "sha256:b90ab29d0c37ec9bf55424c064312930ca5f4bde15ee8619ee44e69319aab163", size = 40988851 }, - { url = "https://files.pythonhosted.org/packages/1b/e9/7a879c137f7e55b30d75d90ce3eb468197646bc7b443ac036ae3fe109055/scipy-1.15.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3ac07623267feb3ae308487c260ac684b32ea35fd81e12845039952f558047b8", size = 38863011 }, - { url = "https://files.pythonhosted.org/packages/51/d1/226a806bbd69f62ce5ef5f3ffadc35286e9fbc802f606a07eb83bf2359de/scipy-1.15.3-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6487aa99c2a3d509a5227d9a5e889ff05830a06b2ce08ec30df6d79db5fcd5c5", size = 30266407 }, - { url = "https://files.pythonhosted.org/packages/e5/9b/f32d1d6093ab9eeabbd839b0f7619c62e46cc4b7b6dbf05b6e615bbd4400/scipy-1.15.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:50f9e62461c95d933d5c5ef4a1f2ebf9a2b4e83b0db374cb3f1de104d935922e", size = 22540030 }, - { url = "https://files.pythonhosted.org/packages/e7/29/c278f699b095c1a884f29fda126340fcc201461ee8bfea5c8bdb1c7c958b/scipy-1.15.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:14ed70039d182f411ffc74789a16df3835e05dc469b898233a245cdfd7f162cb", size = 25218709 }, - { url = "https://files.pythonhosted.org/packages/24/18/9e5374b617aba742a990581373cd6b68a2945d65cc588482749ef2e64467/scipy-1.15.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a769105537aa07a69468a0eefcd121be52006db61cdd8cac8a0e68980bbb723", size = 34809045 }, - { url = "https://files.pythonhosted.org/packages/e1/fe/9c4361e7ba2927074360856db6135ef4904d505e9b3afbbcb073c4008328/scipy-1.15.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db984639887e3dffb3928d118145ffe40eff2fa40cb241a306ec57c219ebbbb", size = 36703062 }, - { url = "https://files.pythonhosted.org/packages/b7/8e/038ccfe29d272b30086b25a4960f757f97122cb2ec42e62b460d02fe98e9/scipy-1.15.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:40e54d5c7e7ebf1aa596c374c49fa3135f04648a0caabcb66c52884b943f02b4", size = 36393132 }, - { url = "https://files.pythonhosted.org/packages/10/7e/5c12285452970be5bdbe8352c619250b97ebf7917d7a9a9e96b8a8140f17/scipy-1.15.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5e721fed53187e71d0ccf382b6bf977644c533e506c4d33c3fb24de89f5c3ed5", size = 38979503 }, - { url = "https://files.pythonhosted.org/packages/81/06/0a5e5349474e1cbc5757975b21bd4fad0e72ebf138c5592f191646154e06/scipy-1.15.3-cp313-cp313t-win_amd64.whl", hash = "sha256:76ad1fb5f8752eabf0fa02e4cc0336b4e8f021e2d5f061ed37d6d264db35e3ca", size = 40308097 }, -] - -[[package]] -name = "scipy" -version = "1.16.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.11'", -] -dependencies = [ - { name = "numpy", version = "2.3.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/4c/3b/546a6f0bfe791bbb7f8d591613454d15097e53f906308ec6f7c1ce588e8e/scipy-1.16.2.tar.gz", hash = "sha256:af029b153d243a80afb6eabe40b0a07f8e35c9adc269c019f364ad747f826a6b", size = 30580599 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0b/ef/37ed4b213d64b48422df92560af7300e10fe30b5d665dd79932baebee0c6/scipy-1.16.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:6ab88ea43a57da1af33292ebd04b417e8e2eaf9d5aa05700be8d6e1b6501cd92", size = 36619956 }, - { url = "https://files.pythonhosted.org/packages/85/ab/5c2eba89b9416961a982346a4d6a647d78c91ec96ab94ed522b3b6baf444/scipy-1.16.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c95e96c7305c96ede73a7389f46ccd6c659c4da5ef1b2789466baeaed3622b6e", size = 28931117 }, - { url = "https://files.pythonhosted.org/packages/80/d1/eed51ab64d227fe60229a2d57fb60ca5898cfa50ba27d4f573e9e5f0b430/scipy-1.16.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:87eb178db04ece7c698220d523c170125dbffebb7af0345e66c3554f6f60c173", size = 20921997 }, - { url = "https://files.pythonhosted.org/packages/be/7c/33ea3e23bbadde96726edba6bf9111fb1969d14d9d477ffa202c67bec9da/scipy-1.16.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:4e409eac067dcee96a57fbcf424c13f428037827ec7ee3cb671ff525ca4fc34d", size = 23523374 }, - { url = "https://files.pythonhosted.org/packages/96/0b/7399dc96e1e3f9a05e258c98d716196a34f528eef2ec55aad651ed136d03/scipy-1.16.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e574be127bb760f0dad24ff6e217c80213d153058372362ccb9555a10fc5e8d2", size = 33583702 }, - { url = "https://files.pythonhosted.org/packages/1a/bc/a5c75095089b96ea72c1bd37a4497c24b581ec73db4ef58ebee142ad2d14/scipy-1.16.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f5db5ba6188d698ba7abab982ad6973265b74bb40a1efe1821b58c87f73892b9", size = 35883427 }, - { url = "https://files.pythonhosted.org/packages/ab/66/e25705ca3d2b87b97fe0a278a24b7f477b4023a926847935a1a71488a6a6/scipy-1.16.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec6e74c4e884104ae006d34110677bfe0098203a3fec2f3faf349f4cb05165e3", size = 36212940 }, - { url = "https://files.pythonhosted.org/packages/d6/fd/0bb911585e12f3abdd603d721d83fc1c7492835e1401a0e6d498d7822b4b/scipy-1.16.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:912f46667d2d3834bc3d57361f854226475f695eb08c08a904aadb1c936b6a88", size = 38865092 }, - { url = "https://files.pythonhosted.org/packages/d6/73/c449a7d56ba6e6f874183759f8483cde21f900a8be117d67ffbb670c2958/scipy-1.16.2-cp311-cp311-win_amd64.whl", hash = "sha256:91e9e8a37befa5a69e9cacbe0bcb79ae5afb4a0b130fd6db6ee6cc0d491695fa", size = 38687626 }, - { url = "https://files.pythonhosted.org/packages/68/72/02f37316adf95307f5d9e579023c6899f89ff3a051fa079dbd6faafc48e5/scipy-1.16.2-cp311-cp311-win_arm64.whl", hash = "sha256:f3bf75a6dcecab62afde4d1f973f1692be013110cad5338007927db8da73249c", size = 25503506 }, - { url = "https://files.pythonhosted.org/packages/b7/8d/6396e00db1282279a4ddd507c5f5e11f606812b608ee58517ce8abbf883f/scipy-1.16.2-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:89d6c100fa5c48472047632e06f0876b3c4931aac1f4291afc81a3644316bb0d", size = 36646259 }, - { url = "https://files.pythonhosted.org/packages/3b/93/ea9edd7e193fceb8eef149804491890bde73fb169c896b61aa3e2d1e4e77/scipy-1.16.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:ca748936cd579d3f01928b30a17dc474550b01272d8046e3e1ee593f23620371", size = 28888976 }, - { url = "https://files.pythonhosted.org/packages/91/4d/281fddc3d80fd738ba86fd3aed9202331180b01e2c78eaae0642f22f7e83/scipy-1.16.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:fac4f8ce2ddb40e2e3d0f7ec36d2a1e7f92559a2471e59aec37bd8d9de01fec0", size = 20879905 }, - { url = "https://files.pythonhosted.org/packages/69/40/b33b74c84606fd301b2915f0062e45733c6ff5708d121dd0deaa8871e2d0/scipy-1.16.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:033570f1dcefd79547a88e18bccacff025c8c647a330381064f561d43b821232", size = 23553066 }, - { url = "https://files.pythonhosted.org/packages/55/a7/22c739e2f21a42cc8f16bc76b47cff4ed54fbe0962832c589591c2abec34/scipy-1.16.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ea3421209bf00c8a5ef2227de496601087d8f638a2363ee09af059bd70976dc1", size = 33336407 }, - { url = "https://files.pythonhosted.org/packages/53/11/a0160990b82999b45874dc60c0c183d3a3a969a563fffc476d5a9995c407/scipy-1.16.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f66bd07ba6f84cd4a380b41d1bf3c59ea488b590a2ff96744845163309ee8e2f", size = 35673281 }, - { url = "https://files.pythonhosted.org/packages/96/53/7ef48a4cfcf243c3d0f1643f5887c81f29fdf76911c4e49331828e19fc0a/scipy-1.16.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e9feab931bd2aea4a23388c962df6468af3d808ddf2d40f94a81c5dc38f32ef", size = 36004222 }, - { url = "https://files.pythonhosted.org/packages/49/7f/71a69e0afd460049d41c65c630c919c537815277dfea214031005f474d78/scipy-1.16.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:03dfc75e52f72cf23ec2ced468645321407faad8f0fe7b1f5b49264adbc29cb1", size = 38664586 }, - { url = "https://files.pythonhosted.org/packages/34/95/20e02ca66fb495a95fba0642fd48e0c390d0ece9b9b14c6e931a60a12dea/scipy-1.16.2-cp312-cp312-win_amd64.whl", hash = "sha256:0ce54e07bbb394b417457409a64fd015be623f36e330ac49306433ffe04bc97e", size = 38550641 }, - { url = "https://files.pythonhosted.org/packages/92/ad/13646b9beb0a95528ca46d52b7babafbe115017814a611f2065ee4e61d20/scipy-1.16.2-cp312-cp312-win_arm64.whl", hash = "sha256:2a8ffaa4ac0df81a0b94577b18ee079f13fecdb924df3328fc44a7dc5ac46851", size = 25456070 }, - { url = "https://files.pythonhosted.org/packages/c1/27/c5b52f1ee81727a9fc457f5ac1e9bf3d6eab311805ea615c83c27ba06400/scipy-1.16.2-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:84f7bf944b43e20b8a894f5fe593976926744f6c185bacfcbdfbb62736b5cc70", size = 36604856 }, - { url = "https://files.pythonhosted.org/packages/32/a9/15c20d08e950b540184caa8ced675ba1128accb0e09c653780ba023a4110/scipy-1.16.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:5c39026d12edc826a1ef2ad35ad1e6d7f087f934bb868fc43fa3049c8b8508f9", size = 28864626 }, - { url = "https://files.pythonhosted.org/packages/4c/fc/ea36098df653cca26062a627c1a94b0de659e97127c8491e18713ca0e3b9/scipy-1.16.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e52729ffd45b68777c5319560014d6fd251294200625d9d70fd8626516fc49f5", size = 20855689 }, - { url = "https://files.pythonhosted.org/packages/dc/6f/d0b53be55727f3e6d7c72687ec18ea6d0047cf95f1f77488b99a2bafaee1/scipy-1.16.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:024dd4a118cccec09ca3209b7e8e614931a6ffb804b2a601839499cb88bdf925", size = 23512151 }, - { url = "https://files.pythonhosted.org/packages/11/85/bf7dab56e5c4b1d3d8eef92ca8ede788418ad38a7dc3ff50262f00808760/scipy-1.16.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7a5dc7ee9c33019973a470556081b0fd3c9f4c44019191039f9769183141a4d9", size = 33329824 }, - { url = "https://files.pythonhosted.org/packages/da/6a/1a927b14ddc7714111ea51f4e568203b2bb6ed59bdd036d62127c1a360c8/scipy-1.16.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c2275ff105e508942f99d4e3bc56b6ef5e4b3c0af970386ca56b777608ce95b7", size = 35681881 }, - { url = "https://files.pythonhosted.org/packages/c1/5f/331148ea5780b4fcc7007a4a6a6ee0a0c1507a796365cc642d4d226e1c3a/scipy-1.16.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:af80196eaa84f033e48444d2e0786ec47d328ba00c71e4299b602235ffef9acb", size = 36006219 }, - { url = "https://files.pythonhosted.org/packages/46/3a/e991aa9d2aec723b4a8dcfbfc8365edec5d5e5f9f133888067f1cbb7dfc1/scipy-1.16.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9fb1eb735fe3d6ed1f89918224e3385fbf6f9e23757cacc35f9c78d3b712dd6e", size = 38682147 }, - { url = "https://files.pythonhosted.org/packages/a1/57/0f38e396ad19e41b4c5db66130167eef8ee620a49bc7d0512e3bb67e0cab/scipy-1.16.2-cp313-cp313-win_amd64.whl", hash = "sha256:fda714cf45ba43c9d3bae8f2585c777f64e3f89a2e073b668b32ede412d8f52c", size = 38520766 }, - { url = "https://files.pythonhosted.org/packages/1b/a5/85d3e867b6822d331e26c862a91375bb7746a0b458db5effa093d34cdb89/scipy-1.16.2-cp313-cp313-win_arm64.whl", hash = "sha256:2f5350da923ccfd0b00e07c3e5cfb316c1c0d6c1d864c07a72d092e9f20db104", size = 25451169 }, - { url = "https://files.pythonhosted.org/packages/09/d9/60679189bcebda55992d1a45498de6d080dcaf21ce0c8f24f888117e0c2d/scipy-1.16.2-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:53d8d2ee29b925344c13bda64ab51785f016b1b9617849dac10897f0701b20c1", size = 37012682 }, - { url = "https://files.pythonhosted.org/packages/83/be/a99d13ee4d3b7887a96f8c71361b9659ba4ef34da0338f14891e102a127f/scipy-1.16.2-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:9e05e33657efb4c6a9d23bd8300101536abd99c85cca82da0bffff8d8764d08a", size = 29389926 }, - { url = "https://files.pythonhosted.org/packages/bf/0a/130164a4881cec6ca8c00faf3b57926f28ed429cd6001a673f83c7c2a579/scipy-1.16.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:7fe65b36036357003b3ef9d37547abeefaa353b237e989c21027b8ed62b12d4f", size = 21381152 }, - { url = "https://files.pythonhosted.org/packages/47/a6/503ffb0310ae77fba874e10cddfc4a1280bdcca1d13c3751b8c3c2996cf8/scipy-1.16.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6406d2ac6d40b861cccf57f49592f9779071655e9f75cd4f977fa0bdd09cb2e4", size = 23914410 }, - { url = "https://files.pythonhosted.org/packages/fa/c7/1147774bcea50d00c02600aadaa919facbd8537997a62496270133536ed6/scipy-1.16.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ff4dc42bd321991fbf611c23fc35912d690f731c9914bf3af8f417e64aca0f21", size = 33481880 }, - { url = "https://files.pythonhosted.org/packages/6a/74/99d5415e4c3e46b2586f30cdbecb95e101c7192628a484a40dd0d163811a/scipy-1.16.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:654324826654d4d9133e10675325708fb954bc84dae6e9ad0a52e75c6b1a01d7", size = 35791425 }, - { url = "https://files.pythonhosted.org/packages/1b/ee/a6559de7c1cc710e938c0355d9d4fbcd732dac4d0d131959d1f3b63eb29c/scipy-1.16.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:63870a84cd15c44e65220eaed2dac0e8f8b26bbb991456a033c1d9abfe8a94f8", size = 36178622 }, - { url = "https://files.pythonhosted.org/packages/4e/7b/f127a5795d5ba8ece4e0dce7d4a9fb7cb9e4f4757137757d7a69ab7d4f1a/scipy-1.16.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:fa01f0f6a3050fa6a9771a95d5faccc8e2f5a92b4a2e5440a0fa7264a2398472", size = 38783985 }, - { url = "https://files.pythonhosted.org/packages/3e/9f/bc81c1d1e033951eb5912cd3750cc005943afa3e65a725d2443a3b3c4347/scipy-1.16.2-cp313-cp313t-win_amd64.whl", hash = "sha256:116296e89fba96f76353a8579820c2512f6e55835d3fad7780fece04367de351", size = 38631367 }, - { url = "https://files.pythonhosted.org/packages/d6/5e/2cc7555fd81d01814271412a1d59a289d25f8b63208a0a16c21069d55d3e/scipy-1.16.2-cp313-cp313t-win_arm64.whl", hash = "sha256:98e22834650be81d42982360382b43b17f7ba95e0e6993e2a4f5b9ad9283a94d", size = 25787992 }, - { url = "https://files.pythonhosted.org/packages/8b/ac/ad8951250516db71619f0bd3b2eb2448db04b720a003dd98619b78b692c0/scipy-1.16.2-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:567e77755019bb7461513c87f02bb73fb65b11f049aaaa8ca17cfaa5a5c45d77", size = 36595109 }, - { url = "https://files.pythonhosted.org/packages/ff/f6/5779049ed119c5b503b0f3dc6d6f3f68eefc3a9190d4ad4c276f854f051b/scipy-1.16.2-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:17d9bb346194e8967296621208fcdfd39b55498ef7d2f376884d5ac47cec1a70", size = 28859110 }, - { url = "https://files.pythonhosted.org/packages/82/09/9986e410ae38bf0a0c737ff8189ac81a93b8e42349aac009891c054403d7/scipy-1.16.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:0a17541827a9b78b777d33b623a6dcfe2ef4a25806204d08ead0768f4e529a88", size = 20850110 }, - { url = "https://files.pythonhosted.org/packages/0d/ad/485cdef2d9215e2a7df6d61b81d2ac073dfacf6ae24b9ae87274c4e936ae/scipy-1.16.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:d7d4c6ba016ffc0f9568d012f5f1eb77ddd99412aea121e6fa8b4c3b7cbad91f", size = 23497014 }, - { url = "https://files.pythonhosted.org/packages/a7/74/f6a852e5d581122b8f0f831f1d1e32fb8987776ed3658e95c377d308ed86/scipy-1.16.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9702c4c023227785c779cba2e1d6f7635dbb5b2e0936cdd3a4ecb98d78fd41eb", size = 33401155 }, - { url = "https://files.pythonhosted.org/packages/d9/f5/61d243bbc7c6e5e4e13dde9887e84a5cbe9e0f75fd09843044af1590844e/scipy-1.16.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d1cdf0ac28948d225decdefcc45ad7dd91716c29ab56ef32f8e0d50657dffcc7", size = 35691174 }, - { url = "https://files.pythonhosted.org/packages/03/99/59933956331f8cc57e406cdb7a483906c74706b156998f322913e789c7e1/scipy-1.16.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:70327d6aa572a17c2941cdfb20673f82e536e91850a2e4cb0c5b858b690e1548", size = 36070752 }, - { url = "https://files.pythonhosted.org/packages/c6/7d/00f825cfb47ee19ef74ecf01244b43e95eae74e7e0ff796026ea7cd98456/scipy-1.16.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5221c0b2a4b58aa7c4ed0387d360fd90ee9086d383bb34d9f2789fafddc8a936", size = 38701010 }, - { url = "https://files.pythonhosted.org/packages/e4/9f/b62587029980378304ba5a8563d376c96f40b1e133daacee76efdcae32de/scipy-1.16.2-cp314-cp314-win_amd64.whl", hash = "sha256:f5a85d7b2b708025af08f060a496dd261055b617d776fc05a1a1cc69e09fe9ff", size = 39360061 }, - { url = "https://files.pythonhosted.org/packages/82/04/7a2f1609921352c7fbee0815811b5050582f67f19983096c4769867ca45f/scipy-1.16.2-cp314-cp314-win_arm64.whl", hash = "sha256:2cc73a33305b4b24556957d5857d6253ce1e2dcd67fa0ff46d87d1670b3e1e1d", size = 26126914 }, - { url = "https://files.pythonhosted.org/packages/51/b9/60929ce350c16b221928725d2d1d7f86cf96b8bc07415547057d1196dc92/scipy-1.16.2-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:9ea2a3fed83065d77367775d689401a703d0f697420719ee10c0780bcab594d8", size = 37013193 }, - { url = "https://files.pythonhosted.org/packages/2a/41/ed80e67782d4bc5fc85a966bc356c601afddd175856ba7c7bb6d9490607e/scipy-1.16.2-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:7280d926f11ca945c3ef92ba960fa924e1465f8d07ce3a9923080363390624c4", size = 29390172 }, - { url = "https://files.pythonhosted.org/packages/c4/a3/2f673ace4090452696ccded5f5f8efffb353b8f3628f823a110e0170b605/scipy-1.16.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:8afae1756f6a1fe04636407ef7dbece33d826a5d462b74f3d0eb82deabefd831", size = 21381326 }, - { url = "https://files.pythonhosted.org/packages/42/bf/59df61c5d51395066c35836b78136accf506197617c8662e60ea209881e1/scipy-1.16.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:5c66511f29aa8d233388e7416a3f20d5cae7a2744d5cee2ecd38c081f4e861b3", size = 23915036 }, - { url = "https://files.pythonhosted.org/packages/91/c3/edc7b300dc16847ad3672f1a6f3f7c5d13522b21b84b81c265f4f2760d4a/scipy-1.16.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:efe6305aeaa0e96b0ccca5ff647a43737d9a092064a3894e46c414db84bc54ac", size = 33484341 }, - { url = "https://files.pythonhosted.org/packages/26/c7/24d1524e72f06ff141e8d04b833c20db3021020563272ccb1b83860082a9/scipy-1.16.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f3a337d9ae06a1e8d655ee9d8ecb835ea5ddcdcbd8d23012afa055ab014f374", size = 35790840 }, - { url = "https://files.pythonhosted.org/packages/aa/b7/5aaad984eeedd56858dc33d75efa59e8ce798d918e1033ef62d2708f2c3d/scipy-1.16.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bab3605795d269067d8ce78a910220262711b753de8913d3deeaedb5dded3bb6", size = 36174716 }, - { url = "https://files.pythonhosted.org/packages/fd/c2/e276a237acb09824822b0ada11b028ed4067fdc367a946730979feacb870/scipy-1.16.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b0348d8ddb55be2a844c518cd8cc8deeeb8aeba707cf834db5758fc89b476a2c", size = 38790088 }, - { url = "https://files.pythonhosted.org/packages/c6/b4/5c18a766e8353015439f3780f5fc473f36f9762edc1a2e45da3ff5a31b21/scipy-1.16.2-cp314-cp314t-win_amd64.whl", hash = "sha256:26284797e38b8a75e14ea6631d29bda11e76ceaa6ddb6fdebbfe4c4d90faf2f9", size = 39457455 }, - { url = "https://files.pythonhosted.org/packages/97/30/2f9a5243008f76dfc5dee9a53dfb939d9b31e16ce4bd4f2e628bfc5d89d2/scipy-1.16.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d2a4472c231328d4de38d5f1f68fdd6d28a615138f842580a8a321b5845cf779", size = 26448374 }, -] - -[[package]] -name = "six" -version = "1.16.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053 }, -] - -[[package]] -name = "snowballstemmer" -version = "2.2.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/44/7b/af302bebf22c749c56c9c3e8ae13190b5b5db37a33d9068652e8f73b7089/snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1", size = 86699 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ed/dc/c02e01294f7265e63a7315fe086dd1df7dacb9f840a804da846b96d01b96/snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a", size = 93002 }, -] - -[[package]] -name = "soupsieve" -version = "2.7" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3f/f4/4a80cd6ef364b2e8b65b15816a843c0980f7a5a2b4dc701fc574952aa19f/soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a", size = 103418 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/9c/0e6afc12c269578be5c0c1c9f4b49a8d32770a080260c333ac04cc1c832d/soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4", size = 36677 }, -] - -[[package]] -name = "sparse" -version = "0.17.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numba" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/63/74/5c674277fc3d61bd1863d233a8e1f7ddf35cb1adeeaf9973888629e7a9b1/sparse-0.17.0.tar.gz", hash = "sha256:6b1ad51a810c5be40b6f95e28513ec810fe1c785923bd83b2e4839a751df4bf7", size = 642387 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/fd/42a1720542199ae6ff0f9c37bbd55dd3033ddd7bbe00d68cde09d6824887/sparse-0.17.0-py2.py3-none-any.whl", hash = "sha256:1922d1d97f692b1061c4f03a1dd6ee21850aedc88e171aa845715f5069952f18", size = 259370 }, -] - -[[package]] -name = "sphinx" -version = "8.1.3" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -dependencies = [ - { name = "alabaster", marker = "python_full_version < '3.11'" }, - { name = "babel", marker = "python_full_version < '3.11'" }, - { name = "colorama", marker = "python_full_version < '3.11' and sys_platform == 'win32'" }, - { name = "docutils", marker = "python_full_version < '3.11'" }, - { name = "imagesize", marker = "python_full_version < '3.11'" }, - { name = "jinja2", marker = "python_full_version < '3.11'" }, - { name = "packaging", marker = "python_full_version < '3.11'" }, - { name = "pygments", marker = "python_full_version < '3.11'" }, - { name = "requests", marker = "python_full_version < '3.11'" }, - { name = "snowballstemmer", marker = "python_full_version < '3.11'" }, - { name = "sphinxcontrib-applehelp", marker = "python_full_version < '3.11'" }, - { name = "sphinxcontrib-devhelp", marker = "python_full_version < '3.11'" }, - { name = "sphinxcontrib-htmlhelp", marker = "python_full_version < '3.11'" }, - { name = "sphinxcontrib-jsmath", marker = "python_full_version < '3.11'" }, - { name = "sphinxcontrib-qthelp", marker = "python_full_version < '3.11'" }, - { name = "sphinxcontrib-serializinghtml", marker = "python_full_version < '3.11'" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/26/60/1ddff83a56d33aaf6f10ec8ce84b4c007d9368b21008876fceda7e7381ef/sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2", size = 3487125 }, -] - -[[package]] -name = "sphinx" -version = "8.2.3" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.11'", -] -dependencies = [ - { name = "alabaster", marker = "python_full_version >= '3.11'" }, - { name = "babel", marker = "python_full_version >= '3.11'" }, - { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" }, - { name = "docutils", marker = "python_full_version >= '3.11'" }, - { name = "imagesize", marker = "python_full_version >= '3.11'" }, - { name = "jinja2", marker = "python_full_version >= '3.11'" }, - { name = "packaging", marker = "python_full_version >= '3.11'" }, - { name = "pygments", marker = "python_full_version >= '3.11'" }, - { name = "requests", marker = "python_full_version >= '3.11'" }, - { name = "roman-numerals-py", marker = "python_full_version >= '3.11'" }, - { name = "snowballstemmer", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/38/ad/4360e50ed56cb483667b8e6dadf2d3fda62359593faabbe749a27c4eaca6/sphinx-8.2.3.tar.gz", hash = "sha256:398ad29dee7f63a75888314e9424d40f52ce5a6a87ae88e7071e80af296ec348", size = 8321876 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/31/53/136e9eca6e0b9dc0e1962e2c908fbea2e5ac000c2a2fbd9a35797958c48b/sphinx-8.2.3-py3-none-any.whl", hash = "sha256:4405915165f13521d875a8c29c8970800a0141c14cc5416a38feca4ea5d9b9c3", size = 3589741 }, -] - -[[package]] -name = "sphinx-autodoc-typehints" -version = "3.0.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.11'", -] -dependencies = [ - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/26/f0/43c6a5ff3e7b08a8c3b32f81b859f1b518ccc31e45f22e2b41ced38be7b9/sphinx_autodoc_typehints-3.0.1.tar.gz", hash = "sha256:b9b40dd15dee54f6f810c924f863f9cf1c54f9f3265c495140ea01be7f44fa55", size = 36282 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3c/dc/dc46c5c7c566b7ec5e8f860f9c89533bf03c0e6aadc96fb9b337867e4460/sphinx_autodoc_typehints-3.0.1-py3-none-any.whl", hash = "sha256:4b64b676a14b5b79cefb6628a6dc8070e320d4963e8ff640a2f3e9390ae9045a", size = 20245 }, -] - -[[package]] -name = "sphinx-autodoc-typehints" -version = "3.5.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.11'", -] -dependencies = [ - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/34/4f/4fd5583678bb7dc8afa69e9b309e6a99ee8d79ad3a4728f4e52fd7cb37c7/sphinx_autodoc_typehints-3.5.2.tar.gz", hash = "sha256:5fcd4a3eb7aa89424c1e2e32bedca66edc38367569c9169a80f4b3e934171fdb", size = 37839 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/05/f2/9657c98a66973b7c35bfd48ba65d1922860de9598fbb535cd96e3f58a908/sphinx_autodoc_typehints-3.5.2-py3-none-any.whl", hash = "sha256:0accd043619f53c86705958e323b419e41667917045ac9215d7be1b493648d8c", size = 21184 }, -] - -[[package]] -name = "sphinx-copybutton" -version = "0.5.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fc/2b/a964715e7f5295f77509e59309959f4125122d648f86b4fe7d70ca1d882c/sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd", size = 23039 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/48/1ea60e74949eecb12cdd6ac43987f9fd331156388dcc2319b45e2ebb81bf/sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e", size = 13343 }, -] - -[[package]] -name = "sphinx-design" -version = "0.6.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2b/69/b34e0cb5336f09c6866d53b4a19d76c227cdec1bbc7ac4de63ca7d58c9c7/sphinx_design-0.6.1.tar.gz", hash = "sha256:b44eea3719386d04d765c1a8257caca2b3e6f8421d7b3a5e742c0fd45f84e632", size = 2193689 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/43/65c0acbd8cc6f50195a3a1fc195c404988b15c67090e73c7a41a9f57d6bd/sphinx_design-0.6.1-py3-none-any.whl", hash = "sha256:b11f37db1a802a183d61b159d9a202314d4d2fe29c163437001324fe2f19549c", size = 2215338 }, -] - -[[package]] -name = "sphinxcontrib-applehelp" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300 }, -] - -[[package]] -name = "sphinxcontrib-devhelp" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530 }, -] - -[[package]] -name = "sphinxcontrib-htmlhelp" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705 }, -] - -[[package]] -name = "sphinxcontrib-jsmath" -version = "1.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071 }, -] - -[[package]] -name = "sphinxcontrib-qthelp" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743 }, -] - -[[package]] -name = "sphinxcontrib-serializinghtml" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072 }, -] - -[[package]] -name = "sphinxext-rediraffe" -version = "0.2.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/1f/b4/e5fbb493f796430230189a1ce5f9beff1ac1b98619fc71ed35deca6059a5/sphinxext-rediraffe-0.2.7.tar.gz", hash = "sha256:651dcbfae5ffda9ffd534dfb8025f36120e5efb6ea1a33f5420023862b9f725d", size = 8735 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/76/4f/c8797e796199e55cf6c8979ecdf5f4b09b81e93f87b3193c759faea63263/sphinxext_rediraffe-0.2.7-py3-none-any.whl", hash = "sha256:9e430a52d4403847f4ffb3a8dd6dfc34a9fe43525305131f52ed899743a5fd8c", size = 8267 }, -] - -[[package]] -name = "tomli" -version = "2.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c0/3f/d7af728f075fb08564c5949a9c95e44352e23dee646869fa104a3b2060a3/tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f", size = 15164 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", size = 12757 }, -] - -[[package]] -name = "typing-extensions" -version = "4.15.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614 }, -] - -[[package]] -name = "tzdata" -version = "2024.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e1/34/943888654477a574a86a98e9896bae89c7aa15078ec29f490fef2f1e5384/tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc", size = 193282 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a6/ab/7e5f53c3b9d14972843a647d8d7a853969a58aecc7559cb3267302c94774/tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd", size = 346586 }, -] - -[[package]] -name = "urllib3" -version = "2.2.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ed/63/22ba4ebfe7430b76388e7cd448d5478814d3032121827c12a2cc287e2260/urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9", size = 300677 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", size = 126338 }, -] - -[[package]] -name = "virtualenv" -version = "20.35.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "distlib" }, - { name = "filelock" }, - { name = "platformdirs" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/a4/d5/b0ccd381d55c8f45d46f77df6ae59fbc23d19e901e2d523395598e5f4c93/virtualenv-20.35.3.tar.gz", hash = "sha256:4f1a845d131133bdff10590489610c98c168ff99dc75d6c96853801f7f67af44", size = 6002907 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/73/d9a94da0e9d470a543c1b9d3ccbceb0f59455983088e727b8a1824ed90fb/virtualenv-20.35.3-py3-none-any.whl", hash = "sha256:63d106565078d8c8d0b206d48080f938a8b25361e19432d2c9db40d2899c810a", size = 5981061 }, -] - -[[package]] -name = "xarray" -version = "2023.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "numpy", version = "2.3.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "packaging" }, - { name = "pandas" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/30/05/c52545c83de39d5ccb3f0b06d9bb3ebde74ea0e775b7da5f2f8e11ab4879/xarray-2023.1.0.tar.gz", hash = "sha256:7bee552751ff1b29dab8b7715726e5ecb56691ac54593cf4881dff41978ce0cd", size = 3065563 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b4/a7/897f484225bd8e179a4f39f8e9a4ca26c14e9f7055b495384b1d56e1382d/xarray-2023.1.0-py3-none-any.whl", hash = "sha256:7e530b1deafdd43e5c2b577d0944e6b528fbe88045fd849e49a8d11871ecd522", size = 973057 }, -]