diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..6113940 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,95 @@ +name: CI +on: + pull_request: + push: + branches: + - main + +# Automatically stop old builds on the same branch/PR +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + setup: + name: Setup workflow + runs-on: ubuntu-latest + permissions: + pull-requests: read + outputs: + code_change: ${{ steps.filter.outputs.code }} + matrix: ${{ env.MATRIX }} + steps: + - uses: actions/checkout@v4 + if: github.event_name != 'pull_request' + + - name: Check for code changes + uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + code: + - 'src/**' + - 'pyproject.toml' + - '.github/workflows/ci.yaml' + + - name: Set matrix option + run: | + if [[ '${{ github.event_name }}' == 'workflow_dispatch' ]]; then + OPTION=${{ github.event.inputs.target }} + elif [[ '${{ github.event_name }}' == 'schedule' ]]; then + OPTION="full" + elif [[ '${{ github.event_name }}' == 'push' && '${{ github.ref_type }}' == 'tag' ]]; then + OPTION="full" + else + OPTION="default" + fi + echo "MATRIX_OPTION=$OPTION" >> $GITHUB_ENV + + - name: Set test matrix with 'default' option + if: env.MATRIX_OPTION == 'default' + run: | + MATRIX=$(jq -nsc '{ + "os": ["ubuntu-latest", "macos-latest", "windows-latest"], + "environment": ["test-310", "test-312"], + }') + echo "MATRIX=$MATRIX" >> $GITHUB_ENV + + - name: Set test matrix with 'full' option + if: env.MATRIX_OPTION == 'full' + run: | + MATRIX=$(jq -nsc '{ + "os": ["ubuntu-latest", "macos-latest", "windows-latest"], + "environment": ["test-310", "test-311", "test-312"] + }') + echo "MATRIX=$MATRIX" >> $GITHUB_ENV + + - name: Set test matrix with 'downstream' option + if: env.MATRIX_OPTION == 'downstream' + run: | + MATRIX=$(jq -nsc '{ + "os": ["ubuntu-latest"], + "environment": ["test-311"] + }') + echo "MATRIX=$MATRIX" >> $GITHUB_ENV + + pixi_lock: + name: Pixi lock + runs-on: ubuntu-latest + steps: + - uses: holoviz-dev/holoviz_tasks/pixi_lock@v0 + with: + cache: ${{ github.event.inputs.cache == 'true' || github.event.inputs.cache == '' }} + + pre-commit: + timeout-minutes: 30 + runs-on: ubuntu-latest + steps: + - name: Checkout branch + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - name: Set up pixi + uses: prefix-dev/setup-pixi@ba3bb36eb2066252b2363392b7739741bb777659 # v0.8.1 + with: + environments: default lint + - name: pre-commit + run: pixi run pre-commit-run --color=always --show-diff-on-failure diff --git a/.gitignore b/.gitignore index e7f1fde..38528a4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ .venv .pyc -.env \ No newline at end of file +.env diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..1025719 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,31 @@ +default_stages: [pre-commit] +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: check-builtin-literals + - id: check-case-conflict + - id: check-docstring-first + - id: check-executables-have-shebangs + - id: check-toml + - id: check-json + - id: detect-private-key + - id: end-of-file-fixer + exclude: \.min\.js$ + - id: trailing-whitespace + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.7.0 + hooks: + - id: ruff + files: src/panel_neuroglancer + - repo: https://github.com/codespell-project/codespell + rev: v2.3.0 + hooks: + - id: codespell + additional_dependencies: + - tomli + - repo: https://github.com/shssoichiro/oxipng + rev: v8.0.0 + hooks: + - id: oxipng + stages: [manual] diff --git a/pixi.toml b/pixi.toml new file mode 100644 index 0000000..c016ec1 --- /dev/null +++ b/pixi.toml @@ -0,0 +1,63 @@ +[project] +name = "panel-neuroglancer" +channels = ["conda-forge"] +platforms = ["osx-arm64", "osx-64", "linux-64", "win-64"] + +[tasks] +postinstall = "pip install --no-build-isolation --no-deps --disable-pip-version-check -e ." + +[dependencies] +python = ">=3.10" +panel = ">=1.5.4" +packaging = "*" + +[pypi-dependencies] +neuroglancer = ">=2.40.1" + +[host-dependencies] +pip = "*" +setuptools = ">=61" +setuptools-scm = "*" +hatchling = "*" +hatch-vcs = "*" + +[feature.test.dependencies] +pytest = ">=6" +pytest-cov = "*" +pytest-rerunfailures = "*" +pytest-xdist = "*" +mypy = "*" +[feature.test.tasks] +test = "pytest" +test-coverage = "pytest --cov=panel_neuroglancer--cov-report=xml --cov-report=term-missing" + +[feature.build.dependencies] +python-build = "*" +twine = "*" +wheel = "*" + +[feature.build.tasks] +build-wheel = "python -m build --no-isolation ." +check-wheel = "twine check dist/*" + +[feature.lint.dependencies] +pre-commit = "*" + +[feature.lint.tasks] +pre-commit-install = "pre-commit install" +pre-commit-run = "pre-commit run -a" + +[feature.py310.dependencies] +python = "3.10.*" +[feature.py311.dependencies] +python = "3.11.*" +[feature.py312.dependencies] +python = "3.12.*" + +[environments] +default = ["test"] +py310 = ["py310", "test"] +py311 = ["py311", "test"] +py312 = ["py312", "test"] +build = ["build"] +lint = { features = ["lint"], no-default-feature = true } diff --git a/pyproject.toml b/pyproject.toml index dea9d5d..78c5a18 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,91 @@ +[build-system] +requires = ["hatchling", "hatch-vcs", "panel>=1.5.4", "packaging"] +build-backend = "hatchling.build" + [project] name = "panel-neuroglancer" -version = "0.1.0" -description = "Add your description here" +dynamic = ["version"] +description = "Volumetric data viewer built on HoloViz Panel and Neuroglancer" readme = "README.md" requires-python = ">=3.10" dependencies = [ "neuroglancer>=2.40.1", "panel>=1.5.4", ] +classifiers = [ + "License :: OSI Approved :: BSD License", + "Development Status :: 5 - Production/Stable", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Operating System :: OS Independent", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "Natural Language :: English", + "Topic :: Scientific/Engineering", + "Topic :: Software Development :: Libraries", +] + +[project.urls] +Homepage = "https://github.com/panel-extensions/panel-neuroglancer" +Source = "https://github.com/panel-extensions/panel-neuroglancer" + +[tool.ruff] +exclude = [ + ".git", + "__pycache__", + ".tox", + ".eggs", + "*.egg", + "doc", + "dist", + "build", + "_build", + "examples", + ".ipynb_checkpoints", + "node_modules", +] +line-length = 165 +fix = true + + +[tool.ruff.lint] +ignore = [ + "E402", # Module level import not at top of file + "E712", # Avoid equality comparisons to True + "E731", # Do not assign a lambda expression, use a def + "N803", # Argument name should be lowercase + "N806", # Variable name should be lowercase +] +select = [ + "B", # flake8-bugbear + "E", # pycodestyle errors + "F", # pyflakes + "W", # pycodestyle warnings + "I", # isort + "PIE", + "T20", + "RUF006", + "UP004", + "UP006", + "UP020", + "UP028", + "UP030", + "UP031", + "UP032", + "UP034", + "UP036", +] + +[tool.hatch.version] +source = "vcs" +raw-options = { version_scheme = "no-guess-dev" } + +[tool.isort] +force_grid_wrap = 4 +multi_line_output = 5 +combine_as_imports = true +lines_between_types = 1 +include_trailing_comma = true diff --git a/src/panel_neuroglancer/neuroglancer.py b/src/panel_neuroglancer/neuroglancer.py index 873e22d..c7ab2ae 100644 --- a/src/panel_neuroglancer/neuroglancer.py +++ b/src/panel_neuroglancer/neuroglancer.py @@ -3,11 +3,9 @@ import neuroglancer import panel as pn import param - from neuroglancer.viewer import Viewer from panel.custom import PyComponent - DEMO_URL = "https://neuroglancer-demo.appspot.com/#!%7B%22dimensions%22%3A%7B%22x%22%3A%5B6.000000000000001e-9%2C%22m%22%5D%2C%22y%22%3A%5B6.000000000000001e-9%2C%22m%22%5D%2C%22z%22%3A%5B3.0000000000000004e-8%2C%22m%22%5D%7D%2C%22position%22%3A%5B5029.42333984375%2C6217.5849609375%2C1182.5%5D%2C%22crossSectionScale%22%3A3.7621853549999242%2C%22projectionOrientation%22%3A%5B-0.05179581791162491%2C-0.8017329573631287%2C0.0831851214170456%2C-0.5895944833755493%5D%2C%22projectionScale%22%3A4699.372698097029%2C%22layers%22%3A%5B%7B%22type%22%3A%22image%22%2C%22source%22%3A%22precomputed%3A%2F%2Fgs%3A%2F%2Fneuroglancer-public-data%2Fkasthuri2011%2Fimage%22%2C%22tab%22%3A%22source%22%2C%22name%22%3A%22original-image%22%7D%2C%7B%22type%22%3A%22image%22%2C%22source%22%3A%22precomputed%3A%2F%2Fgs%3A%2F%2Fneuroglancer-public-data%2Fkasthuri2011%2Fimage_color_corrected%22%2C%22tab%22%3A%22source%22%2C%22name%22%3A%22corrected-image%22%7D%2C%7B%22type%22%3A%22segmentation%22%2C%22source%22%3A%22precomputed%3A%2F%2Fgs%3A%2F%2Fneuroglancer-public-data%2Fkasthuri2011%2Fground_truth%22%2C%22tab%22%3A%22source%22%2C%22selectedAlpha%22%3A0.63%2C%22notSelectedAlpha%22%3A0.14%2C%22segments%22%3A%5B%223208%22%2C%224901%22%2C%2213%22%2C%224965%22%2C%224651%22%2C%222282%22%2C%223189%22%2C%223758%22%2C%2215%22%2C%224027%22%2C%223228%22%2C%22444%22%2C%223207%22%2C%223224%22%2C%223710%22%5D%2C%22name%22%3A%22ground_truth%22%7D%5D%2C%22layout%22%3A%224panel%22%7D" @@ -104,7 +102,7 @@ def _load_state_from_url(self, url): new_state = self._parse_state_from_url(url) self.viewer.set_state(new_state) except Exception as e: - print(f"Error loading Neuroglancer state: {e}") + print(f"Error loading Neuroglancer state: {e}") # noqa def _parse_state_from_url(self, url): return neuroglancer.parse_url(url)