Giter Club home page Giter Club logo

pedantic-python-decorators's Introduction

Hi there ๐Ÿ‘‹

I'm the creator of the Conngenial App. I mainly work with Python and Flutter.

๐Ÿ”Ž Github Profile Details

LostInDarkMath

โšก Github Stats

LostInDarkMath LostInDarkMath

๐Ÿ”ฅ Github Streaks

LostInDarkMath

๐Ÿ“Š Github Contribution Graph

Ashish Kumar Activity Graph

๐Ÿ† Github Achievements

LostInDarkMath


pedantic-python-decorators's People

Contributors

dependabot[bot] avatar lostindarkmath avatar mikemhenry avatar robwalt avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

pedantic-python-decorators's Issues

pedantic fails at Optional TypeHints

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]}

coin flip TypeHinting-Errors at Generic Types

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:
pedantic_test1

If I put the failed test at first, other tests fail:
pedantic_test2

If I execute my test solely, everything is fine.
pedantic_test3

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.

Disable pedantic doesn't works as expected

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()

Very long descriptive Docstring-TypeHint

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?

validate_args accesses custom class instead of arguments when decorated with pedantic

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! (:

Re-add Python 3.11 to CI

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.

Typechecks not working for ellipsis

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__' 

Establish Changelog

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:

  • creating and maintaining a manual changelog. As an example, we can take a look at this repo. Contributors are asked to manually write meaningful changelog entries and I think the latest section of the changelog is just copy-pasted in the body of the release form on the release date
  • adapting the conventional commit format and using tools like cocogitto (or others, there are many similar projects out there) to automatically create changelog information

move `Deserializable` out of flask_parameters

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 :)

enable error messages to display function names

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)

Add support for Python 3.11

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 distinguished
  • Never as alias for NoReturn
  • variadic generics:
    • TypeVarTuple:
      Ts = TypeVarTuple('Ts')
      
      class Array(Generic[*Ts]):
          ...
      
      x = Array[int, int, flat]()
    
      def add_dimension(a: Array[*Ts]) -> Array[int, *Ts]):
         ....

import error in version 1.1.0

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

Object based Validator

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.

Performance of inspect in pedantic package very low

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.
project profile

In my ~130 tests, inspect is called 20000(!!) times and spending 15 seconds in there. (bottom the graph)
tests profile

Hope you can add a little memoization in there!

not a good error message with wrong Tuple-Type Hints

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!

Problems when Union contains Callable Typehint

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

Optional fails at very long Docstring TypeHints

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 :(

pedantic fails at operator overload

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')

pedantic fails at type hints of custom class inside custom class

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.

"Enhancement: forward refs" actually broke forward refs

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

Pedantic shouldn't allow sloppy type annotations

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.