Skip to content

Commit a5d429e

Browse files
authored
Merge pull request #10 from pomponchik/develop
0.0.9
2 parents 907b405 + 9494a19 commit a5d429e

23 files changed

Lines changed: 514 additions & 341 deletions

.github/ISSUE_TEMPLATE/question.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name: Question or consultation
33
about: Ask anything about this project
44
title: ''
5-
labels: guestion
5+
labels: question
66
assignees: pomponchik
77

88
---

.github/workflows/lint.yml

Lines changed: 37 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,50 @@
11
name: Lint
22

3-
on:
4-
push
3+
on: push
54

65
jobs:
76
build:
8-
97
runs-on: ubuntu-latest
108
strategy:
119
matrix:
12-
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
10+
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "3.14t"]
1311

1412
steps:
15-
- uses: actions/checkout@v2
13+
- uses: actions/checkout@v4
1614

17-
- name: Set up Python ${{ matrix.python-version }}
18-
uses: actions/setup-python@v3
19-
with:
15+
- name: Set up Python ${{ matrix.python-version }}
16+
uses: actions/setup-python@v5
17+
with:
2018
python-version: ${{ matrix.python-version }}
2119

22-
- name: Install dependencies
23-
shell: bash
24-
run: pip install -r requirements_dev.txt
25-
26-
- name: Install the library
27-
shell: bash
28-
run: pip install .
29-
30-
- name: Run ruff
31-
shell: bash
32-
run: ruff check transfunctions
33-
34-
- name: Run ruff for tests
35-
shell: bash
36-
run: ruff check tests
20+
- name: Cache pip dependencies
21+
uses: actions/cache@v4
22+
with:
23+
path: ~/.cache/pip
24+
key: ${{ runner.os }}-pip-${{ github.workflow }}-${{ hashFiles('requirements_dev.txt') }}
25+
restore-keys: |
26+
${{ runner.os }}-pip-${{ github.workflow }}-
27+
28+
- name: Install dependencies
29+
shell: bash
30+
run: pip install -r requirements_dev.txt
31+
32+
- name: Install the library
33+
shell: bash
34+
run: pip install .
35+
36+
- name: Run ruff
37+
shell: bash
38+
run: ruff check transfunctions
39+
40+
- name: Run ruff for tests
41+
shell: bash
42+
run: ruff check tests
43+
44+
- name: Run mypy
45+
shell: bash
46+
run: mypy --strict transfunctions
47+
48+
- name: Run mypy for tests
49+
shell: bash
50+
run: mypy tests --exclude typing

.github/workflows/release.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ jobs:
1515
# IMPORTANT: this permission is mandatory for trusted publishing
1616
id-token: write
1717
steps:
18-
- uses: actions/checkout@v2
18+
- uses: actions/checkout@v4
1919

2020
- name: Set up Python ${{ matrix.python-version }}
21-
uses: actions/setup-python@v1
21+
uses: actions/setup-python@v5
2222
with:
23-
python-version: ${{ matrix.python-version }}
23+
python-version: ${{ matrix.python-version }}
2424

2525
- name: Install dependencies
2626
shell: bash
Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,55 @@
11
name: Tests
22

3-
on:
4-
push
3+
on: push
54

65
jobs:
76
build:
8-
97
runs-on: ${{ matrix.os }}
108
strategy:
119
matrix:
1210
os: [macos-latest, ubuntu-latest, windows-latest]
13-
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
11+
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "3.14t"]
1412

1513
steps:
16-
- uses: actions/checkout@v2
17-
- name: Set up Python ${{ matrix.python-version }}
18-
uses: actions/setup-python@v3
19-
with:
14+
- uses: actions/checkout@v4
15+
- name: Set up Python ${{ matrix.python-version }}
16+
uses: actions/setup-python@v5
17+
with:
2018
python-version: ${{ matrix.python-version }}
2119

