From 15f04e12454ad39f070d49d94fbb4ed60aa3b77a Mon Sep 17 00:00:00 2001 From: Harris Jos <104043391+CozySocksAlways@users.noreply.github.com> Date: Wed, 26 Aug 2026 11:22:49 +0200 Subject: [PATCH 1/6] Fix deprecated plt.cm.get_cmap in plot_patterns (#81) * Replaced depricated plt.cm.get_cmap api * Switched to newer API for getting colormap * Apply suggestions from code review Co-authored-by: Michael Denker * Bumbed matplotlib min version to allign with api change --------- Co-authored-by: Michael Denker --- requirements/requirements.txt | 4 +-- viziphant/patterns.py | 3 +- viziphant/test/test_patterns.py | 57 +++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 viziphant/test/test_patterns.py diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 15075df..bb87e1a 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -2,8 +2,8 @@ neo>=0.9.0 elephant>=0.9.0 numpy>=1.19.5 quantities>=0.14.1 -matplotlib>=3.3.2 +matplotlib>=3.6 seaborn>=0.9.0 bokeh>=3.0.0 holoviews>=1.16.0 -networkx>=3.0.0 \ No newline at end of file +networkx>=3.0.0 diff --git a/viziphant/patterns.py b/viziphant/patterns.py index 26a2afc..0ddba68 100644 --- a/viziphant/patterns.py +++ b/viziphant/patterns.py @@ -31,6 +31,7 @@ from collections import defaultdict import matplotlib.pyplot as plt +import matplotlib as mpl import neo import numpy as np import quantities as pq @@ -354,7 +355,7 @@ def plot_patterns(spiketrains, patterns, circle_sizes=(3, 50, 70), if colors is None: # +1 is necessary - cmap = plt.cm.get_cmap("hsv", len(patterns) + 1) + cmap = mpl.colormaps["hsv"].resampled(len(patterns) + 1) colors = np.array([cmap(i) for i in range(len(patterns))]) elif not isinstance(colors, (list, tuple, np.ndarray)): raise TypeError("'colors' must be a list of colors") diff --git a/viziphant/test/test_patterns.py b/viziphant/test/test_patterns.py new file mode 100644 index 0000000..f183565 --- /dev/null +++ b/viziphant/test/test_patterns.py @@ -0,0 +1,57 @@ +""" +Unit tests for the pattern plot functions +:copyright: Copyright 2014-2023 by the Viziphant team, see `doc/authors.rst`. +:license: Modified BSD, see LICENSE.txt for details. +""" + +import neo +import numpy as np +import pytest +import quantities as pq +import viziphant + + +@pytest.fixture +def spiketrains_and_patterns(): + """ + Build minimal inputs for `plot_patterns`: 5 neo.SpikeTrain objects and + 2 pattern dicts (each with 'neurons' and 'times' keys, as returned by + elephant's spade/cell_assembly_detection). + """ + spiketrains = [ + neo.SpikeTrain( + np.linspace(0, 50, num=10), + units="ms", + t_stop=100 * pq.ms, + ) + for _ in range(5) + ] + patterns = [ + {"neurons": [0, 1, 2], "times": [5, 15] * pq.ms}, + {"neurons": [2, 3], "times": [10, 20] * pq.ms}, + ] + return spiketrains, patterns + + +def test_plot_patterns_default_colors(spiketrains_and_patterns): + """ + Regression test for #81 + Change following updates to matplotlib (`plt.cm.get_cmap` deprecation; alternatives are + `plt.get_cmap` or `mpl.colormaps`) + """ + spiketrains, patterns = spiketrains_and_patterns + axes = viziphant.patterns.plot_patterns(spiketrains, patterns) + assert axes is not None + + +def test_plot_patterns_colors_length_mismatch(spiketrains_and_patterns): + """ + Checks if plot_patterns raises value error on color length mismatch + """ + spiketrains, patterns = spiketrains_and_patterns + with pytest.raises( + ValueError, + match=r"The length of \'colors\' must match the length of " + r"the input \'patterns\'\.", + ): + viziphant.patterns.plot_patterns(spiketrains, patterns, colors=["r"]) From e690e298dfb70be9f61ec08a740fd531bcdd5a6e Mon Sep 17 00:00:00 2001 From: Harris Jos <104043391+CozySocksAlways@users.noreply.github.com> Date: Wed, 2 Sep 2026 19:56:29 +0200 Subject: [PATCH 2/6] Migrate packaging from setup.py to pyproject.toml (#82) * Swapped out setup.py with pyproject.toml * Adding a architectural decision record file * Splitup into multiple adrs * upper limit on setuptooks for python 3.8 support; ADR changes * remove newer licence field from pyproject.toml since older setuptools doesn't understand it * Added statement to exclude adrs from packaging * Reinstated licence classifier as per PR review --- MANIFEST.in | 1 + doc/adr/0001-build-backend.md | 13 +++++ doc/adr/0002-build-backend-minimum-version.md | 28 +++++++++++ doc/adr/0003-supported-python-version.md | 10 ++++ pyproject.toml | 47 +++++++++++++++++++ setup.py | 40 ---------------- 6 files changed, 99 insertions(+), 40 deletions(-) create mode 100644 doc/adr/0001-build-backend.md create mode 100644 doc/adr/0002-build-backend-minimum-version.md create mode 100644 doc/adr/0003-supported-python-version.md create mode 100644 pyproject.toml delete mode 100644 setup.py diff --git a/MANIFEST.in b/MANIFEST.in index 6d40b2d..ad21472 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -6,4 +6,5 @@ include viziphant/VERSION recursive-include doc * prune doc/_build prune doc/toctree +prune doc/adr recursive-exclude . *~ diff --git a/doc/adr/0001-build-backend.md b/doc/adr/0001-build-backend.md new file mode 100644 index 0000000..f4313f1 --- /dev/null +++ b/doc/adr/0001-build-backend.md @@ -0,0 +1,13 @@ +# ADR 1. Build Backend +## Context + +## Options + +| Option | Pro | Con | +| ---------- | ----------------------- | ----------------------------------- | +| setuptools | 1. Direct drop-in `setup.py`.
2. Supports dynamic fields (version, readme)
3. Elephant uses it | | +| hatchling | | 1. Needs an extra plugin to do dynamic field for requirement | +| uv_build | 1. "zero config" auto detects stuff
2. Clean and concise | 1. No dynamic dependency support | + +## Decision +Setuptools looks good, maintaining consistency with elephant is given more weightage in this decision diff --git a/doc/adr/0002-build-backend-minimum-version.md b/doc/adr/0002-build-backend-minimum-version.md new file mode 100644 index 0000000..c96e4d0 --- /dev/null +++ b/doc/adr/0002-build-backend-minimum-version.md @@ -0,0 +1,28 @@ +# ADR 2. Build Backend Minimum Version +## Context +- [Quickstart](https://setuptools.pypa.io/en/latest/userguide/quickstart.html#basic-use) on setuptools doesn't specify a minimum version of the setuptools +- [Python Packaging Authority](https://packaging.python.org/en/latest/guides/writing-pyproject-toml/) does mention +- Older build backend version might not support some more modern flags available in toml file + +## Option +| | Option | Pro | Con | +| --- | ----------------------------------------------- | ----------------- | ------------------------- | +| 1 | No minimum bound | | | +| 2 | Use same as PyPackage example | Looks more modern | | +| 3 | Use elephant pyproject.toml setuptools version | | Very old (v61 vs v84 now) | + +## Decision +- Option 2 (i.e. `setuptools >= 77.0`) conflicts with [ADR 3](0003-supported-python-version.md), which keeps + `requires-python = ">=3.8"` for this patch release: setuptools 77.0 requires Python `>=3.9` +- So for patch release (0.4.1) use Option 3 `setuptools >= 69.0, < 75.4`. Remove upper cap once python 3.8 +support removed +- Pinning setuptools below 77.0 also means giving up SPDX style `license` string + + top-level `license-files` as mentioned in PEP 639, which only exists from 77.0 onward. + - Reverted to `license = {file = "LICENSE.txt"}` to match what `setuptools >= 69.0, < 75.4` understands. + - Change back to how elephant has it once upper pin on setuptools is removed + - Since the SPDX `license-expression` isn't available under this cap, kept the + `License :: OSI Approved :: BSD License` classifier as the only machine-readable license + indicator for now. Metadata 2.4 (setuptools >= 77.0) doesn't allow a classifier alongside + `license-expression`, so once the setuptools cap is lifted, switch to + `license = "BSD-3-Clause"` and remove this classifier at the same time. + diff --git a/doc/adr/0003-supported-python-version.md b/doc/adr/0003-supported-python-version.md new file mode 100644 index 0000000..76d8845 --- /dev/null +++ b/doc/adr/0003-supported-python-version.md @@ -0,0 +1,10 @@ +# ADR 3. Supported Python Version + +## Context +- The current minimum supported Python version is `>=3.8`. +release. +- Viziphant is due for a patch release, and a `requires-python` bump would be a semantic versioning break +- Python 3.8 is way beyond it's EOL which was in October 2024. + +## Decision +Keep the exisiting minimum python version cause this looks like an extreme edge case for people running python 3.8, which is past it's EOL, so can safely just keep it as is for now, and change in next minor release diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..e8670f7 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,47 @@ +[build-system] +requires = ["setuptools >= 69.0, < 75.4"] +build-backend = "setuptools.build_meta" + +[project] +name = "viziphant" +dynamic = ["version", "dependencies", "optional-dependencies"] +description = "Viziphant is a package for the visualization of the analysis results of electrophysiology data in Python" +authors = [ + {name = "Viziphant authors and contributors", email = "contact@python-elephant.org"} +] +license = {file = "LICENSE.txt"} +requires-python = ">=3.8" +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Science/Research", + "Natural Language :: English", + "Operating System :: OS Independent", + "License :: OSI Approved :: BSD License", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Scientific/Engineering", +] +keywords = ["neuroscience", "neurophysiology", "electrophysiology", "visualization", "data-analysis"] +readme = {file = "README.md", content-type = "text/markdown"} + +[project.urls] +Repository = "https://github.com/INM-6/viziphant" +Documentation = "https://viziphant.readthedocs.io/en/latest/" +Issues = "https://github.com/INM-6/viziphant/issues" +Changelog = "https://github.com/INM-6/viziphant/releases" + +[tool.setuptools] +packages = ["viziphant"] +include-package-data = true + +[tool.setuptools.dynamic] +version = {file = "viziphant/VERSION"} +dependencies = {file = "requirements/requirements.txt"} + +[tool.setuptools.dynamic.optional-dependencies] +tests = {file = "requirements/requirements-tests.txt"} +docs = {file = "requirements/requirements-docs.txt"} diff --git a/setup.py b/setup.py deleted file mode 100644 index 67be72e..0000000 --- a/setup.py +++ /dev/null @@ -1,40 +0,0 @@ -# -*- coding: utf-8 -*- - -import os - -from setuptools import setup - -with open(os.path.join(os.path.dirname(__file__), - "viziphant", "VERSION")) as version_file: - version = version_file.read().strip() - -with open("README.md") as f: - long_description = f.read() -with open('requirements/requirements.txt') as fp: - install_requires = fp.read() - - -setup( - name="viziphant", - version=version, - packages=['viziphant'], - include_package_data=True, - install_requires=install_requires, - author="Viziphant authors and contributors", - author_email="contact@python-elephant.org", - description="Viziphant is a package for the visualization of the analysis" - "results of electrophysiology data in Python", - long_description=long_description, - long_description_content_type="text/markdown", - license="BSD", - url='https://github.com/INM-6/viziphant', - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Intended Audience :: Science/Research', - 'License :: OSI Approved :: BSD License', - 'Natural Language :: English', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 3', - 'Topic :: Scientific/Engineering'] -) \ No newline at end of file From ecafb74a62474bc9825740d296bdab5956333db4 Mon Sep 17 00:00:00 2001 From: Harris Antony Jos Date: Tue, 25 Aug 2026 11:45:49 +0200 Subject: [PATCH 3/6] refactorted and updated CI workflow file --- .github/workflows/CI_actions.yml | 68 +++++++++----------------------- 1 file changed, 18 insertions(+), 50 deletions(-) diff --git a/.github/workflows/CI_actions.yml b/.github/workflows/CI_actions.yml index dc6335c..b4acc30 100644 --- a/.github/workflows/CI_actions.yml +++ b/.github/workflows/CI_actions.yml @@ -1,66 +1,45 @@ -# This workflow will setup GitHub-hosted runners and install the required dependencies for viziphant documentation. -# On pull requests and on pushes to master it will build the documentation . - +# CI Tests that runs unittests and doc build +# Triggered on pull requests and pushes to master name: tests -# define events that trigger workflow 'Viziphant-tests' on: - # run on pull requests to master branch pull_request: branches: [master] types: [synchronize, opened, reopened] - - # run on pushes to master branch push: branches: [master] -# jobs define the steps that will be executed on the runner jobs: - - # install dependencies for the documentation and build .html docs: - runs-on: ${{ matrix.os }} + runs-on: "ubuntu-latest" + env: + PYTHON_VERSION: "3.10" strategy: - - matrix: - # OS [ubuntu-latest, macos-latest, windows-latest] - os: [ubuntu-latest] - # relevant python versions for viziphant: [3.7, 3.8, 3.9, "3.10", 3.11, 3.12] - python-version: [3.8, 3.9, "3.10", 3.11, 3.12] - - # do not cancel all in-progress jobs if any matrix job fails fail-fast: false steps: - - name: Get current year-month id: date - run: echo "date=$(date +'%Y-%m')" >> $GITHUB_OUTPUT + run: echo "date=$(date +'%Y-%m')" >> "$GITHUB_OUTPUT" - - uses: actions/checkout@v3 + - uses: actions/checkout@v6.0 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + - name: Set up Python ${{ env.PYTHON_VERSION }} + uses: actions/setup-python@v6.2 with: - python-version: ${{ matrix.python-version }} + python-version: ${{ env.PYTHON_VERSION }} - name: Cache pip - uses: actions/cache@v3 + uses: actions/cache@v5.0 with: path: ~/.cache/pip - # Look to see if there is a cache hit for the corresponding requirements files key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements-docs.txt') }}-${{ hashFiles('**/requirements.txt') }}-${{ steps.date.outputs.date }} - - name: Install dependencies run: | + sudo apt install pandoc python -m pip install --upgrade pip - pip install setuptools - pip install -r requirements/requirements-docs.txt - pip install -r requirements/requirements.txt pip install . - conda update conda - conda install -c conda-forge pandoc # run notebooks sed -i -E "s/nbsphinx_execute *=.*/nbsphinx_execute = 'always'/g" doc/conf.py - name: make html @@ -69,50 +48,39 @@ jobs: cd doc make html - # install dependencies and run tests with pytest tests: runs-on: ${{ matrix.os }} strategy: - matrix: - # OS [ubuntu-latest, macos-latest, windows-latest] os: [ubuntu-latest] - # relevant python versions for viziphant: [3.7, 3.8, 3.9, "3.10"] - python-version: [3.9] - - # do not cancel all in-progress jobs if any matrix job fails + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] fail-fast: false steps: - name: Get current year-month id: date - run: echo "date=$(date +'%Y-%m')" >> $GITHUB_OUTPUT + run: echo "date=$(date +'%Y-%m')" >> "$GITHUB_OUTPUT" - - uses: actions/checkout@v3 + - uses: actions/checkout@v6.0 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v6.2 with: python-version: ${{ matrix.python-version }} - name: Cache pip - uses: actions/cache@v3 + uses: actions/cache@v5.0 with: path: ~/.cache/pip - # Look to see if there is a cache hit for the corresponding requirements files key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements-docs.txt') }}-${{ hashFiles('**/requirements.txt') }}-${{ steps.date.outputs.date }} - name: Install dependencies run: | python -m pip install --upgrade pip - pip install setuptools - pip install -r requirements/requirements-docs.txt - pip install -r requirements/requirements-tests.txt - pip install -r requirements/requirements.txt pip install . - name: run tests run: | python --version - pytest viziphant + pytest From f22577ed6539affdd16e2172918686e1431efa73 Mon Sep 17 00:00:00 2001 From: Harris Antony Jos Date: Mon, 7 Sep 2026 16:24:43 +0200 Subject: [PATCH 4/6] Switched to major version only for github actions; major.minor version is not supported --- .github/workflows/CI_actions.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/CI_actions.yml b/.github/workflows/CI_actions.yml index b4acc30..6108e8e 100644 --- a/.github/workflows/CI_actions.yml +++ b/.github/workflows/CI_actions.yml @@ -22,15 +22,15 @@ jobs: id: date run: echo "date=$(date +'%Y-%m')" >> "$GITHUB_OUTPUT" - - uses: actions/checkout@v6.0 + - uses: actions/checkout@v6 - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@v6.2 + uses: actions/setup-python@v6 with: python-version: ${{ env.PYTHON_VERSION }} - name: Cache pip - uses: actions/cache@v5.0 + uses: actions/cache@v5 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements-docs.txt') }}-${{ hashFiles('**/requirements.txt') }}-${{ steps.date.outputs.date }} @@ -62,15 +62,15 @@ jobs: id: date run: echo "date=$(date +'%Y-%m')" >> "$GITHUB_OUTPUT" - - uses: actions/checkout@v6.0 + - uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v6.2 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} - name: Cache pip - uses: actions/cache@v5.0 + uses: actions/cache@v5 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements-docs.txt') }}-${{ hashFiles('**/requirements.txt') }}-${{ steps.date.outputs.date }} From ea3d82bdcf16da325467a0828d26a62c32b37e41 Mon Sep 17 00:00:00 2001 From: Harris Antony Jos Date: Mon, 7 Sep 2026 16:26:04 +0200 Subject: [PATCH 5/6] Updated pip install commands to have necessary packages to run unit tests --- .github/workflows/CI_actions.yml | 4 ++-- requirements/requirements-tests.txt | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI_actions.yml b/.github/workflows/CI_actions.yml index 6108e8e..ad75fa4 100644 --- a/.github/workflows/CI_actions.yml +++ b/.github/workflows/CI_actions.yml @@ -39,7 +39,7 @@ jobs: run: | sudo apt install pandoc python -m pip install --upgrade pip - pip install . + pip install .["docs"] # run notebooks sed -i -E "s/nbsphinx_execute *=.*/nbsphinx_execute = 'always'/g" doc/conf.py - name: make html @@ -78,7 +78,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install . + pip install .["tests"] - name: run tests run: | diff --git a/requirements/requirements-tests.txt b/requirements/requirements-tests.txt index e079f8a..fd0426d 100644 --- a/requirements/requirements-tests.txt +++ b/requirements/requirements-tests.txt @@ -1 +1,2 @@ pytest +elephant[extras] From 7faa520db15731ac7ae371e01fae7d9d48f91666 Mon Sep 17 00:00:00 2001 From: Harris Antony Jos Date: Thu, 10 Sep 2026 09:51:40 +0200 Subject: [PATCH 6/6] Fix for silent failure in unitary event analysis doc build, swapped out np.stack with list comprehension in doc strings example --- viziphant/unitary_event_analysis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/viziphant/unitary_event_analysis.py b/viziphant/unitary_event_analysis.py index c5a86bb..6bf3b78 100644 --- a/viziphant/unitary_event_analysis.py +++ b/viziphant/unitary_event_analysis.py @@ -177,7 +177,7 @@ def plot_ue(spiketrains, Js_dict, significance_level=0.05, spiketrains2 = [homogeneous_poisson_process(rate=50 * pq.Hz, t_stop=2 * pq.s) for _ in range(5)] - spiketrains = np.stack((spiketrains1, spiketrains2), axis=1) + spiketrains = [ [unit1, unit2] for unit1, unit2 in zip(spiketrains1, spiketrains2)] ue_dict = jointJ_window_analysis(spiketrains, bin_size=5 * pq.ms, win_size=100 * pq.ms,