From 01b52bb87d8bec19c119047e3331fc48ae56d8e0 Mon Sep 17 00:00:00 2001 From: Andrew Davison Date: Tue, 25 Mar 2025 15:22:38 +0100 Subject: [PATCH 1/7] Bump minimum Python version that is tested in CI to 3.9 --- .github/workflows/full-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/full-test.yml b/.github/workflows/full-test.yml index 1e132a9e..17a757af 100644 --- a/.github/workflows/full-test.yml +++ b/.github/workflows/full-test.yml @@ -16,7 +16,7 @@ jobs: strategy: fail-fast: true matrix: - python-version: ["3.8", "3.10", "3.12"] + python-version: ["3.9", "3.10", "3.12"] os: ["ubuntu-latest", "windows-latest"] steps: - uses: actions/checkout@v4 From 143c4fc8404675d06e7ea1e5890e048f9a0d02a3 Mon Sep 17 00:00:00 2001 From: Andrew Davison Date: Tue, 25 Mar 2025 15:51:05 +0100 Subject: [PATCH 2/7] Try using "setup-mpi" to avoid error on Windows cf https://github.com/mpi4py/setup-mpi --- .github/workflows/full-test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/full-test.yml b/.github/workflows/full-test.yml index 17a757af..9846fcb5 100644 --- a/.github/workflows/full-test.yml +++ b/.github/workflows/full-test.yml @@ -20,6 +20,7 @@ jobs: os: ["ubuntu-latest", "windows-latest"] steps: - uses: actions/checkout@v4 + - uses: mpi4py/setup-mpi@v1 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: From de38583bccfc38e53f48da4bfde39d5a85ca7528 Mon Sep 17 00:00:00 2001 From: Andrew Davison Date: Tue, 25 Mar 2025 16:00:28 +0100 Subject: [PATCH 3/7] More CI tweaks --- .github/workflows/full-test.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/full-test.yml b/.github/workflows/full-test.yml index 9846fcb5..bdbf2fb3 100644 --- a/.github/workflows/full-test.yml +++ b/.github/workflows/full-test.yml @@ -53,13 +53,18 @@ jobs: - name: Install PyNN itself run: | pip install -e ".[test]" - - name: Test installation has worked + - name: Test installation has worked (Ubuntu) # this is needed because the PyNN tests are just skipped if the simulator # fails to install, so we need to catch import failures separately + if: startsWith(matrix.os, 'ubuntu') run: | python -c "import pyNN.nest" python -c "import pyNN.neuron" python -c "import pyNN.brian2" + - name: Test installation has worked (Windows) + if: startsWith(matrix.os, 'windows') + run: | + python -c "import pyNN.brian2" - name: Lint with flake8 run: | # stop the build if there are Python syntax errors or undefined names From 68324d32c0e859bd33a77e82ee23af036cd41612 Mon Sep 17 00:00:00 2001 From: Andrew Davison Date: Tue, 25 Mar 2025 16:09:38 +0100 Subject: [PATCH 4/7] Trying to fix error with Brian2 codegen: ``` File "C:\hostedtoolcache\windows\Python\3.12.9\x64\Lib\site-packages\brian2\codegen\cpp_prefs.py", line 24, in from distutils._msvccompiler import _get_vc_env ImportError: cannot import name '_get_vc_env' from 'distutils._msvccompiler' (C:\hostedtoolcache\windows\Python\3.12.9\x64\Lib\site-packages\setuptools\_distutils\_msvccompiler.py) ``` ref: https://github.com/pypa/setuptools/issues/4874 --- .github/workflows/full-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/full-test.yml b/.github/workflows/full-test.yml index bdbf2fb3..46eacdaa 100644 --- a/.github/workflows/full-test.yml +++ b/.github/workflows/full-test.yml @@ -32,7 +32,7 @@ jobs: - name: Install basic Python dependencies run: | python -m pip install --upgrade pip - python -m pip install pytest pytest-cov coveralls flake8 + python -m pip install pytest pytest-cov coveralls flake8 "setuptools<=75.8.2" - name: Install Brian 2 run: | python -m pip install brian2 From 6fddd0c0a0d325e84c1aa222f2819b6648f14934 Mon Sep 17 00:00:00 2001 From: Andrew Davison Date: Wed, 26 Mar 2025 12:18:36 +0100 Subject: [PATCH 5/7] Test with numpy < 2, as NEURON doesn't yet support numpy v2 --- .github/workflows/full-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/full-test.yml b/.github/workflows/full-test.yml index 46eacdaa..3cb59358 100644 --- a/.github/workflows/full-test.yml +++ b/.github/workflows/full-test.yml @@ -32,7 +32,7 @@ jobs: - name: Install basic Python dependencies run: | python -m pip install --upgrade pip - python -m pip install pytest pytest-cov coveralls flake8 "setuptools<=75.8.2" + python -m pip install pytest pytest-cov coveralls flake8 "setuptools<=75.8.2" "numpy<2" - name: Install Brian 2 run: | python -m pip install brian2 From 57f5040d59bbf142623005c0a91ee3304e5c6154 Mon Sep 17 00:00:00 2001 From: Andrew Davison Date: Thu, 27 Mar 2025 14:49:07 +0100 Subject: [PATCH 6/7] Fix some issues that produce errors with the `recording.files` module with NumPy v2 --- pyNN/common/projections.py | 2 +- pyNN/recording/files.py | 16 ++-------------- test/unittests/test_connectors_parallel.py | 2 +- test/unittests/test_connectors_serial.py | 2 +- test/unittests/test_files.py | 14 -------------- 5 files changed, 5 insertions(+), 31 deletions(-) diff --git a/pyNN/common/projections.py b/pyNN/common/projections.py index 1257cd1a..66a836fc 100644 --- a/pyNN/common/projections.py +++ b/pyNN/common/projections.py @@ -443,7 +443,7 @@ def save(self, attribute_names, file, format='list', gather=True, with_address=T if attribute_names in ('all', 'connections'): attribute_names = self.synapse_type.get_parameter_names() if isinstance(file, str): - file = recording.files.StandardTextFile(file, mode='wb') + file = recording.files.StandardTextFile(file, mode='w') all_values = self.get(attribute_names, format=format, gather=gather, with_address=with_address) if format == 'array': diff --git a/pyNN/recording/files.py b/pyNN/recording/files.py index 01f6359d..01d8cd38 100644 --- a/pyNN/recording/files.py +++ b/pyNN/recording/files.py @@ -28,17 +28,6 @@ DEFAULT_BUFFER_SIZE = 10000 -def _savetxt(filename, data, format, delimiter): - """ - Due to the lack of savetxt in older versions of numpy - we provide a cut-down version of that function. - """ - f = open(filename, 'w') - for row in data: - f.write(delimiter.join([format % val for val in row]) + '\n') - f.close() - - def savez(file, *args, **kwds): import zipfile from numpy.lib import format @@ -148,10 +137,9 @@ def write(self, data, metadata): # write header header_lines = ["# %s = %s" % item for item in metadata.items()] header = "\n".join(header_lines) + '\n' - self.fileobj.write(header.encode('utf-8')) + self.fileobj.write(header) # write data - savetxt = getattr(np, 'savetxt', _savetxt) - savetxt(self.fileobj, data, fmt='%r', delimiter='\t') + np.savetxt(self.fileobj, data, delimiter='\t') self.fileobj.close() def read(self): diff --git a/test/unittests/test_connectors_parallel.py b/test/unittests/test_connectors_parallel.py index e533b859..282e339b 100644 --- a/test/unittests/test_connectors_parallel.py +++ b/test/unittests/test_connectors_parallel.py @@ -448,7 +448,7 @@ def test_with_plastic_synapses_not_distributed(self, sim=sim): (2, 2, 0.4, 0.13, 130, 97), (0, 1, 0.5, 0.14, 140, 96), # local ] - file = recording.files.StandardTextFile("test.connections.2", mode='wb') + file = recording.files.StandardTextFile("test.connections.2", mode='w') file.write(connection_list, {"columns": ["i", "j", "weight", "delay", "U", "tau_rec"]}) C = connectors.FromFileConnector("test.connections.2", distributed=False) syn = sim.TsodyksMarkramSynapse(tau_facil=88.8) diff --git a/test/unittests/test_connectors_serial.py b/test/unittests/test_connectors_serial.py index b9380e71..3e217c13 100644 --- a/test/unittests/test_connectors_serial.py +++ b/test/unittests/test_connectors_serial.py @@ -555,7 +555,7 @@ def test_with_plastic_synapses_not_distributed(self, sim=sim): (2, 2, 0.4, 0.13, 130, 97), (0, 1, 0.5, 0.14, 140, 96), # local ] - file = recording.files.StandardTextFile("test.connections.2", mode='wb') + file = recording.files.StandardTextFile("test.connections.2", mode='w') file.write(connection_list, {"columns": ["i", "j", "weight", "delay", "U", "tau_rec"]}) C = connectors.FromFileConnector("test.connections.2", distributed=False) syn = sim.TsodyksMarkramSynapse(tau_facil=88.8) diff --git a/test/unittests/test_files.py b/test/unittests/test_files.py index 1a6cfa22..da685659 100644 --- a/test/unittests/test_files.py +++ b/test/unittests/test_files.py @@ -11,20 +11,6 @@ builtin_open = open -def test__savetxt(): - mock_file = Mock() - files.open = Mock(return_value=mock_file) - files._savetxt(filename="dummy_file", - data=[(0, 2.3), (1, 3.4), (2, 4.3)], - format="%f", - delimiter=" ") - target = [(('0.000000 2.300000\n',), {}), - (('1.000000 3.400000\n',), {}), - (('2.000000 4.300000\n',), {})] - assert mock_file.write.call_args_list == target - files.open = builtin_open - - def test_create_BaseFile(): files.open = Mock() bf = files.BaseFile("filename", 'r') From 3d6c7ec2e706cfd963187c97d39299466709f5b6 Mon Sep 17 00:00:00 2001 From: Andrew Davison Date: Thu, 27 Mar 2025 16:40:03 +0100 Subject: [PATCH 7/7] Update metadata files and doc to reflect new minimum Python version (3.9) Also remove numpy and quantities as explicit dependencies, since installing Neo will give us appropriate versions. --- doc/developers/contributing.txt | 2 +- doc/installation.txt | 4 ++-- pyproject.toml | 8 +++----- test/system/test_neuron.py | 7 ------- 4 files changed, 6 insertions(+), 15 deletions(-) diff --git a/doc/developers/contributing.txt b/doc/developers/contributing.txt index d212e4d1..6b99d729 100644 --- a/doc/developers/contributing.txt +++ b/doc/developers/contributing.txt @@ -70,7 +70,7 @@ We try to stay fairly close to PEP8_. Please note in particular: - some function/method names in PyNN use ``mixedCase``, but these will gradually be deprecated and replaced with ``lower_case_with_underscores``. Any new functions or methods should use the latter. - - we currently target versions 3.8+ + - we currently target versions 3.9+ Testing diff --git a/doc/installation.txt b/doc/installation.txt index 1ccf1068..1c0fa559 100644 --- a/doc/installation.txt +++ b/doc/installation.txt @@ -7,10 +7,10 @@ install and run PyNN on Windows, but this has not been tested. Installing PyNN requires: -* Python (version 3.8+) +* Python (version 3.9+) * a recent version of the NumPy_ package * the lazyarray_ package -* the Neo_ package (>= 0.11.0) +* the Neo_ package (>= 0.13.4) * at least one of the supported simulators: e.g. NEURON, NEST, Brian 2 or Arbor. Optional dependencies are: diff --git a/pyproject.toml b/pyproject.toml index d36c1a6a..b2105893 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ name = "PyNN" version = "0.12.3" description = "A Python package for simulator-independent specification of neuronal network models" readme = "README.rst" -requires-python = ">=3.8" +requires-python = ">=3.9" license = {text = "CeCILL http://www.cecill.info"} authors = [ {name = "The PyNN team", email = "pynn-maintainers@protonmail.com"} @@ -20,19 +20,17 @@ classifiers = [ "Natural Language :: English", "Operating System :: OS Independent", "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", + "Programming Language :: Python :: 3.13", "Topic :: Education", "Topic :: Scientific/Engineering" ] dependencies = [ - "numpy>=1.18.5", "lazyarray>=0.5.2", - "neo>=0.11.0", - "quantities>=0.12.1", + "neo>=0.13.4", "morphio" ] diff --git a/test/system/test_neuron.py b/test/system/test_neuron.py index 0b6e2506..8df94a02 100644 --- a/test/system/test_neuron.py +++ b/test/system/test_neuron.py @@ -28,11 +28,6 @@ have_neuroml = False -skip_ci = False -if "JENKINS_SKIP_TESTS" in os.environ: - skip_ci = os.environ["JENKINS_SKIP_TESTS"] == "1" - - def test_ticket168(): """ Error setting firing rate of `SpikeSourcePoisson` after `reset()` in NEURON @@ -131,8 +126,6 @@ class SimpleNeuronType(NativeCellType): def test_electrical_synapse(): pytest.skip("Skipping test for now as it produces a segmentation fault") - if skip_ci: - pytest.skip("Skipping test on CI server as it produces a segmentation fault") p1 = pyNN.neuron.Population(4, pyNN.neuron.standardmodels.cells.HH_cond_exp()) p2 = pyNN.neuron.Population(4, pyNN.neuron.standardmodels.cells.HH_cond_exp()) syn = pyNN.neuron.ElectricalSynapse(weight=1.0)