22-
- name: Install the library
23-
shell: bash
24-
run: pip install .
25-
26-
- name: Install dependencies
27-
shell: bash
28-
run: pip install -r requirements_dev.txt
29-
30-
- name: Print all libs
31-
shell: bash
32-
run: pip list
33-
34-
- name: Run tests and show coverage on the command line
35-
run: |
36-
coverage run --source=transfunctions --omit="*tests*" -m pytest --cache-clear --assert=plain && coverage report -m --fail-under=90
37-
coverage xml
38-
39-
- name: Upload coverage to Coveralls
40-
if: runner.os == 'Linux'
41-
env:
42-
COVERALLS_REPO_TOKEN: ${{secrets.COVERALLS_REPO_TOKEN}}
43-
uses: coverallsapp/github-action@v2
44-
with:
45-
format: cobertura
46-
file: coverage.xml
47-
48-
- name: Run tests and show the branch coverage on the command line
49-
run: coverage run --branch --source=transfunctions --omit="*tests*" -m pytest --cache-clear --assert=plain && coverage report -m --fail-under=90
20+
- name: Install the library
21+
shell: bash
22+
run: pip install .
23+
24+
- name: Cache pip dependencies
25+
uses: actions/cache@v4
26+
with:
27+
path: ~/.cache/pip
28+
key: ${{ runner.os }}-pip-${{ github.workflow }}-${{ hashFiles('requirements_dev.txt') }}
29+
restore-keys: |
30+
${{ runner.os }}-pip-${{ github.workflow }}-
31+
32+
- name: Install dependencies
33+
shell: bash
34+
run: pip install -r requirements_dev.txt
35+
36+
- name: Print all libs
37+
shell: bash
38+
run: pip list
39+
40+
- name: Run tests and show coverage on the command line
41+
run: |
42+
coverage run --source=transfunctions --omit="*tests*" -m pytest --cache-clear --assert=plain && coverage report -m --fail-under=94
43+
coverage xml
44+
45+
- name: Upload coverage to Coveralls
46+
if: runner.os == 'Linux'
47+
env:
48+
COVERALLS_REPO_TOKEN: ${{secrets.COVERALLS_REPO_TOKEN}}
49+
uses: coverallsapp/github-action@v2
50+
with:
51+
format: cobertura
52+
file: coverage.xml
53+
54+
- name: Run tests and show the branch coverage on the command line
55+
run: coverage run --branch --source=transfunctions --omit="*tests*" -m pytest --cache-clear --assert=plain && coverage report -m --fail-under=94

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ test.py
66
dist
77
venv
88
.venv
9+
venv3
910
build
1011
.ruff_cache
1112
.mypy_cache
@@ -14,4 +15,5 @@ uv.lock
1415
.history
1516
.vscode/
1617
.idea/
17-
temp*
18+
.ropeproject
19+
node_modules

.ruff.toml

Lines changed: 0 additions & 4 deletions
This file was deleted.

