diff --git a/.github/workflows/run_all_tests.yml b/.github/workflows/run_all_tests.yml index d03f937..df80271 100644 --- a/.github/workflows/run_all_tests.yml +++ b/.github/workflows/run_all_tests.yml @@ -61,19 +61,20 @@ jobs: if: ${{ matrix.requirements == 'minimum' }} run: | python -m pip install -r requirements-min.txt -r requirements-dev.txt - python -m pip install -e . + python -m pip install . - name: Install run requirements (pinned) if: ${{ matrix.requirements == 'pinned' }} run: | python -m pip install -r requirements-dev.txt - python -m pip install -e . + python -m pip install . - name: Install run requirements (upgraded) if: ${{ matrix.requirements == 'upgraded' }} run: | python -m pip install -r requirements-dev.txt - python -m pip install -U -e . + # force upgrade of all dependencies to latest versions within allowed range + python -m pip install -U --upgrade-strategy eager . - name: Run tests run: | @@ -146,19 +147,19 @@ jobs: if: ${{ matrix.requirements == 'minimum' }} run: | python -m pip install -r requirements-min.txt -r requirements-dev.txt - python -m pip install -e . + python -m pip install . - name: Install run requirements (pinned) if: ${{ matrix.requirements == 'pinned' }} run: | python -m pip install -r requirements-dev.txt - python -m pip install -e . + python -m pip install . - name: Install run requirements (upgraded) if: ${{ matrix.requirements == 'upgraded' }} run: | python -m pip install -r requirements-dev.txt - python -m pip install -U -e . + python -m pip install -U --upgrade-strategy eager . - name: Run tests run: | diff --git a/.github/workflows/run_coverage.yml b/.github/workflows/run_coverage.yml index c36064a..8aa9977 100644 --- a/.github/workflows/run_coverage.yml +++ b/.github/workflows/run_coverage.yml @@ -40,14 +40,12 @@ jobs: - name: Install package run: | - python -m pip install -e . # must install in editable mode for coverage to find sources + python -m pip install . python -m pip list - name: Run tests and generate coverage report run: | - pytest --cov - python -m coverage xml # codecov uploader requires xml format - python -m coverage report -m + pytest --cov --cov-report=xml --cov-report=term # codecov uploader requires xml format # TODO uncomment after setting up repo on codecov.io # - name: Upload coverage to Codecov diff --git a/.github/workflows/validate_schema.yml b/.github/workflows/validate_schema.yml new file mode 100644 index 0000000..570bfbe --- /dev/null +++ b/.github/workflows/validate_schema.yml @@ -0,0 +1,25 @@ +name: Validate schema + +on: + push: + pull_request: + workflow_dispatch: + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up Python 3.12 + uses: actions/setup-python@v4 + with: + python-version: "3.12" + - name: Install HDMF + run: | + pip install hdmf + - name: Download latest nwb schema language specification + run: | + curl -L https://raw.githubusercontent.com/NeurodataWithoutBorders/nwb-schema/dev/nwb.schema.json -o nwb.schema.json + - name: Validate schema specification + run: | + validate_hdmf_spec spec -m nwb.schema.json diff --git a/notebooks/example.ipynb b/notebooks/example.ipynb deleted file mode 100644 index cc61b5f..0000000 --- a/notebooks/example.ipynb +++ /dev/null @@ -1,165 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "174c5018-1c0a-4f55-899d-049bb87f63d5", - "metadata": {}, - "source": [ - "# Example demonstration of the example TetrodeSeries extension neurodata type\n", - "\n", - "TODO: Update this notebook with an example usage of your extension" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "02798a80-faea-4b75-aa97-70afad90fe27", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "\n", - "from pynwb import NWBHDF5IO, NWBFile\n", - "from pynwb.testing.mock.device import mock_Device\n", - "from pynwb.testing.mock.ecephys import mock_ElectrodeGroup, mock_ElectrodeTable\n", - "from pynwb.testing.mock.file import mock_NWBFile\n", - "\n", - "from ndx_fiber_photometry import TetrodeSeries\n", - "\n", - "\n", - "def set_up_nwbfile(nwbfile: NWBFile = None):\n", - " \"\"\"Create an NWBFile with a Device, ElectrodeGroup, and 10 electrodes in the ElectrodeTable.\"\"\"\n", - " nwbfile = nwbfile or mock_NWBFile()\n", - " device = mock_Device(nwbfile=nwbfile)\n", - " electrode_group = mock_ElectrodeGroup(device=device, nwbfile=nwbfile)\n", - " _ = mock_ElectrodeTable(n_rows=10, group=electrode_group, nwbfile=nwbfile)\n", - "\n", - " return nwbfile" - ] - }, - { - "cell_type": "markdown", - "id": "32be75e4-8fe9-401d-a613-8080f357d5f0", - "metadata": {}, - "source": [ - "Create an `NWBFile` object and a `TetrodeSeries` object and add the `TetrodeSeries` object to the `NWBFile`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "dcd2c070-f2c9-4ffc-8637-25ec3e6f7b37", - "metadata": {}, - "outputs": [], - "source": [ - "nwbfile = set_up_nwbfile()\n", - "\n", - "all_electrodes = nwbfile.create_electrode_table_region(\n", - " region=list(range(0, 10)),\n", - " description=\"all the electrodes\",\n", - ")\n", - "\n", - "data = np.random.rand(100, 10)\n", - "tetrode_series = TetrodeSeries(\n", - " name=\"TetrodeSeries\",\n", - " description=\"description\",\n", - " data=data,\n", - " rate=1000.0,\n", - " electrodes=all_electrodes,\n", - " trode_id=1,\n", - ")\n", - "\n", - "nwbfile.add_acquisition(tetrode_series)" - ] - }, - { - "cell_type": "markdown", - "id": "077a8d86-9d03-40e3-b60a-c837ecb643a7", - "metadata": {}, - "source": [ - "Visualize the TetrodeSeries object with the `nwbwidgets` package using the custom widget defined in the extension. You\n", - "will need the latest version of `nwbwidgets` installed for this to work." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1363282d-1a32-447d-9ca3-fc2360785cc2", - "metadata": {}, - "outputs": [], - "source": [ - "from nwbwidgets import nwb2widget, load_extension_widgets_into_spec\n", - "load_extension_widgets_into_spec(\"ndx_my_namespace\")\n", - "nwb2widget(nwbfile)" - ] - }, - { - "cell_type": "markdown", - "id": "ac894fae-6c5e-4a3d-be1e-d158aef084a4", - "metadata": {}, - "source": [ - "Write the file with the extension neurodata type to disk" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2fdaf978-6f95-4871-b26c-dad4bb62da42", - "metadata": {}, - "outputs": [], - "source": [ - "with NWBHDF5IO(\"test.nwb\", \"w\") as io:\n", - " io.write(nwbfile)" - ] - }, - { - "cell_type": "markdown", - "id": "a30660a3-8e64-4c5e-963d-6ecd7e31897e", - "metadata": {}, - "source": [ - "Read the NWB file from disk and print the `TetrodeSeries` object" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "00e38084-b55e-4c7a-ac89-8b10e43f41c1", - "metadata": {}, - "outputs": [], - "source": [ - "with NWBHDF5IO(\"test.nwb\", \"r\") as io:\n", - " read_nwbfile = io.read()\n", - " print(read_nwbfile.acquisition[\"TetrodeSeries\"])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "b36569b2-09d1-4281-bf4c-fb602e53636c", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.6" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/pyproject.toml b/pyproject.toml index a3d876c..017b1fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,36 +43,34 @@ dependencies = [ # "Discussions" = "https://github.com/organization/package/discussions" # "Changelog" = "https://github.com/organization/package/blob/main/CHANGELOG.md" -[tool.hatch.build] -include = [ - "src/pynwb", - "spec/ndx-fiber-photometry.extensions.yaml", - "spec/ndx-fiber-photometry.namespace.yaml", -] -exclude = [ - "src/pynwb/tests", -] - +# Include only the source code under `src/pynwb/ndx_fiber_photometry` and the spec files under `spec` +# in the wheel. [tool.hatch.build.targets.wheel] packages = [ "src/pynwb/ndx_fiber_photometry", "spec" ] +# Rewrite the path to the `spec` directory to `{{ cookiecutter.py_pkg_name }}/spec`. +# `{{ cookiecutter.py_pkg_name }}/__init__.py` will look there first for the spec files. +# The resulting directory structure within the wheel will be: +# {{ cookiecutter.py_pkg_name }}/ +# ├── __init__.py +# ├── spec +# └── widgets [tool.hatch.build.targets.wheel.sources] "spec" = "ndx_fiber_photometry/spec" +# The source distribution includes everything in the package except for the `src/matnwb` directory and +# git and github-related files. [tool.hatch.build.targets.sdist] -include = [ - "src/pynwb", - "spec/ndx-fiber-photometry.extensions.yaml", - "spec/ndx-fiber-photometry.namespace.yaml", - "docs", +exclude = [ + ".git*", + "src/matnwb", ] -exclude = [] [tool.pytest.ini_options] -# uncomment below to run pytest with code coverage reporting. NOTE: breakpoints may not work +# uncomment below to run pytest always with code coverage reporting. NOTE: breakpoints may not work # addopts = "--cov --cov-report html" [tool.codespell] @@ -80,7 +78,7 @@ skip = "htmlcov,.git,.mypy_cache,.pytest_cache,.coverage,*.pdf,*.svg,venvs,.tox, [tool.coverage.run] branch = true -source = ["src/pynwb"] +source = ["ndx_fiber_photometry"] [tool.coverage.report] exclude_lines = [ @@ -94,7 +92,7 @@ preview = true exclude = ".git|.mypy_cache|.tox|.venv|venv|.ipynb_checkpoints|_build/|dist/|__pypackages__|.ipynb|docs/" [tool.ruff] -select = ["E", "F", "T100", "T201", "T203"] +lint.select = ["E", "F", "T100", "T201", "T203"] exclude = [ ".git", ".tox", @@ -105,9 +103,9 @@ exclude = [ ] line-length = 120 -[tool.ruff.per-file-ignores] +[tool.ruff.lint.per-file-ignores] "src/pynwb/ndx_fiber_photometry/__init__.py" = ["E402", "F401"] "src/spec/create_extension_spec.py" = ["T201"] -[tool.ruff.mccabe] +[tool.ruff.lint.mccabe] max-complexity = 17 diff --git a/requirements-dev.txt b/requirements-dev.txt index d86fcdd..db12620 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -5,7 +5,7 @@ codespell==2.2.6 coverage==7.4.4 hdmf==3.14.2 hdmf-docutils==0.4.7 -nwbwidgets==0.11.3 +# nwbwidgets==0.11.3 pre-commit==3.5.0 pynwb==2.8.1 pytest==8.1.1 diff --git a/src/pynwb/ndx_fiber_photometry/__init__.py b/src/pynwb/ndx_fiber_photometry/__init__.py index 6b8b1cd..51f93e0 100644 --- a/src/pynwb/ndx_fiber_photometry/__init__.py +++ b/src/pynwb/ndx_fiber_photometry/__init__.py @@ -31,15 +31,9 @@ EdgeOpticalFilter = get_class("EdgeOpticalFilter", "ndx-fiber-photometry") FiberPhotometry = get_class("FiberPhotometry", "ndx-fiber-photometry") from .fiber_photometry import FiberPhotometryTable + FiberPhotometryResponseSeries = get_class("FiberPhotometryResponseSeries", "ndx-fiber-photometry") CommandedVoltageSeries = get_class("CommandedVoltageSeries", "ndx-fiber-photometry") - -# NOTE: `widgets/tetrode_series_widget.py` adds a "widget" -# attribute to the TetrodeSeries class. This attribute is used by NWBWidgets. -# Delete the `widgets` subpackage or the `tetrode_series_widget.py` module -# if you do not want to define a custom widget for your extension neurodata -# type. - # Remove these functions from the package del load_namespaces, get_class diff --git a/src/pynwb/ndx_fiber_photometry/fiber_photometry.py b/src/pynwb/ndx_fiber_photometry/fiber_photometry.py index 45192d7..3a96e26 100644 --- a/src/pynwb/ndx_fiber_photometry/fiber_photometry.py +++ b/src/pynwb/ndx_fiber_photometry/fiber_photometry.py @@ -3,13 +3,15 @@ FiberPhotometryTable = get_class("FiberPhotometryTable", "ndx-fiber-photometry") + @docval( - {'name': 'region', 'type': list, 'doc': 'the indices of the FiberPhotometryTable'}, - {'name': 'description', 'type': str, 'doc': 'a brief description of what these table entries represent'}, + {"name": "region", "type": list, "doc": "the indices of the FiberPhotometryTable"}, + {"name": "description", "type": str, "doc": "a brief description of what these table entries represent"}, ) def create_fiber_photometry_table_region(self, **kwargs): - region, description = popargs('region', 'description', kwargs) - name = 'fiber_photometry_table_region' + region, description = popargs("region", "description", kwargs) + name = "fiber_photometry_table_region" return super(FiberPhotometryTable, self).create_region(name=name, region=region, description=description) -FiberPhotometryTable.create_fiber_photometry_table_region = create_fiber_photometry_table_region \ No newline at end of file + +FiberPhotometryTable.create_fiber_photometry_table_region = create_fiber_photometry_table_region diff --git a/src/pynwb/ndx_fiber_photometry/widgets/README.md b/src/pynwb/ndx_fiber_photometry/widgets/README.md deleted file mode 100644 index cf51d8e..0000000 --- a/src/pynwb/ndx_fiber_photometry/widgets/README.md +++ /dev/null @@ -1,6 +0,0 @@ -Add widgets that define custom visualizations for your extension, so that -the visualizations can be displayed with -[nwbwidgets](https://github.com/NeurodataWithoutBorders/nwbwidgets). - -You will also need to update the `vis_spec` dictionary in `__init__.py` so that -nwbwidgets can find your custom visualizations. \ No newline at end of file diff --git a/src/pynwb/ndx_fiber_photometry/widgets/__init__.py b/src/pynwb/ndx_fiber_photometry/widgets/__init__.py deleted file mode 100644 index f05d016..0000000 --- a/src/pynwb/ndx_fiber_photometry/widgets/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# This module is imported by nwbwidgets when -# nwbwidgets.load_extension_widgets_into_spec([ndx_fiber_photometry]) -# is called. Otherwise, the module is not imported unless explicitly imported. - -from .tetrode_series_widget import TetrodeSeriesWidget -from .. import TetrodeSeries - -vis_spec = { - TetrodeSeries: TetrodeSeriesWidget, -} diff --git a/src/pynwb/ndx_fiber_photometry/widgets/tetrode_series_widget.py b/src/pynwb/ndx_fiber_photometry/widgets/tetrode_series_widget.py deleted file mode 100644 index 2bbaa67..0000000 --- a/src/pynwb/ndx_fiber_photometry/widgets/tetrode_series_widget.py +++ /dev/null @@ -1,49 +0,0 @@ -# If NWBWidgets is installed, create a custom widget for the TetrodeSeries -# neurodata type. -# -# You will also need to update the `vis_spec` dictionary in `__init__.py` so -# that nwbwidgets can find your custom visualizations. -# -# Example usage: -# from nwbwidgets import nwb2widget, load_extension_widgets_into_spec -# load_extension_widgets_into_spec([ndx_fiber_photometry]) -# nwb2widget(nwbfile) - -try: - from nwbwidgets.ecephys import ElectricalSeriesWidget - from ipywidgets import widgets - - from .. import TetrodeSeries - - # TODO define your own custom widget for your extension neurodata type - # using TetrodeSeriesWidget as an example. - class TetrodeSeriesWidget(ElectricalSeriesWidget): # this is an HBox - """Show the trode_id above the ElectricalSeries widget""" - - def __init__(self, tetrode_series: TetrodeSeries, **kwargs): - super().__init__(electrical_series=tetrode_series, **kwargs) - vbox = widgets.VBox( - children=[ - self._create_trode_id_box(tetrode_series), - widgets.HBox(children=list(self.children)), - ] - ) - self.children = [vbox] - - def _create_trode_id_box(self, tetrode_series: TetrodeSeries): - field_lay = widgets.Layout( - max_height="40px", - max_width="600px", - min_height="30px", - min_width="130px", - ) - key = widgets.Label("trode_id:", layout=field_lay) - val = widgets.Label(str(tetrode_series.trode_id), layout=field_lay) - return widgets.HBox(children=[key, val]) - - # add the widget class to the TetrodeSeries class - TetrodeSeries.widget = TetrodeSeriesWidget - -except ImportError: - print("NWBWidgets is not installed, so we cannot create a new widget.") # noqa: T201 - print("Run `pip install nwbwidgets` to install NWBWidgets.") # noqa: T201 diff --git a/src/pynwb/tests/integration/test_fiber_photometry.py b/src/pynwb/tests/integration/test_fiber_photometry.py index 10670f2..19886bf 100644 --- a/src/pynwb/tests/integration/test_fiber_photometry.py +++ b/src/pynwb/tests/integration/test_fiber_photometry.py @@ -136,7 +136,7 @@ def test_roundtrip(self): description="emission filter for green indicator", model="emission filter model", center_wavelength_in_nm=505.0, - bandwidth_in_nm=30.0, # 505±15nm + bandwidth_in_nm=30.0, # 505±15nm filter_type="Bandpass", ) edge_optical_filter = EdgeOpticalFilter(