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

type: Improve type coverage, pyright compatibility #7

Merged
merged 6 commits into from
Dec 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
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
Loading