diff --git a/README.md b/README.md index e9fe4f1..73215ed 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ [ - ![lincense](https://img.shields.io/github/license/neogeny/Late) + ![license](https://img.shields.io/github/license/neogeny/Late) ](https://www.gnu.org/licenses/lgpl-3.0.html) [ ![version](https://img.shields.io/pypi/pyversions/late.svg) @@ -7,12 +7,12 @@ [ ![fury](https://badge.fury.io/py/Late.svg) ](https://pypi.org/project/Late/) -![downloada](https://img.shields.io/pypi/dm/Late.svg) +![downloads](https://img.shields.io/pypi/dm/Late.svg) [ ![tests](https://github.com/neogeny/late/actions/workflows/default.yml/badge.svg) ](https://github.com/neogeny/late/actions/workflows/default.yml) -# 包 Late +# 包 Late 1.3.0b1 Late binding for Python default arguments @@ -66,7 +66,13 @@ in a way so that type checkers do not complain about using ``None`` as the defau def f(x: list[Any] | None = None) -> list[Any]: ``` -Another problem with the above declaration is that calling ``f(None)`` passes type checking, +or: + +```python +def f(x: Optional[list[Any]] = None) -> list[Any]: +``` + +Another problem with the above declarations is that calling ``f(None)`` passes type checking, when that's probably not the preferred situation. @@ -90,10 +96,16 @@ assert f() == [1] ``` +For constructors for basic structured types, the ``__()`` call may be omitted: + +```python +@latebinding +def f(x: list[Any] = []) -> list[Any]: +``` ### Working with classes -**包 Late** also works with classes and `dataclass`. The ``@latebinding`` decorator +**包 Late** also works with classes and ``dataclass``. The ``@latebinding`` decorator must be the outer one: ```python diff --git a/late/__init__.py b/late/__init__.py index 7e2fb33..08863ea 100644 --- a/late/__init__.py +++ b/late/__init__.py @@ -4,7 +4,7 @@ from collections.abc import Callable, Iterator from typing import Any, NamedTuple, TypeVar -__all__ = ['latebinding', 'late', '__'] +__all__ = ['latebinding'] _R = TypeVar('_R') @@ -17,7 +17,7 @@ class _LateBound(NamedTuple): def late(o: _T | Iterator[_V] | Callable[[], _R]) -> _T | _V | _R: - if isinstance(o, int | float | str | bytes | bool | tuple | bytearray | frozenset): + if isinstance(o, int | float | bool | str | bytes): return o # type: ignore return _LateBound(actual=o) # type: ignore @@ -27,19 +27,24 @@ def late(o: _T | Iterator[_V] | Callable[[], _R]) -> _T | _V | _R: 包 = late -def _lateargs(func: Callable, **kwargs) -> dict[str, Any]: +def _resolve_value(value): + if isinstance(value, _LateBound): + value = value.actual + if isinstance(value, int | float | bool | str | bytes): + return value + if isinstance(value, Iterator): + return next(value) + if inspect.isfunction(value): + return value() + return copy.deepcopy(value) + - def resolve_default(value): - if isinstance(value, Iterator): - return next(value) - if inspect.isfunction(value): - return value() - return copy.deepcopy(value) +def _lateargs(func: Callable, **kwargs) -> dict[str, Any]: lateargs = { - name: resolve_default(param.default.actual) + name: _resolve_value(param.default) for name, param in inspect.signature(func).parameters.items() - if name not in kwargs and isinstance(param.default, _LateBound) + if name not in kwargs and param.default != inspect._empty } return {**kwargs, **lateargs} diff --git a/late/_version.py b/late/_version.py index 8503ee7..3f262a6 100644 --- a/late/_version.py +++ b/late/_version.py @@ -1 +1 @@ -__version__ = '1.3.0b1' +__version__ = '1.2.1' diff --git a/ruff.toml b/ruff.toml index 79ed03f..064916d 100644 --- a/ruff.toml +++ b/ruff.toml @@ -10,7 +10,7 @@ select = [ # 'ERA', # commented out code ] ignore = [ - 'B008', + 'B006', 'B008', 'E501', 'E741', 'E402', 'S101', 'S102', 'S105', 'S301', 'S311', 'C408', @@ -23,7 +23,7 @@ ignore = [ 'PLR0913', 'PLR2004', 'PLR0904', 'PLR0915', 'PLW0603', 'PLW2901', 'PLW3201', 'RET505', - 'RUF009', + 'RUF008', 'RUF009', ] exclude = [] diff --git a/test/complex_test.py b/test/complex_test.py index ede4b7e..980d6c9 100644 --- a/test/complex_test.py +++ b/test/complex_test.py @@ -5,7 +5,7 @@ def test_recursion(): @latebinding - def f(x: list[list[Any]] = __([[]])) -> list[list[Any]]: + def f(x: list[list[Any]] = [[]]) -> list[list[Any]]: x[0].append(1) return x diff --git a/test/simple_test.py b/test/simple_test.py index 3844236..a7172da 100644 --- a/test/simple_test.py +++ b/test/simple_test.py @@ -1,13 +1,13 @@ import inspect from typing import Any -from late import __, _LateBound, latebinding, 包 +from late import latebinding, 包 def test_with_list(): @latebinding - def f(x: list[Any] = __([])) -> list[Any]: + def f(x: list[Any] = []) -> list[Any]: x.append(1) return x @@ -18,13 +18,13 @@ def f(x: list[Any] = __([])) -> list[Any]: def test_immutable(): @latebinding - def f(x: frozenset[Any] = __(frozenset()), y: set = __(set())): + def f(x: frozenset[Any] = frozenset(), y: set = set()): return param = inspect.signature(f).parameters['x'] assert type(param.default) is frozenset param = inspect.signature(f).parameters['y'] - assert type(param.default) is _LateBound + assert type(param.default) is set def test_kanji():