Skip to content

Commit

Permalink
Merge branch 'main' into fix_editable
Browse files Browse the repository at this point in the history
  • Loading branch information
nclack authored Jul 6, 2023
2 parents 7004de3 + 520ddfb commit 2637b27
Show file tree
Hide file tree
Showing 18 changed files with 72 additions and 59 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ jobs:
- name: Install
run: |
python -m pip install --upgrade pip
python -m pip install -e ./napari-from-github
python -m pip install -e ./napari-from-github -c "./napari-from-github/resources/constraints/constraints_py3.10.txt"
python -m pip install -e .[json]
# bare minimum required to test napari/plugins
python -m pip install pytest scikit-image[data] zarr xarray hypothesis matplotlib
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ wheels/
*.egg-info/
.installed.cfg
*.egg
.DS_Store

# PyInstaller
# Usually these files are written by a python script from a template
Expand Down Expand Up @@ -100,6 +101,7 @@ ENV/

# mypy
.mypy_cache/
.ruff_cache/

# IDE settings
.vscode/
Expand Down
10 changes: 0 additions & 10 deletions MANIFEST.in

This file was deleted.

4 changes: 2 additions & 2 deletions _docs/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,9 @@ def example_contribution(
if format == "yaml":
return yaml.safe_dump(output, sort_keys=False)
if format == "toml":
import pytomlpp as toml
import tomli_w

return toml.dumps(output)
return tomli_w.dumps(output)
if format == "json":
return json.dumps(output)
raise ValueError("Invalid format: {format}. Must be 'yaml', 'toml' or 'json'.")
Expand Down
4 changes: 2 additions & 2 deletions _docs/templates/_npe2_widgets_guide.md.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ specification:
from qtpy.QtWidgets import QWidget

class MyPluginWidget(QWidget):
def __init__(self, viewer: 'napari.viewer.Viewer'):
super().__init__()
def __init__(self, viewer: 'napari.viewer.Viewer', parent=None):
super().__init__(parent)
self._viewer = viewer
```

Expand Down
2 changes: 1 addition & 1 deletion _docs/templates/_npe2_writers_guide.md.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ by the corresponding writer contribution in the manifest).
```python
DataType = Any # usually something like a numpy array, but varies by layer
LayerAttributes = dict
LayerName = Literal["image", "labels", "points", "shapes", "surface", "tracks", "vectors"]
LayerName = Literal["graph", "image", "labels", "points", "shapes", "surface", "tracks", "vectors"]
FullLayerData = Tuple[DataType, LayerAttributes, LayerName]
MultiWriterFunction = Callable[[str, List[FullLayerData]], List[str]]
```
Expand Down
28 changes: 8 additions & 20 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
# pyproject.toml
[build-system]
requires = ["setuptools>=45", "wheel", "setuptools_scm>=6.2"]
build-backend = "setuptools.build_meta"
requires = ["hatchling", "hatch-vcs"]
build-backend = "hatchling.build"

[tool.hatch.version]
source = "vcs"

# https://peps.python.org/pep-0621/
[project]
Expand Down Expand Up @@ -33,7 +36,8 @@ dependencies = [
"build",
"psygnal>=0.3.0",
"pydantic<2",
"pytomlpp",
"tomli-w",
"tomli; python_version < '3.11'",
"rich",
"typer",
]
Expand Down Expand Up @@ -72,25 +76,9 @@ npe2 = "npe2._pytest_plugin"
[project.entry-points."setuptools.finalize_distribution_options"]
finalize_npe2 = "npe2._setuptools_plugin:finalize_npe2"

[tool.setuptools]
package-dir = { "" = "src" }
include-package-data = true

[tool.setuptools.packages.find]
where = ["src"]

[tool.setuptools.package-data]
"*" = ["*.pyi", "py.typed"]

[tool.setuptools_scm]
write_to = "src/npe2/_version.py"

[tool.check-manifest]
ignore = [
"src/npe2/_version.py",
".pre-commit-config.yaml",
".github_changelog_generator",
]
ignore = []

[tool.pytest.ini_options]
filterwarnings = ["error:::npe2"]
Expand Down
6 changes: 4 additions & 2 deletions src/npe2/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from importlib.metadata import PackageNotFoundError, version

try:
from ._version import version as __version__
except ImportError:
__version__ = version("npe2")
except PackageNotFoundError: # pragma: no cover
__version__ = "unknown"
__author__ = "Talley Lambert"
__email__ = "talley.lambert@gmail.com"
Expand Down
2 changes: 1 addition & 1 deletion src/npe2/_dynamic_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def cleanup(self) -> None:
self.plugin_manager.unregister(self.manifest.name)

def register(self) -> None:
"""Remove this plugin from its plugin manager."""
"""Register this plugin with its plugin manager."""
self.plugin_manager.register(self.manifest)

def clear(self) -> None:
Expand Down
12 changes: 0 additions & 12 deletions src/npe2/_inspection/_setuputils.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,18 +86,6 @@ def get_package_dir_info(path: Union[Path, str]) -> PackageInfo:
EntryPoint(name.strip(), value.strip(), group)
)

# # check for pyproject.toml
# pyproject_toml = path / "pyproject.toml"
# if pyproject_toml.exists():
# info.pyproject_toml = pyproject_toml
# with open(pyproject_toml, "r") as f:
# data = pytomlpp.load(f)
# if project := data.get("project"):
# info.package_name = project.get("name", "")
# for group, ep in project.get('entry-points', {}).items():
# for name, value in ep.items():
# info.entry_points.append(EntryPoint(name, value, group))

return info


Expand Down
3 changes: 3 additions & 0 deletions src/npe2/_inspection/_visitors.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,9 @@ def napari_get_writer(self, node: ast.FunctionDef):
# we can't convert this to an npe2 command contribution
pass # pragma: no cover

def napari_write_graph(self, node: ast.FunctionDef):
self._parse_writer(node, "graph") # pragma: no cover

def napari_write_image(self, node: ast.FunctionDef):
self._parse_writer(node, "image")

Expand Down
7 changes: 5 additions & 2 deletions src/npe2/_setuptools_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,12 @@ def trace(*k: object) -> None:


def _lazy_tomli_load(data: str) -> dict[str, Any]:
from pytomlpp import loads
try:
import tomllib
except ImportError:
import tomli as tomllib # type: ignore [no-redef]

return loads(data)
return tomllib.loads(data)


def _read_dist_name_from_setup_cfg() -> str | None:
Expand Down
2 changes: 1 addition & 1 deletion src/npe2/io_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def _read(
_pm = PluginManager.instance()

for rdr in _pm.iter_compatible_readers(paths):
if plugin_name and not rdr.command.startswith(plugin_name):
if plugin_name and rdr.plugin_name != plugin_name:
continue
read_func = rdr.exec(
kwargs={"path": paths, "stack": stack, "_registry": _pm.commands}
Expand Down
14 changes: 10 additions & 4 deletions src/npe2/manifest/_bases.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from pathlib import Path
from typing import Callable, Dict, Optional, Union

import pytomlpp as toml
import yaml
from pydantic import BaseModel, PrivateAttr

Expand All @@ -30,10 +29,12 @@ def toml(self, pyproject=False, **kwargs) -> str:
**kwargs
passed to `BaseModel.json()`
"""
import tomli_w

d = self._serialized_data(**kwargs)
if pyproject:
d = {"tool": {"napari": d}}
return toml.dumps(d)
return tomli_w.dumps(d)

def yaml(self, **kwargs) -> str:
"""Generate serialized `yaml` string for this model.
Expand Down Expand Up @@ -74,13 +75,18 @@ def from_file(cls, path: Union[Path, str]):
if path.suffix.lower() == ".json":
loader = json.load
elif path.suffix.lower() == ".toml":
loader = toml.load
try:
import tomllib
except ImportError:
import tomli as tomllib # type: ignore [no-redef]

loader = tomllib.load
elif path.suffix.lower() in (".yaml", ".yml"):
loader = yaml.safe_load
else:
raise ValueError(f"unrecognized file extension: {path}") # pragma: no cover

with open(path, encoding="utf-8") as f:
with open(path, mode="rb") as f:
data = loader(f) or {}

if path.name == "pyproject.toml":
Expand Down
1 change: 1 addition & 0 deletions src/npe2/manifest/contributions/_writers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@


class LayerType(str, Enum):
graph = "graph"
image = "image"
labels = "labels"
points = "points"
Expand Down
2 changes: 1 addition & 1 deletion src/npe2/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def __array__(self) -> "np.ndarray":


LayerName = Literal[
"image", "labels", "points", "shapes", "surface", "tracks", "vectors"
"graph", "image", "labels", "points", "shapes", "surface", "tracks", "vectors"
]
Metadata = Dict
DataType = Union[ArrayLike, Sequence[ArrayLike]]
Expand Down
1 change: 1 addition & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ class HookSpecs:
def napari_provide_sample_data(): ... # type: ignore
def napari_get_reader(path): ...
def napari_get_writer(path, layer_types): ...
def napari_write_graph(path, data, meta): ...
def napari_write_image(path, data, meta): ...
def napari_write_labels(path, data, meta): ...
def napari_write_points(path, data, meta): ...
Expand Down
29 changes: 29 additions & 0 deletions tests/test__io_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,35 @@ def test_read_with_no_plugin():
read(["some.nope"], stack=False)


def test_read_uses_correct_passed_plugin(tmp_path):
pm = PluginManager()
long_name = "gooby-again"
short_name = "gooby"
long_name_plugin = DynamicPlugin(long_name, plugin_manager=pm)
short_name_plugin = DynamicPlugin(short_name, plugin_manager=pm)

path = "something.fzzy"
mock_file = tmp_path / path
mock_file.touch()

@long_name_plugin.contribute.reader(filename_patterns=["*.fzzy"])
def get_read_long(path=mock_file):
raise ValueError(
f"Uhoh, {long_name} was chosen, but given plugin was {short_name}"
)

@short_name_plugin.contribute.reader(filename_patterns=["*.fzzy"])
def get_read(path=mock_file):
def read(paths):
return [(None,)]

return read

# "gooby-again" isn't used even though given plugin starts with the same name
# if an error is thrown here, it means we selected the wrong plugin
io_utils._read(["some.fzzy"], plugin_name=short_name, stack=False, _pm=pm)


def test_read_return_reader(uses_sample_plugin):
data, reader = read_get_reader("some.fzzy")
assert data == [(None,)]
Expand Down

0 comments on commit 2637b27

Please sign in to comment.