Skip to content

Commit 457915b

Browse files
rossantghisvail
andauthored
Release phylib 2.7.1 (#65)
* style: fix existing flake8 violations * ci: enforce flake8 checks * release: prepare phylib 2.7.1 * Ensure dataset is writable * release: refresh 2.7.1 documentation and package metadata * release: date phylib 2.7.1 changelog --------- Co-authored-by: Ghislain Vaillant <ghisvail@users.noreply.github.com>
1 parent 4d95d6e commit 457915b

12 files changed

Lines changed: 111 additions & 43 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,26 @@ concurrency:
1515
cancel-in-progress: true
1616

1717
jobs:
18+
lint:
19+
name: Lint
20+
runs-on: ubuntu-latest
21+
22+
steps:
23+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
24+
25+
- name: Set up Python
26+
uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0
27+
with:
28+
python-version: "3.12"
29+
cache: pip
30+
cache-dependency-path: requirements-dev.txt
31+
32+
- name: Install lint dependencies
33+
run: python -m pip install flake8
34+
35+
- name: Run lint
36+
run: python -m flake8 phylib
37+
1838
test:
1939
name: ${{ matrix.scope }} (${{ matrix.os }}, Python ${{ matrix.python-version }})
2040
runs-on: ${{ matrix.os }}
@@ -66,7 +86,7 @@ jobs:
6686
build:
6787
name: Build package
6888
runs-on: ubuntu-latest
69-
needs: test
89+
needs: [lint, test]
7090

7191
steps:
7292
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

‎CHANGELOG.md‎

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,34 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## [Unreleased]
8+
## [2.7.1] - 2026-10-08
99

1010
### Added
11-
- Code of Conduct for project participation and incident reporting.
11+
- Added a Code of Conduct for project participation and incident reporting.
12+
13+
### Changed
14+
- Optimized spike selection and stored sparse-waveform extraction for large datasets,
15+
including bounded display sampling and batched memory-mapped reads.
16+
- Unknown raw-data file extensions now emit a warning instead of preventing the dataset
17+
from loading.
1218

1319
### Fixed
20+
- Prevented geometry range searches from hanging with `float32` input.
1421
- Development checkouts now report a development version with a Git commit suffix.
15-
- Template datasets without waveform templates can be reloaded after cluster assignments change,
16-
and stored spike-waveform subsets provide their waveform sample count.
17-
- Cluster assignments are written atomically, so a crash during a save no longer truncates `spike_clusters.npy`.
18-
- TSV, JSON, text and `params.py` files are written atomically, so a crash during a save no longer
19-
truncates the file it was replacing, for instance `cluster_group.tsv`.
20-
- #57 a blank `dat_path` in `params.py` is now read as "no raw data file" instead of resolving to
21-
the dataset directory.
22+
- Fixed reloading curated template-less datasets after cluster assignments change.
23+
- Derived the waveform sample count from stored spike-waveform subsets when templates are
24+
unavailable.
25+
- Treated a blank `dat_path` in `params.py` as no raw-data file instead of the dataset
26+
directory (#57).
27+
- Made cluster-assignment, TSV, JSON, text, and `params.py` writes atomic to prevent
28+
truncation if saving is interrupted.
29+
- Corrected wheel metadata to advertise Python 3 only.
30+
- Included the changelog and test dependency list in source distributions.
31+
32+
### Development
33+
- Enforced flake8 checks in CI and fixed existing lint violations.
34+
- Made copied test datasets writable when the source cache is read-only, supporting
35+
sandboxed distribution builds (#66).
2236

2337
## [2.7.0] 2025-12-10
2438

‎MANIFEST.in‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
include LICENSE.md
22
include README.md
3+
include CHANGELOG.md
34
include requirements.txt
5+
include requirements-dev.txt
46

5-
recursive-include tests *
67
recursive-include phylib/electrode/probes *.prb
78
recursive-exclude * __pycache__
89
recursive-exclude * *.py[co]

‎README.md‎

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,54 @@
1-
# phylib
2-
[![Build Status](https://img.shields.io/travis/cortex-lab/phylib.svg)](https://travis-ci.org/cortex-lab/phylib)
3-
[![codecov.io](https://img.shields.io/codecov/c/github/cortex-lab/phylib.svg)](http://codecov.io/github/cortex-lab/phylib?branch=master)
4-
5-
Electrophysiological data analysis library used by [phy](https://github.com/kwikteam/phy/), a spike sorting visualization software, and [ibllib](https://github.com/int-brain-lab/ibllib/).
6-
7-
1+
# phylib
2+
[![CI](https://github.com/cortex-lab/phylib/actions/workflows/ci.yml/badge.svg)](https://github.com/cortex-lab/phylib/actions/workflows/ci.yml)
3+
[![codecov.io](https://img.shields.io/codecov/c/github/cortex-lab/phylib.svg)](http://codecov.io/github/cortex-lab/phylib?branch=master)
4+
5+
Electrophysiological data analysis library used by [phy](https://github.com/kwikteam/phy/), a spike sorting visualization software, and [ibllib](https://github.com/int-brain-lab/ibllib/).
6+
7+
88
## Contribution
99

1010
- participation in phylib is governed by the project [Code of Conduct](CODE_OF_CONDUCT.md)
1111
- run all tests using pytest `pytest phylib`
1212
- PR to master
1313
- keep `phylib/__init__.py` on the next development version (for example,
1414
`2.7.1.dev0`)
15-
- for a release, update `CHANGELOG.md`, remove the `.dev0` suffix, run the
16-
tests, and tag the exact release commit
15+
- for a release, follow the steps below
1716
- after publishing, immediately bump `phylib/__init__.py` to the next
1817
`.dev0` version
19-
- publish to pypi:
20-
```shell
21-
rm -R dist
22-
rm -R build
23-
python setup.py sdist bdist_wheel
24-
twine upload dist/*
18+
19+
## Release
20+
21+
Before merging the release PR, confirm maintainer approval and green CI. Update
22+
`CHANGELOG.md` with all changes since the previous release, replace the pending
23+
release date with the actual release date, and remove the `.dev0` suffix from
24+
`phylib/__init__.py`.
25+
26+
Validate the final release checkout in a dedicated environment:
27+
28+
```shell
29+
uv venv --python 3.12 .venv-release
30+
uv pip install --python .venv-release -r requirements-dev.txt -e . setuptools build twine
31+
.venv-release/bin/python -m flake8 phylib
32+
.venv-release/bin/python -m pytest phylib
2533
```
34+
35+
After the approved PR is merged, create an annotated tag from the merged release
36+
commit (for example, `git tag -a 2.7.1 -m "phylib 2.7.1"`). Build fresh artifacts
37+
from that tagged checkout into an empty output directory:
38+
39+
```shell
40+
.venv-release/bin/python -m build --outdir dist/2.7.1
41+
.venv-release/bin/python -m twine check dist/2.7.1/*
42+
```
43+
44+
Install the wheel in a separate clean environment and verify its version and
45+
imports. Verify the downstream phy template-less waveform regression against
46+
this wheel before publishing. Upload only the artifacts just checked:
47+
48+
```shell
49+
.venv-release/bin/python -m twine upload dist/2.7.1/*
50+
```
51+
52+
Verify installation of the published version from PyPI in another clean
53+
environment. Then bump `master` to `2.7.2.dev0` and add an `Unreleased` changelog
54+
section. Substitute the appropriate version numbers for subsequent releases.

‎phylib/__init__.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
__author__ = 'Cyrille Rossant'
2424
__email__ = 'cyrille.rossant at gmail.com'
25-
__version__ = '2.7.1.dev0'
25+
__version__ = '2.7.1'
2626
__version_git__ = __version__ + _git_version()
2727

2828

‎phylib/io/alf.py‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -373,10 +373,11 @@ def get_waveforms_amp(templates):
373373
cha = np.max(templates, axis=1) - np.min(templates, axis=1)
374374
return np.max(cha, axis=1)
375375

376-
# we unwhiten the templates waveforms, this will expand the templates to the original non-sparse size
376+
# Unwhitening expands the template waveforms to the original non-sparse size.
377377
templates_phy = np.zeros([nclu, templates['waveforms'].shape[1], nch], dtype=np.float32)
378378
for i in np.arange(templates_phy.shape[0]):
379-
templates_phy[i] = np.matmul(templates['waveforms'][i], wm[templates['waveformsChannels'][i], :])
379+
templates_phy[i] = np.matmul(
380+
templates['waveforms'][i], wm[templates['waveformsChannels'][i], :])
380381

381382
# the original templates have a rms of 1.0, so here we just need to normalize by rms
382383
rms_templates = np.sum(np.sum(templates['waveforms'] ** 2, axis=1), axis=1) ** 0.5
@@ -390,7 +391,8 @@ def get_waveforms_amp(templates):
390391
np.save(target_path.joinpath('channel_map.npy'), np.arange(nch))
391392
np.save(target_path.joinpath('templates.npy'), templates_phy)
392393

393-
np.save(target_path.joinpath('templates_ind.npy'), np.tile(np.arange(nclu)[np.newaxis, :], reps=[nch, 1]))
394+
template_indices = np.tile(np.arange(nclu)[np.newaxis, :], reps=[nch, 1])
395+
np.save(target_path.joinpath('templates_ind.npy'), template_indices)
394396

395397
# if we have metrics information, output the ks2_label information
396398
if alf_path.joinpath('cluster.metrics.pqt').exists():

‎phylib/io/model.py‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -994,14 +994,14 @@ def get_waveforms(self, spike_ids, channel_ids=None):
994994
# Load from precomputed spikes.
995995
try:
996996
return get_spike_waveforms(
997-
spike_ids, channel_ids, spike_waveforms=self.spike_waveforms,
998-
n_samples_waveforms=nsw)
997+
spike_ids, channel_ids, spike_waveforms=self.spike_waveforms,
998+
n_samples_waveforms=nsw)
999999
except AssertionError:
1000-
logger.warning(
1001-
"Error when loading waveforms from precomputed waveforms, trying to load the raw data.")
1002-
spike_samples = self.spike_samples[spike_ids]
1003-
return extract_waveforms(
1004-
self.traces, spike_samples, channel_ids, n_samples_waveforms=nsw)
1000+
logger.warning(
1001+
"Error when loading precomputed waveforms; trying the raw data instead.")
1002+
spike_samples = self.spike_samples[spike_ids]
1003+
return extract_waveforms(
1004+
self.traces, spike_samples, channel_ids, n_samples_waveforms=nsw)
10051005
else:
10061006
# Or load directly from raw data (slower).
10071007
spike_samples = self.spike_samples[spike_ids]

‎phylib/io/tests/test_alf.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ def __init__(self, tempdir):
4141
np.save(p / 'amplitudes.npy', nr.uniform(low=0.5, high=1.5, size=self.ns))
4242
np.save(p / 'channel_positions.npy', np.c_[np.arange(self.nc), np.zeros(self.nc)])
4343
templates = np.random.normal(size=(self.nt, 50, self.nc))
44-
templates = templates / (np.sum(np.sum(templates ** 2, axis=1), axis=1) ** .5)[:, np.newaxis, np.newaxis]
44+
template_norms = np.sum(np.sum(templates ** 2, axis=1), axis=1) ** .5
45+
templates = templates / template_norms[:, np.newaxis, np.newaxis]
4546
np.save(p / 'templates.npy', templates)
4647
np.save(p / 'similar_templates.npy', np.tile(np.arange(self.nt), (self.nt, 1)))
4748
np.save(p / 'channel_map.npy', np.c_[np.arange(self.nc)])

‎phylib/io/tests/test_array.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,7 @@ def __getitem__(self, indices):
425425
sampled = _sample_spikes_evenly(SortedSpikeIDs(), 10)
426426
ae(sampled, np.linspace(0, 999_999, 10, dtype=np.int64))
427427

428+
428429
def test_select_spikes_1():
429430
spike_times = np.array([0., 1., 2., 3.3, 4.4])
430431
spike_clusters = np.array([1, 2, 1, 2, 4])

‎phylib/io/tests/test_datasets.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,10 @@ def test_download_file(tempdir, mock_urls):
141141

142142
assert_succeeds = (data_here and data_valid and
143143
((checksum_here == checksum_valid) or
144-
(not(checksum_here) and checksum_valid)))
144+
(not checksum_here and checksum_valid)))
145145

146146
download_succeeds = (assert_succeeds or (data_here and
147-
(not(data_valid) and not(checksum_here))))
147+
(not data_valid and not checksum_here)))
148148

149149
if download_succeeds:
150150
data = _dl(path)

0 commit comments

Comments
 (0)