Skip to content
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
6 changes: 1 addition & 5 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
[run]
omit =
*pedantic/tests*
*/home/travis/virtualenv*

[report]
exclude_lines =
# Don't complain if non-runnable code isn't run:
pragma: no cover
except ImportError
raise ImportError
raise RuntimeError
if __name__ == .__main__.:
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
# Changelog
## Pedantic 3.0.1
- improve line coverage
- make task `check-changelog` work even before making a commit
- cleanup `.coveragerc`

## Pedantic 3.0.0
- removed decorator `@count_calls`
- removed decorator `@does_same_as_function`
Expand Down
5 changes: 3 additions & 2 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,9 @@ tasks:
# Fetch latest master if we're in a repo with a remote
git fetch origin master >/dev/null 2>&1 || true

if git diff --name-only "$BASE"...HEAD | grep -qx "CHANGELOG.md"; then
echo "✅ CHANGELOG.md was updated."
if git diff --name-only "$BASE"...HEAD | grep -qx "CHANGELOG.md" ||
git diff --name-only HEAD | grep -qx "CHANGELOG.md"; then
echo "✅ CHANGELOG.md was updated."
else
echo "❌ CHANGELOG.md was NOT updated."
echo ""
Expand Down
2 changes: 1 addition & 1 deletion pedantic/models/decorated_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def __init__(self, func: Callable[..., Any]) -> None: # noqa: D107
self._func = func

if not callable(func):
raise PedanticTypeCheckException(f'{self.full_name} should be a method or function.')
raise PedanticTypeCheckException(f'{func} should be a method or function')

self._full_arg_spec = inspect.getfullargspec(func)
self._signature = inspect.signature(func)
Expand Down
17 changes: 7 additions & 10 deletions pedantic/type_checking_logic/check_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,6 @@ def _is_instance(obj: Any, type_: Any, type_vars: Dict[TypeVar_, Any], context:
validator = _ORIGIN_TYPE_CHECKERS[base]
return validator(obj, type_args, type_vars, context)

if base.__base__ != typing.Generic:
raise RuntimeError(f'Unknown base: {base}')
return isinstance(obj, base)

if _is_forward_ref(type_=type_):
Expand All @@ -249,13 +247,13 @@ def _is_instance(obj: Any, type_: Any, type_vars: Dict[TypeVar_, Any], context:
if type_ in {list, set, dict, frozenset, tuple, type}:
raise PedanticTypeCheckException('Missing type arguments')

try:
return isinstance(obj, type_)
except TypeError:
if isinstance(type_, _ProtocolMeta):
return True # we do not check this
if isinstance(type_, _ProtocolMeta):
if getattr(type_, '_is_runtime_protocol', False):
return isinstance(obj, type_)

return True # since this Protocol not has the @runtime_cheable decorator

raise
return isinstance(obj, type_)


def _is_forward_ref(type_: Any) -> bool:
Expand Down Expand Up @@ -713,8 +711,7 @@ def _instancecheck_generator(
generator: typing.Generator, type_args: Tuple, _: Dict[TypeVar_, Any], __: Dict[str, Any] | None = None,
) -> bool:
from pedantic.models import GeneratorWrapper # noqa: PLC0415 must be local due to circular imports
if not isinstance(generator, GeneratorWrapper):
raise TypeError(generator)
assert isinstance(generator, GeneratorWrapper) # noqa: S101
return (generator.yield_type == type_args[0]
and generator.send_type == type_args[1]
and generator.return_type == type_args[2])
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "pedantic"
version = "3.0.0"
version = "3.0.1"
description = "Some useful Python decorators for cleaner software development."
readme = "README.md"
requires-python = ">=3.11,<4.0"
Expand Down
29 changes: 29 additions & 0 deletions tests/decorators/pedantic/test_pedantic.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
TypeVar,
TypeVarTuple,
Union,
runtime_checkable,
)

import pytest
Expand Down Expand Up @@ -2588,12 +2589,40 @@ class IsDataclass(Protocol):
class Foo:
v: int

class NoDataClass:
def __init__(self, v: int) -> None:
self.v = v

@pedantic
def foo(x: IsDataclass) -> IsDataclass:
return x

foo(x=Foo(v=42))
foo(x=NoDataClass(v=42)) # raises no error since IsDataclass is not runtime checkable


def test_dataclass_runtime_checkable_protocol():
@runtime_checkable
class IsDataclass(Protocol):
__dataclass_fields__: ClassVar[Dict]

@dataclass
class Foo:
v: int

class NoDataClass:
def __init__(self, v: int) -> None:
self.v = v

@pedantic
def foo(x: IsDataclass) -> IsDataclass:
return x

foo(x=Foo(v=42))

with pytest.raises(expected_exception=PedanticTypeCheckException):
foo(x=NoDataClass(v=42))


def test_dataclass_protocol_in_type():
class IsDataclass(Protocol):
Expand Down
8 changes: 8 additions & 0 deletions tests/models/test_decorated_function.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import pytest

from pedantic.exceptions import PedanticTypeCheckException
from pedantic.models.decorated_function import DecoratedFunction


Expand Down Expand Up @@ -140,3 +143,8 @@ def f_4():
assert DecoratedFunction(f_2).num_of_decorators == 1
assert DecoratedFunction(f_3).num_of_decorators == 2
assert DecoratedFunction(f_4).num_of_decorators == 3


def test_wrap_obj_which_is_not_callable():
with pytest.raises(expected_exception=PedanticTypeCheckException, match='42 should be a method or function'):
DecoratedFunction(func=42)