Skip to content

Commit

Permalink
Add import compat layer to __init__ (#24)
Browse files Browse the repository at this point in the history
Co-authored-by: Sviatoslav Sydorenko (Святослав Сидоренко) <[email protected]>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Oct 6, 2024
1 parent 8a68f5a commit a68b078
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,6 @@ coverage:
typing:
flags:
- MyPy
target: 70% # 100%
target: 68.5% # 100%

...
1 change: 0 additions & 1 deletion CHANGES/19.breaking.rst

This file was deleted.

3 changes: 3 additions & 0 deletions CHANGES/19.packaging.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Moved :func:`propcache.api.under_cached_property` and :func:`propcache.api.cached_property` to ``propcache.api`` -- by :user:`bdraco`.

Both decorators remain importable from the top-level package, however importing from `propcache.api` is now the recommended way to use them.
1 change: 1 addition & 0 deletions CHANGES/24.packaging.rst
28 changes: 26 additions & 2 deletions src/propcache/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,30 @@
"""propcache: An accelerated property cache for Python classes."""

__version__ = "1.0.0.dev0"
from typing import TYPE_CHECKING, List

__version__ = "0.2.0.dev0"

# Imports have moved to `propcache.api` in 1.0.0+.
__all__ = ()
# This module is now a facade for the API.
if TYPE_CHECKING:
from .api import cached_property, under_cached_property

__all__ = ("cached_property", "under_cached_property")


def _import_facade(attr: str) -> object:
"""Import the public API from the `api` module."""
if attr in __all__:
from . import api # pylint: disable=import-outside-toplevel

return getattr(api, attr)
raise AttributeError(f"module 'propcache' has no attribute '{attr}'")


def _dir_facade() -> List[str]:
"""Include the public API in the module's dir() output."""
return [*__all__, *globals().keys()]


__getattr__ = _import_facade
__dir__ = _dir_facade
45 changes: 45 additions & 0 deletions tests/test_init.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"""Test imports can happen from top-level."""

import pytest

import propcache
from propcache import _helpers


def test_api_at_top_level():
"""Verify the public API is accessible at top-level."""
assert propcache.cached_property is not None
assert propcache.under_cached_property is not None
assert propcache.cached_property is _helpers.cached_property
assert propcache.under_cached_property is _helpers.under_cached_property


@pytest.mark.parametrize(
"prop_name",
("cached_property", "under_cached_property"),
)
@pytest.mark.parametrize(
"api_list", (dir(propcache), propcache.__all__), ids=("dir", "__all__")
)
def test_public_api_is_in_inspectable_object_lists(prop_name, api_list):
"""Verify the public API is discoverable programmatically.
This checks for presence of known public decorators module's
``__all__`` and ``dir()``.
"""
assert prop_name in api_list


def test_importing_invalid_attr_raises():
"""Verify importing an invalid attribute raises an AttributeError."""
match = r"^module 'propcache' has no attribute 'invalid_attr'$"
with pytest.raises(AttributeError, match=match):
propcache.invalid_attr


def test_import_error_invalid_attr():
"""Verify importing an invalid attribute raises an ImportError."""
# No match here because the error is raised by the import system
# and may vary between Python versions.
with pytest.raises(ImportError):
from propcache import invalid_attr # noqa: F401

0 comments on commit a68b078

Please sign in to comment.