Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 53 additions & 25 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,41 +11,69 @@ on:

jobs:
build:

name: Python ${{ matrix.python-version }} (${{ matrix.backend }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10"]
cython: ['python -m pip install -q cython', 'echo "No Cython"']
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
# PySpike treats Cython as an optional accelerator. Both install paths
# need to work and produce a passing test suite:
#
# cython -- `pip install .` uses pip's isolated build env, which
# installs Cython per pyproject.toml's build-system
# requires. setup.py compiles the .pyx sources into
# .so modules and the fast path is used at runtime.
#
# no-cython -- `pip install --no-build-isolation .` with Cython
# deliberately absent. setup.py's `try: import Cython`
# raises ImportError, no extensions are built, and
# each pyspike module falls back to python_backend.py
# via its own `try/except ImportError`.
backend: [cython, no-cython]

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies

- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install libblas-dev
sudo apt-get install liblapack-dev
sudo apt-get install gfortran
python -m pip install --upgrade pip
python -m pip install flake8 pytest nose numpy scipy
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Install Cython
run: |
${{ matrix.cython }}
- name: Install package
sudo apt-get install -y libblas-dev liblapack-dev gfortran

- name: Upgrade pip
run: python -m pip install --upgrade pip

- name: Install PySpike — with Cython (build isolation)
if: matrix.backend == 'cython'
run: pip install .

- name: Install PySpike — without Cython (pure-Python fallback)
if: matrix.backend == 'no-cython'
run: |
python SetupNoPrompt.py build_ext --inplace
# - name: Lint with flake8
# run: |
# # stop the build if there are Python syntax errors or undefined names
# flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
# flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
# Provide only the bare build prereqs Cython is not among them, so
# setup.py's `try: from Cython.Distutils import build_ext` fails and
# no C extensions are produced. --no-build-isolation makes pip use
# this env instead of provisioning a fresh one (which would install
# Cython per pyproject.toml).
pip install "setuptools>=77" wheel "numpy>=1.25"
pip install --no-build-isolation .
# Sanity check: the compiled extension must NOT be importable.
python -c "
try:
from pyspike.cython import cython_distances
except ImportError:
print('OK: cython_distances absent, runtime will use python_backend')
else:
raise SystemExit('FAIL: cython_distances was built despite no-cython matrix')
"

- name: Install test dependencies
run: pip install pytest scipy

- name: Test with PyTest
run: |
python -m pytest
run: python -m pytest
10 changes: 8 additions & 2 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
include *.rst
include *.txt
include pyspike/cython/*.c
include directionality/cython/*.c
include License.txt
include Changelog
include pyproject.toml

# Cython sources — required so `pip install` from sdist can regenerate
# the .c files and build the extension modules.
recursive-include pyspike/cython *.pyx *.pxd

recursive-include examples *.py *.txt
recursive-include test *.py *.txt
recursive-include doc *
5 changes: 0 additions & 5 deletions SetupNoPrompt.py

This file was deleted.

53 changes: 53 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
[build-system]
# setuptools >=77 supports the SPDX-string form `license = "BSD-2-Clause"`
# in [project] (PEP 639). numpy and Cython are required because setup.py
# calls numpy.get_include() and compiles .pyx sources for the C extensions.
requires = [
"setuptools>=77",
"wheel",
"Cython>=3.0",
"numpy>=1.25",
]
build-backend = "setuptools.build_meta"

[project]
name = "pyspike"
version = "0.8.1"
description = "A Python library for the numerical analysis of spike train similarity"
readme = "Readme.rst"
requires-python = ">=3.9"
license = "BSD-2-Clause"
license-files = ["License.txt"]
authors = [
{ name = "Mario Mulansky", email = "mario.mulansky@gmx.net" },
]
keywords = ["data analysis", "spike", "neuroscience"]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Science/Research",
"Topic :: Scientific/Engineering",
"Topic :: Scientific/Engineering :: Information Analysis",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
]
dependencies = [
"numpy",
]

[project.urls]
Homepage = "https://github.com/mariomulansky/PySpike"
Repository = "https://github.com/mariomulansky/PySpike"
Issues = "https://github.com/mariomulansky/PySpike/issues"

[tool.setuptools]
include-package-data = true

[tool.setuptools.packages.find]
exclude = ["doc*", "test*", "examples*"]
# Cython .c files are generated at build time (.gitignore excludes them) and
# the resulting .so files are placed automatically. No package_data needed
# for the wheel; MANIFEST.in handles inclusion of .pyx sources in the sdist.
24 changes: 9 additions & 15 deletions pyspike/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,17 @@
spike_train_order_bi, spike_train_order_multi, \
optimal_spike_train_sorting, permutate_matrix

# define the __version__ following
# http://stackoverflow.com/questions/17583443
from pkg_resources import get_distribution, DistributionNotFound
import os.path
# Expose the installed version via importlib.metadata (stdlib since 3.8).
# Previously this used pkg_resources, which depends on setuptools — and from
# Python 3.12 onwards venvs no longer ship setuptools by default, so the old
# import would fail at runtime in fresh 3.12+ environments.
from importlib.metadata import version as _get_version, PackageNotFoundError

try:
_dist = get_distribution('pyspike')
# Normalize case for Windows systems
dist_loc = os.path.normcase(_dist.location)
here = os.path.normcase(__file__)
if not here.startswith(os.path.join(dist_loc, 'pyspike')):
# not installed, but there is another version that *is*
raise DistributionNotFound
except DistributionNotFound:
__version__ = 'Please install this project with setup.py'
else:
__version__ = _dist.version
__version__ = _get_version("pyspike")
except PackageNotFoundError:
# Running from a source checkout that hasn't been installed.
__version__ = "0.0.0+unknown"

disable_backend_warning = False

Expand Down
2 changes: 0 additions & 2 deletions setup.cfg

This file was deleted.

154 changes: 56 additions & 98 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
""" setup.py

to compile cython files:
python setup.py build_ext --inplace
Compile the Cython extensions for PySpike.

All packaging metadata (name, version, dependencies, classifiers, ...) lives in
pyproject.toml. This file only declares the C extension modules, because that
still needs imperative setup() configuration.

Copyright 2014-2017, Mario Mulansky <mario.mulansky@gmx.net>
To compile cython files in-place:
python setup.py build_ext --inplace


Copyright 2014-2026, Mario Mulansky <mario.mulansky@gmx.net>

Distributed under the BSD License

"""
from setuptools import setup, find_packages
from distutils.extension import Extension
import os.path

from setuptools import Extension, setup

try:
from Cython.Distutils import build_ext
except ImportError:
Expand All @@ -22,110 +28,62 @@


class numpy_include(os.PathLike):
"""Defers import of numpy until install_requires is through"""
def __str__(self):
import numpy
return numpy.get_include()

def __fspath__(self):
return str(self)


if os.path.isfile("pyspike/cython/cython_add.c") and \
os.path.isfile("pyspike/cython/cython_get_tau.c") and \
os.path.isfile("pyspike/cython/cython_profiles.c") and \
os.path.isfile("pyspike/cython/cython_distances.c") and \
os.path.isfile("pyspike/cython/cython_directionality.c") and \
os.path.isfile("pyspike/cython/cython_simulated_annealing.c"):
use_c = True
else:
use_c = False
"""Defers import of numpy until the build environment is in place.

pyproject.toml lists numpy as a build-system requirement, so by the time
setup.py actually runs build_ext, numpy is importable. We can't import it
at module top level, though, because setuptools imports setup.py before
build-system requires are installed.
"""

def __str__(self):
import numpy
return numpy.get_include()

def __fspath__(self):
return str(self)


_CYTHON_MODULES = (
"cython_add",
"cython_get_tau",
"cython_profiles",
"cython_distances",
"cython_directionality",
"cython_simulated_annealing",
)


def _all_c_sources_present():
return all(
os.path.isfile(f"pyspike/cython/{name}.c") for name in _CYTHON_MODULES
)


use_c = _all_c_sources_present()

if not use_cython and not use_c:
print('Cython not installed. Programs will be slow.')
# Ans = input('Abort? (Y/N)\n')
# if len(Ans)>0 and (Ans[0]=='Y' or Ans[0]=='y'):
# print("\nAborting\n")
# raise RuntimeError('User termination')
print("Cython not installed and no pre-generated .c files found. "
"PySpike will fall back to the pure-Python backend (slow).")

cmdclass = {}
ext_modules = []

if use_cython: # Cython is available, compile .pyx -> .c
ext_modules += [
Extension("pyspike.cython.cython_add",
["pyspike/cython/cython_add.pyx"]),
Extension("pyspike.cython.cython_get_tau",
["pyspike/cython/cython_get_tau.pyx"]),
Extension("pyspike.cython.cython_profiles",
["pyspike/cython/cython_profiles.pyx"]),
Extension("pyspike.cython.cython_distances",
["pyspike/cython/cython_distances.pyx"]),
Extension("pyspike.cython.cython_directionality",
["pyspike/cython/cython_directionality.pyx"]),
Extension("pyspike.cython.cython_simulated_annealing",
["pyspike/cython/cython_simulated_annealing.pyx"])
if use_cython: # Cython is available, compile .pyx -> .c -> binary
ext_modules = [
Extension(f"pyspike.cython.{name}", [f"pyspike/cython/{name}.pyx"])
for name in _CYTHON_MODULES
]
cmdclass.update({'build_ext': build_ext})
elif use_c: # c files are there, compile to binaries
ext_modules += [
Extension("pyspike.cython.cython_add",
["pyspike/cython/cython_add.c"]),
Extension("pyspike.cython.cython_get_tau",
["pyspike/cython/cython_get_tau.c"]),
Extension("pyspike.cython.cython_profiles",
["pyspike/cython/cython_profiles.c"]),
Extension("pyspike.cython.cython_distances",
["pyspike/cython/cython_distances.c"]),
Extension("pyspike.cython.cython_directionality",
["pyspike/cython/cython_directionality.c"]),
Extension("pyspike.cython.cython_simulated_annealing",
["pyspike/cython/cython_simulated_annealing.c"])
cmdclass["build_ext"] = build_ext
elif use_c: # No Cython, but pre-generated .c files are present
ext_modules = [
Extension(f"pyspike.cython.{name}", [f"pyspike/cython/{name}.c"])
for name in _CYTHON_MODULES
]
# neither cython nor c files available -> automatic fall-back to python backend
# else: neither Cython nor .c files fall through to pure-Python backend.

setup(
name='pyspike',
packages=find_packages(exclude=['doc', 'test*']),
version='0.8.0',
cmdclass=cmdclass,
ext_modules=ext_modules,
include_dirs=[numpy_include()],
description='A Python library for the numerical analysis of spike\
train similarity',
author='Mario Mulansky',
author_email='mario.mulansky@gmx.net',
license='BSD',
url='https://github.com/mariomulansky/PySpike',
install_requires=['numpy'],
keywords=['data analysis', 'spike', 'neuroscience'], # arbitrary keywords
classifiers=[
# How mature is this project? Common values are
# 3 - Alpha
# 4 - Beta
# 5 - Production/Stable
'Development Status :: 4 - Beta',

# Indicate who your project is intended for
'Intended Audience :: Science/Research',
'Topic :: Scientific/Engineering',
'Topic :: Scientific/Engineering :: Information Analysis',

'License :: OSI Approved :: BSD License',

'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
],
package_data={
'pyspike': ['cython/cython_add.c',
'cython/cython_profiles.c',
'cython/cython_get_tau.c',
'cython/cython_distances.c',
'cython/cython_directionality.c',
'cython/cython_simulated_annealing.c'],
'test': ['Spike_testdata.txt']
}
)
Loading