Skip to content

Commit

Permalink
fix handling of undefined type annotations when from __future__ impor…
Browse files Browse the repository at this point in the history
…t annotations is on
  • Loading branch information
karlicoss committed Oct 19, 2023
1 parent 65140ba commit 2abd77b
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 6 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ Cachew gives the best of two worlds and makes it both **easy and efficient**. Th
- first your objects get [converted](src/cachew/marshall/cachew.py#L34) into a simpler JSON-like representation
- after that, they are mapped into byte blobs via [`orjson`](https://github.com/ijl/orjson).

When the function is called, cachew [computes the hash of your function's arguments ](src/cachew/__init__.py:#L511)
When the function is called, cachew [computes the hash of your function's arguments ](src/cachew/__init__.py:#L589)
and compares it against the previously stored hash value.

- If they match, it would deserialize and yield whatever is stored in the cache database
Expand All @@ -145,7 +145,7 @@ and compares it against the previously stored hash value.

* primitive: `str`, `int`, `float`, `bool`, `datetime`, `date`, `Exception`

See [tests.test_types](src/cachew/tests/test_cachew.py#L697), [tests.test_primitive](src/cachew/tests/test_cachew.py#L731), [tests.test_dates](src/cachew/tests/test_cachew.py#L651), [tests.test_exceptions](src/cachew/tests/test_cachew.py#L1101)
See [tests.test_types](src/cachew/tests/test_cachew.py#L697), [tests.test_primitive](src/cachew/tests/test_cachew.py#L731), [tests.test_dates](src/cachew/tests/test_cachew.py#L651), [tests.test_exceptions](src/cachew/tests/test_cachew.py#L1127)
* [@dataclass and NamedTuple](src/cachew/tests/test_cachew.py#L613)
* [Optional](src/cachew/tests/test_cachew.py#L515) types
* [Union](src/cachew/tests/test_cachew.py#L837) types
Expand All @@ -165,7 +165,7 @@ You can find some of my performance tests in [benchmarks/](benchmarks) dir, and


# Using
See [docstring](src/cachew/__init__.py#L290) for up-to-date documentation on parameters and return types.
See [docstring](src/cachew/__init__.py#L296) for up-to-date documentation on parameters and return types.
You can also use [extensive unit tests](src/cachew/tests/test_cachew.py) as a reference.

Some useful (but optional) arguments of `@cachew` decorator:
Expand Down Expand Up @@ -271,7 +271,7 @@ Now you can use `@mcachew` in place of `@cachew`, and be certain things don't br
## Settings


[cachew.settings](src/cachew/__init__.py#L66) exposes some parameters that allow you to control `cachew` behaviour:
[cachew.settings](src/cachew/__init__.py#L67) exposes some parameters that allow you to control `cachew` behaviour:
- `ENABLE`: set to `False` if you want to disable caching for without removing the decorators (useful for testing and debugging).
You can also use [cachew.extra.disabled_cachew](src/cachew/extra.py#L21) context manager to do it temporarily.
- `DEFAULT_CACHEW_DIR`: override to set a different base directory. The default is the "user cache directory" (see [appdirs docs](https://github.com/ActiveState/appdirs#some-example-output)).
Expand Down
7 changes: 6 additions & 1 deletion src/cachew/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,12 @@ def infer_return_type(func) -> Union[Failure, Inferred]:
>>> infer_return_type(unsupported_list)
"can't infer type from typing.List[cachew.Custom]: can't cache <class 'cachew.Custom'>"
"""
hints = get_type_hints(func)
try:
hints = get_type_hints(func)
except Exception as ne:
# get_type_hints might fail if types are forward defined or missing
# see test_future_annotation for an example
return str(ne)
rtype = hints.get('return', None)
if rtype is None:
return f"no return type annotation on {func}"
Expand Down
2 changes: 1 addition & 1 deletion src/cachew/logging_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ def get_enlighten():
return Mock()

try:
import enlighten # type: ignore[import]
import enlighten # type: ignore[import-untyped]
except ModuleNotFoundError:
warnings.warn("You might want to 'pip install enlighten' for a nice progress bar")

Expand Down
26 changes: 26 additions & 0 deletions src/cachew/tests/test_cachew.py
Original file line number Diff line number Diff line change
Expand Up @@ -996,6 +996,32 @@ def orig2():
assert list(fun()) == [123]


@pytest.mark.parametrize('throw', [False, True])
def test_future_annotations(tmp_path: Path, throw: bool) -> None:
"""
this will work in runtime without cachew if from __future__ import annotations is used
so should work with cachew decorator as well
"""
src = tmp_path / 'src.py'
src.write_text(f'''
from __future__ import annotations
from cachew import settings, cachew
settings.THROW_ON_ERROR = {throw}
@cachew
def fun() -> BadType:
print("called!")
return 0
fun()
'''.lstrip())

ctx = pytest.raises(Exception) if throw else nullcontext()
with ctx:
assert check_output([sys.executable, src], text=True).strip() == "called!"


def test_recursive_simple(tmp_path: Path) -> None:
d0 = 0
d1 = 1000
Expand Down

0 comments on commit 2abd77b

Please sign in to comment.