Skip to content

Commit 907b405

Browse files
authored
Merge pull request #9 from pomponchik/develop
0.0.8
2 parents 8f375a3 + 21c0b9e commit 907b405

20 files changed

Lines changed: 1545 additions & 69 deletions

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@ test.py
55
*.egg-info
66
dist
77
venv
8+
.venv
89
build
910
.ruff_cache
1011
.mypy_cache
1112
.coverage
13+
uv.lock
14+
.history
15+
.vscode/
16+
.idea/
17+
temp*

.ruff.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
11
lint.ignore = ['E501', 'E712']
2+
3+
[format]
4+
quote-style = "single"

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
[![Test-Package](https://github.com/pomponchik/transfunctions/actions/workflows/tests_and_coverage.yml/badge.svg)](https://github.com/pomponchik/transfunctions/actions/workflows/tests_and_coverage.yml)
99
[![Python versions](https://img.shields.io/pypi/pyversions/transfunctions.svg)](https://pypi.python.org/pypi/transfunctions)
1010
[![PyPI version](https://badge.fury.io/py/transfunctions.svg)](https://badge.fury.io/py/transfunctions)
11+
[![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/)
1112
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
1213

1314
This library is designed to solve one of the most important problems in python programming - dividing all written code into 2 camps: sync and async. We get rid of code duplication by using templates.
@@ -151,7 +152,7 @@ def template():
151152
The `get_async_function` method will return an async function that looks like this:
152153

153154
```python
154-
def template():
155+
async def template():
155156
print('so, ', end='')
156157
print("it's an async function!")
157158
```

pyproject.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "transfunctions"
7-
version = "0.0.7"
7+
version = "0.0.8"
88
authors = [
99
{ name="Evgeniy Blinov", email="[email protected]" },
1010
]
@@ -16,6 +16,7 @@ dependencies = [
1616
'dill==0.4.0',
1717
'typing_extensions ; python_version <= "3.10"',
1818
]
19+
1920
classifiers = [
2021
"Operating System :: OS Independent",
2122
'Operating System :: MacOS :: MacOS X',
@@ -52,6 +53,11 @@ keywords = [
5253
paths_to_mutate="transfunctions"
5354
runner="pytest"
5455

56+
[tool.pytest.ini_options]
57+
markers = [
58+
"mypy_testing",
59+
]
60+
5561
[project.urls]
5662
'Source' = 'https://github.com/pomponchik/transfunctions'
5763
'Tracker' = 'https://github.com/pomponchik/transfunctions/issues'

requirements_dev.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
pytest==7.4.3
1+
pytest==8.0.2
22
coverage==7.6.1
33
build==1.2.2.post1
44
twine==6.1.0
55
mypy==1.14.1
6+
pytest-mypy-testing==0.1.3
67
ruff==0.9.9
78
mutmut==3.2.3
89
full_match==0.0.2

tests/typing/__init__.py

Whitespace-only changes.

tests/typing/decorators/__init__.py

Whitespace-only changes.
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
import sys
2+
import asyncio
3+
from contextlib import suppress
4+
5+
if sys.version_info <= (3, 11):
6+
from typing_extensions import reveal_type
7+
else:
8+
from typing import reveal_type
9+
10+
import pytest
11+
12+
from transfunctions import superfunction, sync_context, async_context, generator_context, yield_from_it
13+
14+
15+
"""
16+
Что нужно проверить:
17+
18+
1. Что await_it, yield_from_it и yield_it типизированы.
19+
20+
Что проверено:
21+
22+
"""
23+
24+
25+
@pytest.mark.mypy_testing
26+
def test_superfunction_deduced_return_type_sync() -> None:
27+
@superfunction
28+
def typed_superfunction(arg: float, *, kwarg: int = 0) -> int:
29+
with sync_context:
30+
return 1
31+
with async_context:
32+
return 2
33+
34+
reveal_type(~typed_superfunction(1.0)) # N: Revealed type is "builtins.int"
35+
36+
37+
@pytest.mark.mypy_testing
38+
def test_superfunction_deduced_return_type_async() -> None:
39+
@superfunction
40+
def typed_superfunction(arg: float, *, kwarg: int = 0) -> int:
41+
with sync_context:
42+
return 1
43+
with async_context:
44+
return 2
45+
46+
reveal_type(asyncio.run(typed_superfunction(1.0))) # N: Revealed type is "builtins.int"
47+
48+
49+
@pytest.mark.mypy_testing
50+
def test_superfunction_param_spec_fail_on_incorrect_arg_type_sync() -> None:
51+
@superfunction
52+
def typed_superfunction(arg: float, *, kwarg: int = 0) -> int:
53+
with sync_context:
54+
return 1
55+
with async_context:
56+
return 2
57+
58+
~typed_superfunction(None, kwarg=1) # E: Argument 1 to "typed_superfunction" has incompatible type "None"; expected "float"
59+
60+
61+
@pytest.mark.mypy_testing
62+
def test_superfunction_param_spec_fail_on_incorrect_kwarg_type_sync() -> None:
63+
@superfunction
64+
def typed_superfunction(arg: float, *, kwarg: int = 0) -> int:
65+
with sync_context:
66+
return 1
67+
with async_context:
68+
return 2
69+
70+
~typed_superfunction(1.0, kwarg=None) # E: Argument "kwarg" to "typed_superfunction" has incompatible type "None"; expected "int"
71+
72+
73+
@pytest.mark.mypy_testing
74+
def test_superfunction_param_spec_on_correct_args_types_sync() -> None:
75+
@superfunction
76+
def typed_superfunction(arg: float, *, kwarg: int = 0) -> int:
77+
with sync_context:
78+
return 1
79+
with async_context:
80+
return 2
81+
82+
~typed_superfunction(1.0, kwarg=1)
83+
84+
85+
@pytest.mark.mypy_testing
86+
def test_superfunction_param_spec_fail_on_incorrect_arg_type_async() -> None:
87+
@superfunction
88+
def typed_superfunction(arg: float, *, kwarg: int = 0) -> int:
89+
with sync_context:
90+
return 1
91+
with async_context:
92+
return 2
93+
94+
asyncio.run(typed_superfunction(None, kwarg=1)) # E: Argument 1 to "typed_superfunction" has incompatible type "None"; expected "float"
95+
96+
97+
@pytest.mark.mypy_testing
98+
def test_superfunction_param_spec_fail_on_incorrect_kwarg_type_async() -> None:
99+
@superfunction
100+
def typed_superfunction(arg: float, *, kwarg: int = 0) -> int:
101+
with sync_context:
102+
return 1
103+
with async_context:
104+
return 2
105+
106+
asyncio.run(typed_superfunction(1.0, kwarg=None)) # E: Argument "kwarg" to "typed_superfunction" has incompatible type "None"; expected "int"
107+
108+
109+
@pytest.mark.mypy_testing
110+
def test_superfunction_param_spec_on_correct_args_types_async() -> None:
111+
@superfunction
112+
def typed_superfunction(arg: float, *, kwarg: int = 0) -> int:
113+
with sync_context:
114+
return 1
115+
with async_context:
116+
return 2
117+
118+
asyncio.run(typed_superfunction(1.0, kwarg=1))
119+
120+
121+
@pytest.mark.mypy_testing
122+
def test_superfunction_param_spec_fail_on_missing_args_sync() -> None:
123+
@superfunction
124+
def typed_superfunction(arg: float, *, kwarg: int = 0) -> int:
125+
with sync_context:
126+
return 1
127+
with async_context:
128+
return 2
129+
130+
with suppress(TypeError):
131+
~typed_superfunction() # E: Missing positional argument "arg" in call to "typed_superfunction" [call-arg]
132+
133+
134+
@pytest.mark.mypy_testing
135+
@pytest.mark.xfail
136+
def test_superfunction_param_spec_fail_on_extra_args_sync() -> None:
137+
@superfunction
138+
def typed_superfunction(arg: float, *, kwarg: int = 0) -> int:
139+
with sync_context:
140+
return 1
141+
with async_context:
142+
return 2
143+
144+
with suppress(TypeError):
145+
~typed_superfunction(1.0, 2.0, kwarg=1)
146+
147+
148+
@pytest.mark.mypy_testing
149+
@pytest.mark.xfail
150+
def test_superfunction_param_spec_fail_on_extra_kwargs_sync() -> None:
151+
@superfunction
152+
def typed_superfunction(arg: float, *, kwarg: int = 0) -> int:
153+
with sync_context:
154+
return 1
155+
with async_context:
156+
return 2
157+
158+
with suppress(TypeError):
159+
~typed_superfunction(1.0, kwarg=1, kwarg2=1)
160+
161+
162+
@pytest.mark.mypy_testing
163+
def test_superfunction_param_spec_fail_on_missing_args_async() -> None:
164+
@superfunction
165+
def typed_superfunction(arg: float, *, kwarg: int = 0) -> int:
166+
with sync_context:
167+
return 1
168+
with async_context:
169+
return 2
170+
171+
with suppress(TypeError):
172+
asyncio.run(typed_superfunction()) # E: Missing positional argument "arg" in call to "typed_superfunction" [call-arg]
173+
174+
175+
@pytest.mark.mypy_testing
176+
@pytest.mark.xfail
177+
def test_superfunction_param_spec_fail_on_extra_args_async() -> None:
178+
@superfunction
179+
def typed_superfunction(arg: float, *, kwarg: int = 0) -> int:
180+
with sync_context:
181+
return 1
182+
with async_context:
183+
return 2
184+
185+
with suppress(TypeError):
186+
asyncio.run(typed_superfunction(1.0, 2.0, kwarg=1))
187+
188+
189+
@pytest.mark.mypy_testing
190+
@pytest.mark.xfail
191+
def test_superfunction_param_spec_fail_on_extra_kwargs_async() -> None:
192+
@superfunction
193+
def typed_superfunction(arg: float, *, kwarg: int = 0) -> int:
194+
with sync_context:
195+
return 1
196+
with async_context:
197+
return 2
198+
199+
with suppress(TypeError):
200+
asyncio.run(typed_superfunction(1.0, kwarg=1, kwarg2=1))
201+
202+
203+
@pytest.mark.mypy_testing
204+
@pytest.mark.xfail # it shouldn't work because typed_superfunction is a generator function, gut it's not returning a generator object according to it's typing.
205+
def test_simple_using_of_generator_function_with_simple_yield_from() -> None:
206+
@superfunction
207+
def typed_superfunction(arg: float, *, kwarg: int = 0) -> int:
208+
with sync_context:
209+
return 1
210+
with async_context:
211+
return 2
212+
with generator_context:
213+
yield from [1, 2, 3]
214+
215+
list(typed_superfunction(1))
216+
217+
218+
# TODO: we should understand why it works
219+
@pytest.mark.xfail
220+
@pytest.mark.mypy_testing
221+
def test_wrong_using_of_generator_function_with_simple_yield_from() -> None:
222+
@superfunction
223+
def typed_superfunction(arg: float, *, kwarg: int = 0) -> int:
224+
with sync_context:
225+
return 1
226+
with async_context:
227+
return 2
228+
with generator_context:
229+
yield from [1, 2, 3]
230+
231+
list(typed_superfunction(None))
232+
233+
234+
@pytest.mark.mypy_testing
235+
def test_simple_using_of_generator_function_with_yield_from_it_marker_function() -> None:
236+
@superfunction
237+
def typed_superfunction(arg: float, *, kwarg: int = 0) -> int:
238+
with sync_context:
239+
return 1
240+
with async_context:
241+
return 2
242+
with generator_context:
243+
yield_from_it([1, 2, 3])
244+
245+
list(typed_superfunction(1))
246+
247+
248+
# TODO: we should understand why it works
249+
@pytest.mark.xfail
250+
@pytest.mark.mypy_testing
251+
def test_using_of_generator_function_with_yield_from_it_marker_function_with_wrong_return_value() -> None:
252+
@superfunction
253+
def typed_superfunction(arg: float, *, kwarg: int = 0) -> int:
254+
with sync_context:
255+
return 1
256+
with async_context:
257+
return 2
258+
with generator_context:
259+
yield_from_it(['one', 'two'])
260+
261+
list(typed_superfunction(1))

0 commit comments

Comments
 (0)