Skip to content

Commit

Permalink
Merge fix (#57)
Browse files Browse the repository at this point in the history
* [DRAFT] maybe some future implementations

* [FEAT] ValidationType to add validation functions to type annotation

* [FEAT] docstring from type annotations

* [FEAT] docstring from type annotations

* [UPD] add testing

* [FIX] issue when extension module is installed

* [UPD] separate test with py3.9 only syntax

* [UPD] skip specific test when running with installed module

* fixup!

* [UPD] better info for typing.Literal

* fixup!

* [UPD] include new feature information

* [UPD] beta version

* [UPD] mkdocs for readthedocs

* [UPD] include readthedocs conf

* Update .readthedocs.yaml

* Update .readthedocs.yaml

* .

* .

* fixup!

* fixup!fixup!

* fixup!fixup!fixup!

* fixup!fixup!fixup!fixup!

* fixup!

* [UPD] docs

* fixup!

* [FIX] missing iterable check

* [FIX] include missing type check for Iterable

* isort: .

* isort: .

* black: .

Co-authored-by: eisenmenger <[email protected]>
  • Loading branch information
FelixTheC and eisenmenger authored May 30, 2021
1 parent 9c6c1a8 commit 16a7e9a
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 6 deletions.
3 changes: 3 additions & 0 deletions docs/release-notes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Release Notes

## v2.1.2
- include missing type check for `Iterable`

## v2.1.0
- new type hint type `Validator` like `Union` you join a type-hint and a validation function
- better `TypeMisMatch` tracecback informations (you will now see the value which caused this issue)
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

setup(
name="strongtyping",
version="2.1.1",
version="2.1.2",
description="Decorator which checks whether the function is called with the correct type of parameters",
long_description=README,
long_description_content_type="text/markdown",
Expand Down
7 changes: 7 additions & 0 deletions strongtyping/strong_typing_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,13 @@ def checking_typing_validator(arg, possible_types, *args):
return check_type(arg, required_type)


def checking_typing_iterable(arg: Any, possible_types: tuple, *args):
if not hasattr(arg, "__iter__"):
return False
pssble_type = possible_types[0]
return all(check_type(argument, pssble_type) for argument in arg)


def module_checking_typing_list(arg: Any, possible_types: Any):
if (
not hasattr(possible_types, "__args__")
Expand Down
43 changes: 38 additions & 5 deletions strongtyping/tests/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,26 @@
from datetime import datetime
from enum import Enum, IntEnum
from types import FunctionType, MethodType
from typing import Any, Callable, Dict, Generator, Iterator, List
from typing import (
Any,
Callable,
Dict,
Generator,
Iterable,
Iterator,
List,
NewType,
Optional,
Set,
Tuple,
Type,
Union,
)
from unittest import mock

import pytest
import ujson as ujson

from strongtyping.config import SEVERITY_LEVEL
from strongtyping.strong_typing import match_class_typing, match_typing
from strongtyping.strong_typing_utils import (
Expand All @@ -32,10 +49,6 @@
from typing import Literal
except ImportError:
print("python version < 3.8")
from typing import NewType, Optional, Set, Tuple, Type, Union

import pytest
import ujson as ujson


def test_get_possible_types_from_typing():
Expand Down Expand Up @@ -1164,5 +1177,25 @@ def foo(val_a: List, val_b: Dict, val_c: Set, val_d: Tuple):
assert foo([1, 2, "foo"], {"foo": "bar", 2: [2, 3]}, set([1, 2, 3]), (1, "2"))


def test_with_iterable():
AllowedCluster = Iterable[int]

@match_typing
def cluster(items: AllowedCluster):
return True

assert cluster((1, 2, 3, 4, 5))
assert cluster({1: 0, 2: 0, 3: 0}.keys())

with pytest.raises(TypeMisMatch):
cluster("123")

with pytest.raises(TypeMisMatch):
cluster(cluster)

with pytest.raises(TypeMisMatch):
cluster(1)


if __name__ == "__main__":
pytest.main(["-vv", "-s", __file__])
100 changes: 100 additions & 0 deletions tmp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@created: 12.05.21
@author: eisenmenger
"""
from functools import wraps
from itertools import count
from typing import Union
import inspect

from strongtyping.strong_typing import match_typing
from strongtyping.strong_typing_utils import TypeMisMatch, ValidType, _ValidType, \
get_possible_types, get_origins


def getsource(object):
"""Return the text of the source code for an object.
The argument may be a module, class, method, function, traceback, frame,
or code object. The source code is returned as a single string. An
OSError is raised if the source code cannot be retrieved."""
lines, lnum = inspect.getsourcelines(object)
return ''.join(lines[1:])


def docs_from_tying(func):

@wraps(func)
def inner(*args, **kwargs):
return func(*args, **kwargs)

annotations = func.__annotations__
doc_infos = [f"Function: {func.__name__}", '-' * 30]
for key, val in annotations.items():
if key != 'return':
doc_infos.append(f':param: {key}')
doc_infos.append(f':type: {val}')
if get_origins(val)[1] == 'ValidType':
doc_infos.append(f':requirement: \n{getsource(get_possible_types(val)[1])}')

if 'return' in annotations:
doc_infos.append(f'\n:returns: {annotations["return"].__name__}')

text = '\n'.join(doc_infos)
inner.__doc__ = text
return inner


def validate_input(val):
if not (0 < val < 10000) or (hasattr(val, 'is_integer') and not val.is_integer()):
# A float is an integer if the remainder is 0 like 10.0
return False


def validate_str_input(val):
return 10 < len(val) < 20


@match_typing
@docs_from_tying
def foo(val: ValidType[int, validate_input],
val_2: ValidType[str, validate_str_input]) -> int:
return val ** 2


# def test_valid_input():
# for i in range(1, 10000):
# assert foo(i) == i ** 2
#
#
# def test_invalid_input():
# counter = count(start=1.01, step=.01)
# for i in range(500):
# try:
# foo(next(counter))
# except TypeMisMatch:
# assert True
# else:
# raise AssertionError
#
# for i in range(1, 10000):
# try:
# foo(i * -1)
# except TypeMisMatch:
# assert True
# else:
# raise AssertionError
#
# for data in ("1", "foo", "bar"):
# try:
# foo(data)
# except TypeMisMatch:
# assert True
# else:
# raise AssertionError


if __name__ == '__main__':
print(foo.__doc__)

0 comments on commit 16a7e9a

Please sign in to comment.