lostindarkmath / pedantic-python-decorators Goto Github PK
View Code? Open in Web Editor NEWSome useful decorators for any situation. Includes runtime type checking.
License: Apache License 2.0
Some useful decorators for any situation. Includes runtime type checking.
License: Apache License 2.0
Coroutines could be allowed in @in_subprocess
by creating a new event loop in _inner
and then calling event_loop.run_until_complete(fun)
you can assign me and I'll send a PR sometime soon
Ticket: TheKevJames/coveralls-python#387
This blocks all PRs unfortunately.
Currently, things like the following are allowed.
@pedantic
def operation(l: list, d: dict) -> None
# do something
```
But I think pedantic should force the usage of `typing.List` and `typing.Dict` instead.
Heyho,
i came across a problem when decorating a function with validate_args and pedantic.
Instead of checking my function argument, validate_args accesses the instance of the class thus failing.
Example:
I want to check if the int ist greater then 0. I decorate this function with pedantic and call it:
from pedantic import validate_args, pedantic
class classA:
def __init__(self, a: int) -> None:
self.a = a
@validate_args(lambda x: (x > 0,f'Argument should be greater then 0, but it was {x}.'))
@pedantic
def some_calculation(self, x: int) -> int:
return x
if __name__ == '__main__':
classA = classA(a=10)
classA.some_calculation(x=15)
But I get the error:
Traceback (most recent call last):
File ".../src/pypypy.py", line 17, in <module>
classA.some_calculation(x=15)
File "...\lib\site-packages\pedantic\method_decorators.py", line 289, in wrapper
res, msg = validator(arg)
File ".../src/pypypy.py", line 9, in <lambda>
@validate_args(lambda x: (x > 0,f'Argument should be greater then 0, but it was {x}.'))
TypeError: '>' not supported between instances of 'classA' and 'int'
It seems like, validate_args wants to compare classA-object with an int which of course does not work.
If I switch the decorators, pedantic says:
File "...\lib\site-packages\pedantic\method_decorators.py", line 25, in __require_kwargs
assert args_without_self == (), f'Use kwargs when you call {func.__name__}! {args_without_self}'
AssertionError: Use kwargs when you call some_calculation! (<__main__.classA object at 0x000001991322D8E0>,)
Hope you can enable double decorators like this! Would be really helpful! (:
Example Code:
from datetime import datetime
from typing import Callable, Union
from pedantic import frozen_dataclass
@frozen_dataclass
class DatetimeCallable:
dt: datetime | Callable[[], datetime]
if __name__ == '__main__':
x = DatetimeCallable(
dt=datetime(year=2022, month=1, day=1),
).validate_types()
Exception:
TypeError: datetime.datetime(2022, 1, 1, 0, 0) is not a callable object
After #69 was merged, forward refs are not parsed correctly anymore.
It complains that the type, which is forward referred to, is not defined.
Code:
@frozen_dataclass
class Comment:
...
replies: List['Comment']
Error:
pedantic.exceptions.PedanticTypeCheckException: In dataclass "Comment" in field "replies": An error occurred during type hint checking. Value: [Comment(...)] Annotation: typing.List[ForwardRef('Comment')] Mostly this is caused by an incorrect type annotation. Details: name 'Comment' is not defined
Every once in a while I see that this lib has a new release. Unfortunately the release notification doesn't contain any useful information besides the release date. As a interested person, I would love to know what changed, so maybe establishing a changelog would be helpful.
There are various ways to accomplish this. Here are two possible solutions:
Error message:
Traceback (most recent call last):
File "C:/Users/WILLI/AppData/Roaming/JetBrains/PyCharm2020.3/scratches/bug.py", line 21, in <module>
f.do()
File "C:/Users/WILLI/AppData/Roaming/JetBrains/PyCharm2020.3/scratches/bug.py", line 12, in do
print(self.bar(value=self._value))
TypeError: bar() got multiple values for argument 'value'
Example for reproducing the bug:
from pedantic import pedantic_class, disable_pedantic
disable_pedantic()
@pedantic_class
class Foo:
def __init__(self) -> None:
self._value = 42
def do(self) -> None:
print(self.bar(value=self._value))
@staticmethod
def bar(value: int) -> int:
return value + 75
if __name__ == '__main__':
f = Foo()
f.do()
Due to an issue with a frozen job when using Python 3.11.0rc2+
I removed the 3.11-dev
from the CI.
The release of Python 3.11 is planned for October 24, 2022. After this stable release, Python 3.11 should be re-added to the CI and the bug should be fixed if it is still present.
New typing features that comes with Python 3.11:
typing.Self
LiteralString
: I will treat it as str
, because pedantic
does only type-checking at runtime and at runtime those cannot be distinguishedNever
as alias for NoReturn
TypeVarTuple
: Ts = TypeVarTuple('Ts')
class Array(Generic[*Ts]):
...
x = Array[int, int, flat]()
def add_dimension(a: Array[*Ts]) -> Array[int, *Ts]):
....
Hey there,
I came across a small issue with the typeHint parser.
Consider the class Token has a function to compare itself to another token object:
class Token():
@pedantic
def compare(self, other: 'Token') -> bool
# compares tokens. Returns True if equal, or false otherwise
Comparing two tokens:
tok1 = Token('abcd')
tok2 = Token('abcd')
if tok1.compare(other=tok2): # true, do something
I get the error message:
File "...\lib\site-packages\pedantic\type_hint_parser.py", line 14, in is_instance
if type_.__module__ == 'typing':
AttributeError: 'str' object has no attribute '__module__'
If I look inside the type hint parser I see the cause of the error:
if type_.__module__ == 'typing':
In this case you want to access the module-attribute of the string 'Token' which of course doesnt work.
It would be nice if you could fix the typehint parser in this special case.
I am working to get this package onto conda-forge, but it looks like there is an issue with the source tarballs uploaded onto pypi. The wheels work fine, but when building from source, it looks like the package itself is missing from the tarballs.
See:
conda-forge/staged-recipes#23986 (comment)
Thanks!
Hey there, closly related to #20 I have a similar problem in the same function:
Lets say I replace List[BPMNElement] with List[src.converter.bpmn_models.bpmn_element.BPMNElement]:
def make_element(self, element_type: BPMNEnum,
src_tgt_elements: Optional[List[BPMNElement]] = None) -> List[BPMNElement]:
"""
Searches all element_types in XML-DOM and returns corresponding
BPMN-Objects.
Args:
element_type(BPMNEnum): abc
**src_tgt_elements (Optional[List[src.converter.bpmn_models.bpmn_element.BPMNElement]]): abc**
Returns:
List[src.converter.bpmn_models.bpmn_element.BPMNElement]: abc
"""
Then I get the error:
AssertionError: In function GraphBuilder.make_element: Documented type of parameter src_tgt_elements is incorrect. Expected Union[List[src.converter.bpmn_models.bpmn_element.BPMNElement], NoneType] but documented is Optional[List[src.converter.bpmn_models.bpmn_element.BPMNElement]].`
The error message states, that Union[..., NoneType] should be used instead of Optional[...].
If I replace Optional with Union than everything is fine -- which is again syntactically awkward :(
Heyho,
I tried to use your pedantic_class decorator on a class with alot of methods and documentation.
Somewhere in this class in a docstring syntax error. But I cant find it because the error messages doesnt show any function name or text snipped or line of code.
Example of a class containing two function. One with a correct docstring syntax and one without. But of course, in a larger class it is a miracle to find the syntax error - or you have to check every method with pedantic :(
from pedantic import pedantic_class
@pedantic_class
class Foo:
def __init__(self, a: int) -> None:
self.a = int
def func(self, b:str) -> str:
"""
Function with docstring syntax error below.
Args:
b (str):
simple string
Returns:
str: simple string
"""
return b
def bunk(self) -> int:
'''
Function with correct docstring.
Returns:
int: 42
'''
return 42
if __name__ == '__main__':
foo = Foo(a=10)
foo.func(b='bar')
The error log is quite large. I only copy the featureless last lines:
File "...\lib\site-packages\docstring_parser\google.py", line 106, in _build_meta
before, desc = text.split(":", 1)
ValueError: not enough values to unpack (expected 2, got 1)
Hey there,
I upgradet to Pedantic 1.20 but came across a severe side effect error in pedantic.
All of my ~130 tests were successful in version<1.20 but now in 1.20 there are 4 failing tests.
All of them refer to this function of my stack-class:
def top(self) -> Optional[T]:
if len(self.items) > 0:
return self.items[len(self.items)-1]
else:
return None
When the stack is empty, I want to return None, which I typehint with Optional.
Sometimes pedantic accepts this function and sometimes not! It depends on the order of my unit tests... Look at the two pictures:
If I put the failed test at first, other tests fail:
If I execute my test solely, everything is fine.
All of my tests are independent from each other. If I follow the debugger, sometimes pedantic throws an error and sometimes not - with everything else being equal.
Here is the full implementation of my stack class:
from typing import List, Optional, Union
from typing import TypeVar, Generic
from pedantic import pedantic_class
T = TypeVar('T')
@pedantic_class
class Stack(Generic[T]):
def __init__(self) -> None:
self.items: List[T] = []
def push(self, item: T) -> None:
self.items.append(item)
def pop(self) -> T:
return self.items.pop()
def empty(self) -> bool:
return not self.items
def top(self) -> Optional[T]:
if len(self.items) > 0:
return self.items[len(self.items)-1]
else:
return None
So far, in all of every tests I instantiate a new stack object with an empty list of items. And I only put igraph.Vertex objects in it.
......
I spend some time in the debugger understanding this behaviour:
The first time I call stack.top() and let it return a None, everything is fine.
Second, third and n-th time returning a None will be fine.
But then the first time calling stack.top() when expecting to return a igraph.Vertex(), pedantic screams at me:
AssertionError: In function Stack.top:
E For TypeVar ~T exists a type conflict: value None has type <class 'NoneType'> and value igraph.Vertex(<igraph.Graph object at 0x000001DDB10FBC70>, 0, {'name': 'open'}) has type <class 'igraph.Vertex'>
Pedantic expects a None.
So it looks like, your inner pedantic workings bind 'T' only to a None and not to None and the runtime-T-type.
Hope you can fix it.
Hihi,
I profiled my project & tests with CProfile and found out your pedantic package relies heavily on the inspect package. Which makes your pedantic decorators quite slow.
I added two pictures of the profiling:
It spends two full seconds in the inspect package with a total of 2800 calls! (left side of the graph)
getsource is called by three of your functions: is_property_setter, is_function_that_wants_args and is_static_method.
In my ~130 tests, inspect is called 20000(!!) times and spending 15 seconds in there. (bottom the graph)
Hope you can add a little memoization in there!
Hey there,
I have an issue when decorating a function with @pedantic that overloads the python in-operator.
Example:
@pedantic
def __contains__(self, item:str) -> bool:
if item in self.my_string:
return True
else:
return False
I override contains to enable following syntax:
my_text = MyTextObject('abcd')
if 'ab' in my_text -> True # do something
But when I decorate contains with @pedantic and call it, then I get the error-message:
assert args_without_self == (), f'Use kwargs when you call {func.__name__}! {args_without_self}'
AssertionError: Use kwargs when you call __contains__! ('ab',)
I guess, because the python in-operator doesnt call contains with explicit function arguments? Is it a problem with your implementation or is it Python?
full code snippet:
from pedantic import pedantic
class MyTextObject:
def __init__(self, text):
self.my_string = text
@pedantic
def __contains__(self, item: str) -> bool:
if item in self.my_string:
return True
else:
return False
my_text = MyTextObject('abcd')
if 'ab' in my_text:
print('yes, its working')
Heyhi,
when I put wrong type hints on a method, I get a not very useful error log.
from typing import Tuple
from pedantic import pedantic_class
@pedantic_class
class myStaticClass:
@staticmethod
def double_func(a:int) -> int:
x,y = myStaticClass.static_bar()
return x
@staticmethod
def static_bar() -> (int, int): #this is wrong. Correct would be Tuple[int, int]
return 0, 1
if __name__ == '__main__':
print(myStaticClass.double_func(a=0))
I get the error:
File "...\lib\site-packages\pedantic\type_hint_parser.py", line 13, in is_instance
if type_.__module__ == 'typing':
AttributeError: 'tuple' object has no attribute '__module__'
Not saying where the wrong type hint is. Hope you can fix it!
Currently every value has to be described in the decorator, this makes the definition of an request very long. Especially when the request is a post with a lot of data.
It would be very handy if there was a class based Serialization like in the django REST framework
Something like @validate(ObjectSerializer(MySerializerClass))
then the decorator returns the dataclass as a single parameter to the actual function.
Hey there.
I upgraded the pedantic version to 1.1.0 but as soon I want to use it in my project, I get the error:
Traceback (most recent call last):
File "...\src\main.py", line 3, in <module>
from src.converter.converter import Converter
File "...\src\converter\converter.py", line 5, in <module>
from pedantic import pedantic_class
File "...\venv38\lib\site-packages\pedantic\__init__.py", line 1, in <module>
from pedantic.class_decorators \
File "...\venv38\lib\site-packages\pedantic\class_decorators.py", line 4, in <module>
from pedantic.method_decorators import pedantic, pedantic_require_docstring, trace, timer
File "...\venv38\lib\site-packages\pedantic\method_decorators.py", line 11, in <module>
from pedantic.models.decorated_function import DecoratedFunction
ModuleNotFoundError: No module named 'pedantic.models'
Seems like sth breaks inside you package!
Hope you can fix it soon
Deserializable is a simple abstract base class. Sure, it is used for Flask deserialization, but the base class itself does not depend on Flask.
Please move the class out of the Flask-dependent part of the pedantic, I don't want my package to depend on Flask, but still be used by a pedantic-using Flask service :)
Hey there,
the pedantic decorator seems to fail with optional arguments.
When executing the snippet below...
class MyClass():
@pedantic
def foo(self, a:int, b: Optional[int] = 1) -> int:
return a+b
if __name__ == '__main__':
myclass = MyClass()
print(myclass.foo(a=10)) # should print 11
... pedantic prints:
Traceback (most recent call last):
File ".../src/mypy.py", line 13, in <module>
print(myclass.foo(a=10)) # should print 11
File "...\lib\site-packages\pedantic\method_decorators.py", line 355, in wrapper
return __require_kwargs_and_type_checking(func=func, args=args, kwargs=kwargs, annotations=annotations)
File "...\lib\site-packages\pedantic\method_decorators.py", line 52, in __require_kwargs_and_type_checking
assert len(kwargs) + 1 == len(annotations), \
AssertionError: Function "foo" misses some type hints or arguments: {'a': 10}, {'return': <class 'int'>, 'a': <class 'int'>, 'b': typing.Union[int, NoneType]}
Hey there,
I have a deep package structure in my project. When I want to add Docstring in my function I get a issues with the required syntax.
Issue: Full package name required - which can be very long.
def make_element(self, element_type: BPMNEnum,
src_tgt_elements: Optional[List[BPMNElement]] = None) -> List[BPMNElement]:
"""
Searches all element_types in XML-DOM and returns corresponding
BPMN-Objects.
Args:
element_type(BPMNEnum): abc
src_tgt_elements Optional[List[BPMNElement]]: abc
Returns:
**List[BPMNElement]: abc**
"""
In this case I get an error about the return type:
AssertionError: In function GraphBuilder.make_element:
Documented type is incorrect: Annotation: List[src.converter.bpmn_models.bpmn_element.BPMNElement] Documented: List[BPMNElement]
Such long package names are very unhandy. Is is possible to just use 'BPMNElement' instead?
Example Code:
from typing import Tuple
from pedantic import frozen_dataclass
@frozen_dataclass
class EllipsisClass:
a: Tuple[...]
if __name__ == '__main__':
x = EllipsisClass(a=(1, 2, 3)).validate_types()
Exception:
pedantic.exceptions.PedanticTypeCheckException: In dataclass "Data" in field "knowledge_items_to_map": An error occurred during type hint checking. Value: ('72d95a5e-bdc0-11e9-bd9f-0242ac120006', '317ca686-bf52-11e9-bd9f-0242ac120006', '584b61ba-bf54-11e9-bd9f-0242ac120006', '6033bfda-bf54-11e9-bd9f-0242ac120006', '6af40682-bf54-11e9-bd9f-0242ac120006', '056c72b0-bf25-11e9-bd9f-0242ac120006', 'f682bee0-c27a-11e9-92c7-0242ac120008') Annotation: typing.Tuple[...] Mostly this is caused by an incorrect type annotation. Details: 'ellipsis' object has no attribute '__module__'
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.