Giter VIP home page Giter VIP logo

strongtyping's Introduction

PyPI version Python application Python tox image Code style: black Imports: isort Documentation Status

Strong Typing

Decorator which checks at Runtime whether the function is called with the correct type of parameters.
And raises TypeMisMatch if the used parameters in a function call where invalid.

strongtyping's People

Contributors

amitshahar avatar dependabot[bot] avatar felixthec avatar jakobdev avatar pfython 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  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  avatar  avatar  avatar  avatar

strongtyping's Issues

Cannot import match_typing on Python 3.10.4 with strongtyping 3.11.1

Describe the bug

I'm aware of issue #108 but cannot see how is fixed in 3.11.1.

The setup.py only states python_requires=">=3.7", so I'm not sure what I'm doing wrong?

Python Version used
Python 3.10.4

Package Version used
strongtyping 3.11.1

Addon in use
strongtyping_modules [no]

To Reproduce
Steps to reproduce the behavior:

docker run -it --rm python:3.10.4-slim-bullseye bash

pip install strongtyping==3.11.1

python3 -c 'from strongtyping.strong_typing import match_typing'

Results in import error

root@d1c3b68452de:/# python3 -c 'from strongtyping.strong_typing import match_typing'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/usr/local/lib/python3.10/site-packages/strongtyping/strong_typing.py", line 6, in <module>
    from typing import NotRequired, Required, Type, get_args, get_origin
ImportError: cannot import name 'NotRequired' from 'typing' (/usr/local/lib/python3.10/typing.py)

Expected behavior

Did not expect an import Error.

Screenshots

root@d1c3b68452de:/# python3 --version
Python 3.10.4
root@d1c3b68452de:/# pip install strongtyping==3.11.1
Collecting strongtyping==3.11.1
  Downloading strongtyping-3.11.1-py3-none-any.whl (22 kB)
Installing collected packages: strongtyping
Successfully installed strongtyping-3.11.1
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
WARNING: You are using pip version 22.0.4; however, version 23.1.2 is available.
You should consider upgrading via the '/usr/local/bin/python -m pip install --upgrade pip' command.
root@d1c3b68452de:/# python3 -c 'from strongtyping.strong_typing import match_typing'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/usr/local/lib/python3.10/site-packages/strongtyping/strong_typing.py", line 6, in <module>
    from typing import NotRequired, Required, Type, get_args, get_origin
ImportError: cannot import name 'NotRequired' from 'typing' (/usr/local/lib/python3.10/typing.py)

Desktop (please complete the following information):

  • OS: Docker container
# uname -a
Linux d1c3b68452de 5.19.0-41-generic #42-Ubuntu SMP PREEMPT_DYNAMIC Mon Apr 17 19:17:03 UTC 2023 x86_64 GNU/Linux
  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

Smartphone (please complete the following information):

  • Device: [e.g. iPhone6]
  • OS: [e.g. iOS8.1]
  • Browser [e.g. stock browser, safari]
  • Version [e.g. 22]

Additional context
Add any other context about the problem here.

Handle functions wich specify a value having of type TypeDict

Describe the bug

class UserSelectedComponent(TypedDict):
    id: uuid.UUID
    min_concentration: float
    max_concentration: float

@match_typing
def validate_selected_component_entry(value: UserSelectedComponent):
    pass

