Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make widgets templates optional #93

Merged
merged 5 commits into from
Sep 9, 2024
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
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This repo provides a template for creating Neurodata Extensions (NDX) for the
[Neurodata Without Borders](https://nwb.org/)
data standard.

We currently support creating Neurodata Extensions only using Python 3.8+.
This template currently supports creating Neurodata Extensions only using Python 3.8+.
MATLAB support is in development.

## Getting started
Expand Down Expand Up @@ -59,6 +59,17 @@ By default, to aid with debugging, the project is configured NOT to run code cov

https://github.com/nwb-extensions/ndx-template/blob/11ae225b3fd3934fa3c56e6e7b563081793b3b43/%7B%7B%20cookiecutter.namespace%20%7D%7D/pyproject.toml#L82-L83

## Integrating with NWB Widgets

When answering the cookiecutter prompts, you will be asked whether you would like to create templates for integration with [NWB Widgets](https://github.com/NeurodataWithoutBorders/nwbwidgets), a library of plotting widgets for interactive visualization of NWB neurodata types within a Jupyter notebook. If you answer "yes", then an example widget and example notebook will be created for you. If you answer "no", but would like to add a widget later on, follow the instructions below:

1. Create a directory named `widgets` in `src/pynwb/{your_python_package_name}/`.
2. Copy [`__init__.py`](https://github.com/nwb-extensions/ndx-template/blob/main/%7B%7B%20cookiecutter.namespace%20%7D%7D/src/pynwb/%7B%7B%20cookiecutter.py_pkg_name%20%7D%7D/widgets/__init__.py) to that directory and adapt the contents to your extension.
3. Copy [`tetrode_series_widget.py`](https://github.com/nwb-extensions/ndx-template/blob/main/%7B%7B%20cookiecutter.namespace%20%7D%7D/src/pynwb/%7B%7B%20cookiecutter.py_pkg_name%20%7D%7D/widgets/tetrode_series_widget.py) to that directory and adapt the contents to your extension.
4. Create a directory named `notebooks` in the root of the repository.
5. Copy [example.ipynb](https://github.com/nwb-extensions/ndx-template/blob/main/%7B%7B%20cookiecutter.namespace%20%7D%7D/notebooks/example.ipynb) to that directory and adapt the contents to your extension.
6. Add `nwbwidgets` to `requirements-dev.txt`.

## Maintainers
- [@rly](https://github.com/rly)
- [@oruebel](https://github.com/oruebel)
Expand Down
4 changes: 3 additions & 1 deletion cookiecutter.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"license": ["BSD-3", "MIT", "Apache Software License 2.0", "Other"],
"py_pkg_name": "{{ cookiecutter.namespace|replace('-', '_') }}",
"initialize_git": true,
"widgets": false,
"_extensions": ["local_extensions.ZipExtension"],
"__prompts__": {
"namespace": "Select a name for your extension. It must start with 'ndx-'",
Expand All @@ -22,6 +23,7 @@
"release": "Select an initial release level",
"license": "Select a license",
"py_pkg_name": "Select a name for the Python package",
"initialize_git": "Initialize a git repository?"
"initialize_git": "Initialize a git repository?",
"widgets": "Create templates for integration with NWB Widgets (interactive visualization)?"
}
}
19 changes: 19 additions & 0 deletions hooks/post_gen_project.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from hdmf_docutils.init_sphinx_extension_doc import main as init_sphinx_extension_doc
import shutil
from subprocess import check_call
import sys

Expand Down Expand Up @@ -43,11 +44,29 @@ def _initialize_git():
check_call(["git", "branch", "-M", "main"])


def _remove_widget_files():
# The template contains example files for NWB Widgets integration. Many extension creators
# do not plan to add widgets, so these files add clutter and potential confusion. If the
# user specifies that they do not want to add widgets, remove these files from the template.
# This is easier than adding the files only if they want to add widgets.
dirs_to_remove = {
"./notebooks", # currently contains only widget demo -- be more specific if others exist
"src/pynwb/{{ cookiecutter.py_pkg_name }}/widgets"
}
for path in dirs_to_remove:
print(f"Deleting directory {path}")
shutil.rmtree(path)


def main():
"""Run the post gen project hook main entry point."""

if "{{ cookiecutter.widgets }}" == "True":
_remove_widget_files()

_generate_doc()
_create_extension_spec()

if "{{ cookiecutter.initialize_git }}" == "True":
_initialize_git()

Expand Down
20 changes: 20 additions & 0 deletions tests/test_bake_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,26 @@ def test_bake_project_extra(cookies):
_check_gen_files(result.project_path, "ndx-test")


def test_bake_project_widgets(cookies):
"""Test evaluating the template with widgets."""
result = cookies.bake(extra_context={"widgets": "yes"})

assert result.exit_code == 0
assert result.exception is None

for expected_file in [
"notebooks/example.ipynb",
"src/pynwb/ndx_my_namespace/widgets/__init__.py",
"src/pynwb/ndx_my_namespace/widgets/tetrode_series_widget.py",
"src/pynwb/ndx_my_namespace/widgets/README.md",
]:
expected_file = os.path.join(result.project_path, expected_file)
assert os.path.exists(expected_file), f"Missing file: {expected_file}"

with open(expected_file, "r") as fp:
assert fp.read().strip() != "", f"Empty file: {expected_file}"


def _check_gen_files(project_dir: str, namespace: str):
"""Test that the correct files are generated after the template is evaluated."""
for expected_file in [
Expand Down
11 changes: 7 additions & 4 deletions {{ cookiecutter.namespace }}/NEXTSTEPS.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ and any other packages required to develop, document, and run your extension.

6. Define API classes for your new extension data types.

- As a starting point, `src/pynwb/__init__.py` includes an example for how to use
- As a starting point, `src/pynwb/{{ cookiecutter.py_pkg_name }}/__init__.py` includes an
example for how to use
the `pynwb.get_class` to generate a basic Python class for your new extension data
type. This class contains a constructor and properties for the new data type.
- Instead of using `pynwb.get_class`, you can define your own custom class for the
Expand All @@ -28,7 +29,8 @@ and any other packages required to develop, document, and run your extension.
[Extending NWB tutorial](https://pynwb.readthedocs.io/en/stable/tutorials/general/extensions.html)
for more details.

7. Define tests for your new extension data types in `src/pynwb/tests` or `src/matnwb/tests`.
7. Define tests for your new extension data types in
`src/pynwb/{{ cookiecutter.py_pkg_name }}/tests` or `src/matnwb/tests`.
A test for the example `TetrodeSeries` data type is provided as a reference and should be
replaced or removed.

Expand All @@ -49,9 +51,10 @@ replaced or removed.
)

7. (Optional) Define custom visualization widgets for your new extension data types in
`src/pynwb/widgets` so that the visualizations can be displayed with
`src/pynwb/{{ cookiecutter.py_pkg_name }}/widgets` 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
You will also need to update the `vis_spec` dictionary in
`src/pynwb/{{ cookiecutter.py_pkg_name }}/widgets/__init__.py` so that
nwbwidgets can find your custom visualizations.

8. You may need to modify `pyproject.toml` and re-run `python -m pip install -e .` if you
Expand Down
2 changes: 1 addition & 1 deletion {{ cookiecutter.namespace }}/requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ codespell==2.3.0
coverage==7.5.4
hdmf==3.14.1
hdmf-docutils==0.4.7
nwbwidgets==0.11.3
{%- if cookiecutter.widgets -%}nwbwidgets==0.11.3{% endif %}
pre-commit==3.5.0 # latest pre-commit does not support py3.8
pynwb==2.8.0
pytest==8.2.2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,5 @@
# `@register_class("TetrodeSeries", "{{ cookiecutter.namespace }}")`
TetrodeSeries = get_class("TetrodeSeries", "{{ cookiecutter.namespace }}")

# 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
Loading