README.md

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
![logo](https://raw.githubusercontent.com/pomponchik/transfunctions/develop/docs/assets/logo_2.svg)
1+
<details>
2+
<summary>ⓘ</summary>
23

34
[![Downloads](https://static.pepy.tech/badge/transfunctions/month)](https://pepy.tech/project/transfunctions)
45
[![Downloads](https://static.pepy.tech/badge/transfunctions)](https://pepy.tech/project/transfunctions)
@@ -11,6 +12,10 @@
1112
[![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/)
1213
[![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)
1314

15+
</details>
16+
17+
![logo](https://raw.githubusercontent.com/pomponchik/transfunctions/develop/docs/assets/logo_2.svg)
18+
1419
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.
1520

1621

@@ -21,6 +26,7 @@ This library is designed to solve one of the most important problems in python p
2126
- [**Code generation**](#code-generation)
2227
- [**Markers**](#markers)
2328
- [**Superfunctions**](#superfunctions)
29+
- [**Typing**](#typing)
2430

2531

2632
## Quick start
@@ -297,3 +303,34 @@ However, it is not completely free. The fact is that this mode uses a special tr
297303
- Exceptions will not work normally inside this function. Rather, they can be picked up and intercepted in [`sys.unraisablehook`](https://docs.python.org/3/library/sys.html#sys.unraisablehook), but they will not go up the stack above this function. This is due to a feature of CPython: exceptions that occur inside callbacks for finalizing objects are completely escaped.
298304

299305
This mode is well suited for functions such as logging or sending statistics from your code: simple functions from which no exceptions or return values are expected. In all other cases, I recommend using the tilde syntax.
306+
307+
308+
## Typing
309+
310+
Typing is the most difficult problem we faced when developing this library. In most situations, it has already been solved, but in some cases you may still notice flaws when using `mypy` or other static type analyzers. If you encounter similar problems, please [report](https://github.com/pomponchik/transfunctions/issues) them.
311+
312+
There are 2 main difficulties in developing typing here:
313+
314+
- Code generation creates code in runtime that is not in the source files of your project. Whereas most type analyzers look at your code statically, at what is actually present in your files.
315+
- We mix several types of syntax in a single template function, but the static analyzer does not know that this is a template and part of the code will be deleted from here. In its opinion, this is the final function that will continue to be used in your project.
316+
317+
As you can see, typing in Python is not well suited for metaprogramming. However, in this project, almost all the problems with typing turned out to be solved in one way or another. The main reason why this is so is that we mostly *remove* code from functions, but hardly *add* it there during code generation. In other words, we almost never encounter the problem of how to type the *added* code. This makes the solution to most typing problems accessible. However! Unfortunately, we were not able to completely hide all the typing problems under the hood, but you should still be aware of some of them if you use `mypy` or another analyzer.
318+
319+
If you use the keyword `yield from`, you need to call the function `yield_from_it` instead:
320+
321+
```python
322+
from transfunctions import yield_it
323+
324+
@superfunction
325+
def my_superfunction():
326+
print('so, ', end='')
327+
with sync_context:
328+
print("it's just usual function!")
329+
with async_context:
330+
print("it's an async function!")
331+
with generator_context:
332+
print("it's a generator function!")
333+
yield_from_it([1, 2, 3])
334+
```
335+
336+
The keywords yield or yield from are available to you and work perfectly, but from the point of view of a static type checker, they turn the function into a generator, which should also mean a special type annotation. By replacing this fragment with a function call, we hack it.

pyproject.toml

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,13 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "transfunctions"
7-
version = "0.0.8"
8-
authors = [
9-
{ name="Evgeniy Blinov", email="zheni-b@yandex.ru" },
10-
]
7+
version = "0.0.9"
8+
authors = [{ name = "Evgeniy Blinov", email = "zheni-b@yandex.ru" }]
119
description = 'Say NO to Python fragmentation on sync and async'
1210
readme = "README.md"
1311
requires-python = ">=3.8"
1412
dependencies = [
15-
'displayhooks>=0.0.4',
13+
'displayhooks>=0.0.5',
1614
'dill==0.4.0',
1715
'typing_extensions ; python_version <= "3.10"',
1816
]
@@ -30,6 +28,9 @@ classifiers = [
3028
'Programming Language :: Python :: 3.11',
3129
'Programming Language :: Python :: 3.12',
3230
'Programming Language :: Python :: 3.13',
31+
'Programming Language :: Python :: 3.14',
32+
'Programming Language :: Python :: Free Threading',
33+
'Programming Language :: Python :: Free Threading :: 3 - Stable',
3334
'License :: OSI Approved :: MIT License',
3435
'Intended Audience :: Developers',
3536
'Topic :: Software Development :: Libraries',
@@ -50,13 +51,16 @@ keywords = [
5051
"transfunctions" = ["py.typed"]
5152

5253
[tool.mutmut]
53-
paths_to_mutate="transfunctions"
54-
runner="pytest"
54+
paths_to_mutate = "transfunctions"
55+
runner = "pytest"
5556

5657
[tool.pytest.ini_options]
57-
markers = [
58-
"mypy_testing",
59-
]
58+
markers = ["mypy_testing"]
59+
60+
[tool.ruff]
61+
lint.ignore = ['E501', 'E712', 'PTH123', 'PTH118', 'PLR2004', 'PTH107', 'SIM105', 'SIM102', 'RET503', 'PLR0912', 'C901', 'RUF001']
62+
lint.select = ["ERA001", "YTT", "ASYNC", "BLE", "B", "A", "COM", "INP", "PIE", "T20", "PT", "RSE", "RET", "SIM", "SLOT", "TID252", "ARG", "PTH", "I", "C90", "N", "E", "W", "D201", "D202", "D419", "F", "PL", "PLE", "PLR", "PLW", "RUF", "TRY201", "TRY400", "TRY401"]
63+
format.quote-style = "single"
6064

6165
[project.urls]
6266
'Source' = 'https://github.com/pomponchik/transfunctions'

requirements_dev.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ mypy==1.14.1
66
pytest-mypy-testing==0.1.3
77
ruff==0.9.9
88
mutmut==3.2.3
9-
full_match==0.0.2
9+
full_match==0.0.3

tests/documentation/test_readme.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,23 @@
33
from contextlib import redirect_stdout
44

55
from transfunctions import (
6-
transfunction,
7-
sync_context,
86
async_context,
97
generator_context,
8+
sync_context,
9+
transfunction,
1010
)
1111

1212

1313
def test_quick_start():
1414
@transfunction
1515
def template():
16-
print('so, ', end='')
16+
print('so, ', end='') # noqa: T201
1717
with sync_context:
18-
print("it's just usual function!")
18+
print("it's just usual function!") # noqa: T201
1919
with async_context:
20-
print("it's an async function!")
20+
print("it's an async function!") # noqa: T201
2121
with generator_context:
22-
print("it's a generator function!")
22+
print("it's a generator function!") # noqa: T201
2323
yield
2424

2525
buffer = io.StringIO()

0 commit comments

Comments
 (0)