calling now the function validate_selected_component_entry will lead to

    return supported_typings[f"checking_typing_{origin_name}"](
KeyError: 'checking_typing_typeddict'

RecursionError

Describe the bug
When try to check this code:

from strongtyping.strong_typing import match_class_typing
import dataclasses as dc
import typing as t

@match_class_typing
@dc.dataclass
class QueryPipelineFunctions:
    """"""
    callables: t.List[str]

QueryPipelineFunctions(["a", "b"])

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\.virtualenvs\arqdat\lib\site-packages\strongtyping\strong_typing.py", line 54, in inner
    cached_key = (func, args.__str__(), kwargs.__str__())
  File "C:\Users\arqdat\lib\site-packages\strongtyping\strong_typing.py", line 54, in inner
    cached_key = (func, args.__str__(), kwargs.__str__())
  File "C:\Users\arqdat\lib\site-packages\strongtyping\strong_typing.py", line 54, in inner
    cached_key = (func, args.__str__(), kwargs.__str__())
  [Previous line repeated 329 more times]
  File "C:\Users\arqdat\lib\site-packages\strongtyping\strong_typing.py", line 49, in inner
    if arg_names and severity_level > SEVERITY_LEVEL.DISABLED.value:
RecursionError: maximum recursion depth exceeded while calling a Python object

Python Version used
Python 3.8.8

Package Version used
strongtyping 2.2.2

Addon in use
strongtyping_modules [no]

To Reproduce
Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior
Expect that no error is found

Screenshots
If applicable, add screenshots to help explain your problem.

Desktop (please complete the following information):

  • OS: [Windows 11]
  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

Smartphone (please complete the following information):

  • Device: [e.g. iPhone6]
  • OS: [e.g. iOS8.1]
  • Browser [e.g. stock browser, safari]
  • Version [e.g. 22]

Additional context
Add any other context about the problem here.

Cannot validate typing of nested TypedDict having a NotRequired clause in Python 3.11

I want to validate a dictionary with a key whose value is another dictionary.
Therefore, I have a parent TypedDict with a key targeting another TypedDict.

The thing is, I am using the NotRequired clause, which is available in Python 3.11 natively.
(see https://peps.python.org/pep-0655/#usage-in-python-3-11 )

But trying to validate this dict with this configuration gives me the following import error:

Traceback (most recent call last):
  File "/app/script.py", line 7, in <module>
    from strongtyping.strong_typing import match_class_typing
  File "/usr/local/lib/python3.11/site-packages/strongtyping/strong_typing.py", line 14, in <module>
    from typing_extensions import NotRequired, Required
ModuleNotFoundError: No module named 'typing_extensions'

Python Version used
Python 3.11.1 (main, Dec 8 2022, 00:05:00) [GCC 10.2.1 20210110] on linux

Package Version used
strongtyping==3.10.3

Addon in use
strongtyping_modules [yes/no] : no

To Reproduce
Steps to reproduce the behavior:

  1. Run a container of this docker image python:3.11.1
docker run -it --rm python:3.11.1
  1. In this container, install the following :
pip install strongtyping==3.10.3
  1. In this container, run the following script:
from typing import TypedDict, NotRequired
from strongtyping.strong_typing import match_class_typing


@match_class_typing
class ChildType(TypedDict):
    key_a: int
    key_b: int


@match_class_typing
class ParentType(TypedDict):
    child: ChildType
    not_required: NotRequired[int]


parent = {"not_required": 3, "child": {"key_a": 1, "key_b": "2"}}
ParentType(parent)

Expected behavior
Should exit without error.

Desktop (please complete the following information):

  • OS:
$ uname -a
Linux ab90f1fc83ca 5.13.0-25-generic #26~20.04.1-Ubuntu SMP Fri Jan 7 16:27:40 UTC 2022 x86_64 GNU/Linux

This time, this is not an edge case, Python 3.11 should be taken into account in the way strongtyping import NotRequired and Required.
Thanks again for your work!

No pyi files in strongtyping-stubs 3.11.1 on PyPI

Both "Source Distribution" (strongtyping-3.11.1.tar.gz) and "Built Distribution" (strongtyping-3.11.1-py3-none-any.whl) do not include any strongtyping-stubs/**/*.pyi files. Thus, no type hints when installing from PyPI.

This issue appears to be related to setuptools. Adding package_data param to setup() function seems to solve it:

setup(
    name="strongtyping",
    version="3.11.2",
    description="Decorator which checks whether the function is called with the correct type of parameter
    long_description=README,
    long_description_content_type="text/markdown",
    url="https://strongtyping.readthedocs.io/en/latest/",
    author="FelixTheC",
    author_email="[email protected]",
    license="MIT",
    classifiers=[
        "License :: OSI Approved :: MIT License",
        "Programming Language :: Python :: 3.11",
    ],
    packages=packages,
    package_data={'strongtyping-stubs': ['**/*.py', '**/*.pyi']},
    python_requires=">=3.11",
    include_package_data=True,
)

Are there errors for undefined keys?

First of all, thank you for creating such a useful library.

Is it possible to return an exception for undefined keys?
As an example, if I pass a dict to User with a key age that I have not defined, it will return an exception.

The reason I want this feature is to help find things that are not defined in the TypedDict. It would be nice to be able to log warnings, etc. even if they are not exceptions.

from typing import TypedDict

from strongtyping.strong_typing import match_class_typing

@match_class_typing
class User(TypedDict):.
    id: str
    username: str
    description: str | None

User({"id": "0123", "username": "test", "description": None, "age": 10})

Due to my inexperience with English, I am using DeepL. I apologize if you find anything offensive.

Infinite recursion when @dataclass decorator comes after @match_class_typing

Describe the bug
As per the documentation, the @dataclass decorator should appear after @match_class_typing, e.g.

@match_class_typing
@dataclass
class Dummy:
    attr_a: int
    attr_b: str

If you add a method and call it, like so:

@match_class_typing
@dataclass
class MyType:
    value: float = None

    def method(self):
        pass

req = MyType()
req.method()

then I get infinite recursion:

Traceback (most recent call last):
  File ".../wat.py", in <module>
    req.method()
  File ".../python3.10/site-packages/strongtyping/strong_typing.py", line 59, in inner
    cached_key = (func, args.__str__(), kwargs.__str__())
  File ".../python3.10/site-packages/strongtyping/strong_typing.py", line 59, in inner
    cached_key = (func, args.__str__(), kwargs.__str__())
  File ".../python3.10/site-packages/strongtyping/strong_typing.py", line 59, in inner
    cached_key = (func, args.__str__(), kwargs.__str__())
  [Previous line repeated 330 more times]
RecursionError: maximum recursion depth exceeded

Reversing the order of decorators, i.e.

@dataclass
@match_class_typing
class MyType:
    value: float = None

    def method(self):
        pass

req = MyType(7)

will fix the infinite recursion but break strongtyping (i.e. the example above doesn't throw an exception when an int is passed where a float is expected.

I was able to work around this by adding __repr__ to the list of skipped methods in strong_typing.py (i.e. in addition to __init__) but I'm not sure if that's the right fix.

Python Version used
Python 3.10.8

Package Version used
strongtyping 3.10.6

Addon in use
strongtyping_modules [yes/no]

To Reproduce
Steps to reproduce the behavior:

@match_class_typing
@dataclass
class MyType:
    value: float = None

    def method(self):
        pass

req = MyType()
req.method()

Expected behavior
Code runs an prints nothing

Subclass checks

It would be great if there were a function to check if defaultdict[str, float] is a subclass of dict[str, float]. Use case: Descriptors ensuring (done in __set_name__) that the typing of parent classes is Liskov Substition consistent with child classes.

@match_class_typing decorater disrupts "isintance(obj, Class)" calls

Using @match_class_typing class decorator on a class leads to wrong behavior of "isinstance"

from strongtyping.strong_typing import match_class_typing

@match_class_typing
class A:
    pass

x = A()
assert isinstance(x, A) 

Expected behavior
isinstance(x, A) should return True.

Initializing TypedDict like a normal class fails

Describe the bug
When trying to create an instance of a TypedDict class in a way you would normaly create an instance. I got an error.

To Reproduce

@match_class_typing
class UserSelectedComponent(TypedDict):
    id: uuid.UUID
    min_concentration: float
    max_concentration: float

obj = UserSelectedComponent(
                id=uuid.UUID("f3567953-0f62-423b-9966-a8036b8fec25"),
                min_concentration=1.0,
                max_concentration=5.0,
            )

Expected behavior
An instance of UserSelectedComponent should be created, if the data is valid.

Docstring from typing also as class decorator

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Describe the solution you'd like
A clear and concise description of what you want to happen.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

Wrong repr when using match_class_typing

Describe the bug

@match_class_typing
class Foo:

    def __init__(self, val: list = []):
        self.val = val


if __name__ == "__main__":
    print(Foo)

>>> <strongtyping.strong_typing.match_class_typing object at 0x7f97fd033fd0>

but it should be

>>> <class '__main__.Foo'>

The fix:
put it inside match_class_typing
def repr(self):
return repr(self.cls)

More explicit TypeMisMatch errors

Python Version used
Python 3.10.4 (main, May 28 2022, 13:25:38) [GCC 10.2.1 20210110] on linux

Package Version used
strongtyping 3.10.4

Addon in use
strongtyping_modules [yes/no]

To Reproduce

# Python 3.10.4 (main, May 28 2022, 13:25:38) [GCC 10.2.1 20210110] on linux
# pip install typing-extensions==4.4.0
# pip install strongtyping==3.10.4

from typing import TypedDict

from strongtyping.strong_typing import match_class_typing


@match_class_typing
class MyType(TypedDict):
    a: int
    b: int


parent = {"b": "4", "extra": "5"}
MyType(parent)

gives

Incorrect parameter: `{'b': '4',
 'extra': '5'}`
	required: {'a': <class 'int'>, 'b': <class 'int'>}
Traceback (most recent call last):
  File "/root/src/script.py", line 17, in <module>
    MyType(parent)
  File "/root/.local/lib/python3.10/site-packages/strongtyping/strong_typing.py", line 176, in __call__
    raise self.excep_raise(self.create_error_msg(arguments))
strongtyping.strong_typing_utils.TypeMisMatch

Expected behavior
A clearer error message on which attributes are missing/extra/have wrong types e.g.

Missing parameter(s): 'a'
Extra parameter(s): 'extra'
Wrong-typed parameter(s): 'b' 

Traceback (most recent call last):
  File "/root/src/script.py", line 18, in <module>
    MyType(parent)
  File "/root/.local/lib/python3.10/site-packages/strongtyping/strong_typing.py", line 176, in __call__
    raise self.excep_raise(self.create_error_msg(arguments))
strongtyping.strong_typing_utils.TypeMisMatch

Thanks again for your work!!!

A possible change to match_class_typing decorator type hints

The following code that loads statically-typed JSON works as expected:

# test.py

import json
from typing import TypedDict

config_json_str = """
{
  "id": 42,
  "name": "baz"
}
"""


class Config(TypedDict):
    id: int
    name: str


def load() -> Config:
    return json.loads(config_json_str)


# conf_id type is Config
conf = load()

# conf_id type is int
conf_id = conf["id"]

# conf_id type is str
conf_name = conf["name"]

print(f"Conf vals: {conf_id}, {conf_name}")

The following key usage error is detected by pyright at "compile time" and by python at runtime.

# runtime: KeyError: 'babayka'
# pyright check: Type of "conf_name" is unknown [reportUnknownVariableType]
conf_babayka = conf["babayka"]

The following config_json_str "name" key error would not be detected at "compile time" and at runtime it will only be detected when trying to access non-existing "name" key in conf dict:

config_json_str = """
{
  "id": 42,
  "babayka_name": "baz"
}
"""

strongtyping can bring config keys and shape errors to load() stage or (better) to test runners, which is awesome ๐Ÿ˜Ž For example:

# test_typing_issues.py

import json
from typing import TypedDict

from strongtyping.strong_typing import match_class_typing, match_typing

config_json_str = """
{
  "id": 42,
  "name": "baz"
}
"""


@match_class_typing
class Config(TypedDict):
    id: int
    name: str


@match_typing
def validate_config(config: Config) -> Config:
    return config


def load() -> Config:
    return validate_config(json.loads(config_json_str))


# conf_id type is Config
conf = load()

# conf_id type is str
conf_id = conf["id"]

# conf_id type is str
conf_name = conf["name"]

print(f"Conf vals: {conf_id}, {conf_name}")

The above test_typing_issues.py works as expected at runtime, pyright however produces the following errors:

<...>/test_typing_issues.py
  <...>/src/python/cdaq_proto/test_typing_issues.py:21:29 - warning: Expected type expression but received "match_class_typing" (reportGeneralTypeIssues)
  <...>/src/python/cdaq_proto/test_typing_issues.py:21:40 - warning: Expected type expression but received "match_class_typing" (reportGeneralTypeIssues)
  <...>/src/python/cdaq_proto/test_typing_issues.py:21:21 - error: Type of parameter "config" is unknown (reportUnknownParameterType)
  <...>/src/python/cdaq_proto/test_typing_issues.py:22:12 - warning: Return type is unknown (reportUnknownVariableType)
  <...>/src/python/cdaq_proto/test_typing_issues.py:21:40 - warning: Declared return type is unknown (reportUnknownVariableType)
  <...>/src/python/cdaq_proto/test_typing_issues.py:30:15 - warning: Expected type expression but received "match_class_typing" (reportGeneralTypeIssues)
  <...>/src/python/cdaq_proto/test_typing_issues.py:30:15 - warning: Declared return type is unknown (reportUnknownVariableType)
  <...>/src/python/cdaq_proto/test_typing_issues.py:35:1 - warning: Type of "conf" is unknown (reportUnknownVariableType)
  <...>/src/python/cdaq_proto/test_typing_issues.py:38:1 - warning: Type of "conf_id" is unknown (reportUnknownVariableType)
  <...>/src/python/cdaq_proto/test_typing_issues.py:41:1 - warning: Type of "conf_name" is unknown (reportUnknownVariableType)
1 error, 9 warnings, 0 informations

A hackerish way to resolve the above typing issues at user side could separating strongtyping-decorated classes, explicit casting and disabling the issue of "variable used as type":

# test_hackerish.py

import json
from typing import TypedDict, cast

from strongtyping.strong_typing import match_class_typing, match_typing

config_json_str = """
{
  "id": 42,
  "name": "baz"
}
"""


class Config(TypedDict):
    id: int
    name: str


ConfigStrong = match_class_typing(Config)


@match_typing
def validate_config(config: ConfigStrong) -> Config:  # type: ignore reportGeneralTypeIssues
    return cast(Config, config)


def load() -> Config:
    return validate_config(json.loads(config_json_str))


# conf_id type is Config
conf = load()

# conf_id type is str
conf_id = conf["id"]

# conf_id type is str
conf_name = conf["name"]

print(f"Conf vals: {conf_id}, {conf_name}")

In my understanding, to eliminate the above typing issues without user-side hacking, strongtyping decorators type hints should be changed to identity functions. For example:

# strongtyping-stubs/strong_typing.pyi

# Remove original "class match_class_typing: ..."
# ...

from typing import TypeVar

T = TypeVar("T")

def match_class_typing(cls: T) -> T: ...

# ...

This fix should eliminate typing issues in the above "test_typing_issues.py" example. The "class match_class_typing: ..." could be defined under a different name such as "class MatchClassTyping: ...". If library users need access to MatchClassTyping class members, they can just cast their "match_class_typing" decorated class to MatchClassTyping.

Probably, the above is applicable to other strongtyping decorators...

Cannot validate typing of nested TypedDict having a NotRequired clause in Python 3.10

I want to validate a dictionary with a key whose value is another dictionary.
Therefore, I have a parent TypedDict with a key targeting another TypedDict.

The thing is, I am using the NotRequired clause, which is available in Python 3.10 through the typing_extensions package (4.4.0 being the latest version for now).
As seen at https://peps.python.org/pep-0655/#usage-in-python-3-11 , in Python 3.10, when using the NotRequired clause, it is recommended to use the TypedDict class from typing_extensions

from typing_extensions import TypedDict
# instead of
from typing import TypedDict

But trying to validate this dict with this configuration gives me the following error:

root@340bf78f3c82:/$ python script.py 
Traceback (most recent call last):
  File "/f/.local/lib/python3.10/site-packages/strongtyping/strong_typing_utils.py", line 489, in check_type
    is_instance = isinstance(argument, type_of) or argument == type_of
  File "/f/.local/lib/python3.10/site-packages/typing_extensions.py", line 671, in _check_fails
    raise TypeError('TypedDict does not support instance and class checks')
TypeError: TypedDict does not support instance and class checks

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/f/src/script.py", line 17, in <module>
    ParentType(parent)
  File "/f/.local/lib/python3.10/site-packages/strongtyping/strong_typing.py", line 182, in __call__
    if not checking_typing_typedict_values(arguments, self.__annotations__, self.__total__):
  File "/f/.local/lib/python3.10/site-packages/strongtyping/strong_typing_utils.py", line 268, in checking_typing_typedict_values
    return all(check_type(args.get(key), val) for key, val in required_types.items())
  File "/f/.local/lib/python3.10/site-packages/strongtyping/strong_typing_utils.py", line 268, in <genexpr>
    return all(check_type(args.get(key), val) for key, val in required_types.items())
  File "/f/.local/lib/python3.10/site-packages/strongtyping/strong_typing_utils.py", line 491, in check_type
    return isinstance(argument, type_of._subs_tree()[1:])
  File "/f/.local/lib/python3.10/site-packages/strongtyping/strong_typing.py", line 136, in __getattr__
    return getattr(self.cls, item)
AttributeError: type object 'ChildType' has no attribute '_subs_tree'

Python Version used
Python 3.10.4 (main, May 28 2022, 13:25:38) [GCC 10.2.1 20210110] on linux

Package Version used
strongtyping==3.10.3

Addon in use
strongtyping_modules [yes/no] : no

To Reproduce
Steps to reproduce the behavior:

  1. Run a container of this docker image python:3.10.4-slim-bullseye
docker run -it --rm python:3.10.4-slim-bullseye bash
  1. In this container, install the following :
pip install typing-extensions==4.4.0
pip install strongtyping==3.10.3
  1. In this container, run the following script:
from typing_extensions import NotRequired, TypedDict
from strongtyping.strong_typing import match_class_typing


@match_class_typing
class ChildType(TypedDict):
    key_a: int
    key_b: int


@match_class_typing
class ParentType(TypedDict):
    child: ChildType
    not_required: NotRequired[int]


parent = {"child": {"key_a": 1, "key_b": 2}}
ParentType(parent)

Expected behavior
Should exit without error.

Desktop (please complete the following information):

  • OS:
$ uname -a
Linux c 5.13.0-25-generic #26~20.04.1-Ubuntu SMP Fri Jan 7 16:27:40 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux

Additional context

It is to note that the following works as expected though:

from typing import TypedDict
from typing_extensions import NotRequired
from strongtyping.strong_typing import match_class_typing


@match_class_typing
class ChildType(TypedDict):
    key_a: int
    key_b: int


@match_class_typing
class ParentType(TypedDict):
    child: ChildType
    not_required: NotRequired[int]


parent = {"child": {"key_a": 1, "key_b": 2}}
ParentType(parent)

But then, in this case, the NotRequired clause is not respected, the following will error with a TypeMisMatch error, and it should not:

parent = {"not_required": 3, "child": {"key_a": 1, "key_b": "2"}}
ParentType(parent)
Incorrect parameter: `{'child': {'key_a': 1,
           'key_b': '2'},
 'not_required': 3}`
	required: {'child': <strongtyping.strong_typing.MatchTypedDict object at 0x7f169c29eb90>, 'not_required': typing_extensions.NotRequired[int]}
Traceback (most recent call last):
  File "/f/src/script.py", line 24, in <module>
    ParentType(parent)
  File "/f/.local/lib/python3.10/site-packages/strongtyping/strong_typing.py", line 183, in __call__
    raise self.excep_raise(self.create_error_msg(arguments))
strongtyping.strong_typing_utils.TypeMisMatch

It is an edge case, but I thought it was worth reporting it because NotRequired brings a lot for typing.
Thanks for your work!

Include mypy into Action

When package users are working with mypy the get errors (Untyped decorator makes function "xyz" untyped)

Fix with:
F = TypeVar('F', bound=Callable[..., Any])
DecoFact = Callable[[F], F] # Decorator_Factory

Use strictes mypy settings as possible and fix issues

Is strongtypes safe to depend on?

I'm planning to use strongtypes in a upcoming project, which is a library. The PyPi page of strongtypes shows this:

This is the release for Python-3.10 only

If you need a different version please checkout the release Tags 2.*.*

This rises the question: How safe is it, to depend on strongtypes? My lib will require Python 3.10, but for the future, it will be bad if my lib depends on this and a new Version, which only works with 3.11 will be uploaded. Telling Users of my lib to install from GitHub if they want older versions, is not a good practice.

Evaluate arg types of TypeDicts' Required and NotRequired types instead of origin types

Is your feature request related to a problem? Please describe.
We're using TypeDict along with @match_class_typing of strongtyping module to apply type validations for api schemas.
We often have the need to define a TypedDict with some keys that are required and others that are optional and can be missing. Previously the only way to define such a TypedDict was to declare one TypedDict with one total=True and then inherit it from another TypedDict with total=False

example

class _Example(TypedDict): # implicitly total=True
foo: str

class Example(_Example, total=False):
bar: int

Recently TypeDicts started supporting Required and NotRequired types (from typing or typing_extensions modules) for their attributes. source https://peps.python.org/pep-0655/#specification

So a TypeDict class can be easily defined as:

class Example(TypedDict, total=False):
foo: Required[str]
bar: int

Required[type] and NotRequired[type] have origin type Required|NotRequired and e.g str, int, dict, list, etc as their type args. So strongtyping complains and gives a type mismatch error in case a key is of type str for example, but has been defined as Required[str]

It would be great if strongtyping could support getting type arguments in case of Required and Not Required "types" instead of the origin type

Describe the solution you'd like
The simplest solution I can think of is adding an extra if clause in strong_typing_utils.py to handle Required and NotRequired e.g

      if any(x in origin_name for x in ["required", "notrequired"]):
          if get_origin(get_args(type_of)[0]):
              type_of = get_origin(get_args(type_of)[0])
          return isinstance(argument, get_args(type_of))
def check_type(argument, type_of, mro=False, **kwargs):
    from strongtyping.types import IterValidator, Validator

    if checking_typing_generator(argument, type_of):
        # generator will be exhausted when we check it, so we return it without any checking
        return argument

    check_result = True
    if type_of is not None:
        origin, origin_name = get_origins(type_of)
        origin_name = origin_name.lower()

     if any(x in origin_name for x in ["required", "notrequired"]):
            if get_origin(get_args(type_of)[0]):
                type_of = get_origin(get_args(type_of)[0])
            return isinstance(argument, get_args(type_of))

        if "new_type" in origin_name:
            type_of = type_of.__supertype__
            origin, origin_name = get_origins(type_of)
            origin_name = origin_name.lower()
            .
            ..
            ...
            ....

Describe alternatives you've considered
Ideally, we could define in the decorator specific origins that should be "ignored" and take into account their args according to the following https://peps.python.org/pep-0655/#interaction-with-get-origin-and-get-args
The user could define as a string the original name of the type e.g Required and this would get lowercased for comparison reasons in the utility function.

Additional context

issue with functool.partials

from django.urls.conf import path as url_path

@match_typing
def swagger_from_url(self, urlpatterns: List[url_path]):
    pass

path = partial(_path, Pattern=RoutePattern)

leads to AttributeError: 'functools.partial' object has no attribute '_subs_tree'

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.