Skip to content

Commit

Permalink
Merge pull request #7 from effigies/chore/type-coverage
Browse files Browse the repository at this point in the history
type: Improve type coverage, pyright compatibility
  • Loading branch information
effigies authored Dec 9, 2024
2 parents 2e40bb6 + 01aa783 commit 21e076b
Show file tree
Hide file tree
Showing 7 changed files with 37 additions and 15 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,21 @@ with load_resource.as_path('resourceDir') as resource_dir:
Note that `load_resource()` is a shorthand for `load_resource.cached()`,
whose explicitness might be more to your taste.

### Type checking

Some type checkers may complain on `Loader(__package__)` because `__package__` may be `None`.
To resolve this, add `assert __package__` before the call, for example:

```python
from acres import Loader

assert __package__
load_resource = Loader(__package__)
```

This does have a runtime cost, so `# type: ignore[reportArgumentType,unused-ignore]`
can also be used to avoid incurring that, if import times are a concern.

## Interpreter-scoped resources, locally scoped loaders

`Loader.cached` uses a global cache. This ensures that cached files do not get
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ authors = [
{name = "The NiPreps Developers", email = "[email protected]"}
]
dependencies = [
"importlib_resources; python_version < '3.11'",
"importlib_resources >=5.7; python_version < '3.11'",
]
requires-python = ">=3.8"
readme = "README.md"
Expand Down
18 changes: 11 additions & 7 deletions src/acres/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@

@cache
def _cache_resource(resource: Traversable) -> Path:
return EXIT_STACK.enter_context(as_file(resource))
# PY310(importlib_resources): no-any-return, PY311+(importlib.resources): unused-ignore
return EXIT_STACK.enter_context(as_file(resource)) # type: ignore[no-any-return,unused-ignore]


class Loader:
Expand Down Expand Up @@ -170,7 +171,7 @@ def _doc(self) -> str:

return '\n'.join(doclines)

def readable(self, *segments) -> Traversable:
def readable(self, *segments: str) -> Traversable:
"""Provide read access to a resource through a Path-like interface.
This file may or may not exist on the filesystem, and may be
Expand All @@ -179,9 +180,10 @@ def readable(self, *segments) -> Traversable:
This result is not cached or copied to the filesystem in cases where
that would be necessary.
"""
return self.files.joinpath(*segments)
# PY310(importlib_resources): no-any-return, PY311+(importlib.resources): unused-ignore
return self.files.joinpath(*segments) # type: ignore[no-any-return,unused-ignore]

def as_path(self, *segments) -> AbstractContextManager[Path]:
def as_path(self, *segments: str) -> AbstractContextManager[Path]:
"""Ensure data is available as a :class:`~pathlib.Path`.
This method generates a context manager that yields a Path when
Expand All @@ -190,9 +192,10 @@ def as_path(self, *segments) -> AbstractContextManager[Path]:
This result is not cached, and any temporary files that are created
are deleted when the context is exited.
"""
return as_file(self.files.joinpath(*segments))
# PY310(importlib_resources): no-any-return, PY311+(importlib.resources): unused-ignore
return as_file(self.files.joinpath(*segments)) # type: ignore[no-any-return,unused-ignore]

def cached(self, *segments) -> Path:
def cached(self, *segments: str) -> Path:
"""Ensure data resource is available as a :class:`~pathlib.Path`.
Any temporary files that are created remain available throughout
Expand All @@ -202,6 +205,7 @@ def cached(self, *segments) -> Path:
data multiple times, but directories and their contents being
requested separately may result in some duplication.
"""
return _cache_resource(self.files.joinpath(*segments)) # type: ignore[arg-type]
# PY310(importlib_resources): unused-ignore, PY311+(importlib.resources) arg-type
return _cache_resource(self.files.joinpath(*segments)) # type: ignore[arg-type,unused-ignore]

__call__ = cached
2 changes: 1 addition & 1 deletion tests/data/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from acres import Loader

load_resource = Loader(__package__)
load_resource = Loader(__package__) # type: ignore[reportArgumentType,unused-ignore]
5 changes: 3 additions & 2 deletions tests/test_data_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from importlib_resources.abc import Traversable


def test_acres():
def test_acres() -> None:
assert isinstance(load_resource, Loader)

text_resource = load_resource.readable('text_file')
Expand Down Expand Up @@ -39,5 +39,6 @@ def test_acres():
assert load_resource.readable('text_file') is not cached_text_resource


def test_acres_docstring():
def test_acres_docstring() -> None:
assert load_resource.__doc__
assert 'text_file' in load_resource.__doc__
6 changes: 3 additions & 3 deletions tests/test_loader_instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
from . import data


def test_loader_from_module():
def test_loader_from_module() -> None:
my_loader = Loader(data)

text_resource = my_loader.readable('text_file')
assert text_resource.read_text() == 'A file with some text.\n'


def test_loader_from_name():
def test_loader_from_name() -> None:
my_loader = Loader('acres')

assert my_loader.readable('py.typed').exists()
assert my_loader.readable('py.typed').read_bytes() == b''
4 changes: 3 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,12 @@ description = Check type consistency
labels = check
deps =
mypy
pyright
importlib_resources
skip_install = true
commands =
mypy src tests
mypy --strict src tests
pyright src tests

[testenv:build{,-strict}]
labels =
Expand Down

0 comments on commit 21e076b

Please sign in to comment.