Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
8de9d89
test case
Aug 5, 2025
0263fda
readme
Aug 5, 2025
90b7c83
readme
Aug 5, 2025
1d1bd2b
readme
Aug 5, 2025
c44e18a
comment the xfail flag
Aug 7, 2025
26080f2
uncomment the xfail flag
Aug 7, 2025
2f551f7
add dependencies cache for the tests CI workflow
Aug 8, 2025
783e363
up coverage
Aug 8, 2025
35db597
get coverage down
Aug 8, 2025
ce6f897
empty line
Aug 8, 2025
8ae5f47
Add workflow name to pip cache key
Aug 8, 2025
1c41335
Add pip dependency caching to the lint workflow
Aug 10, 2025
2d6007f
empty line
Aug 10, 2025
3405dba
gitignore
Aug 10, 2025
4ee6dff
some typing shit
Aug 11, 2025
1e63db8
delete empty line
Aug 11, 2025
0c979c6
another typing shit
Aug 11, 2025
1e425a1
another typing shit
Aug 11, 2025
4e31e57
another typing shit
Aug 11, 2025
0a11352
add mypy to CI
Aug 11, 2025
5b07682
some typing shit
Aug 11, 2025
cf29bf2
some typing shit
Aug 11, 2025
58b6568
some typing shit
Aug 11, 2025
268eaf4
some typing shit
Aug 11, 2025
9d5fdd0
some typing shit
Aug 11, 2025
9f58555
Add `--check` flag to mypy in lint workflow
Aug 11, 2025
df68f99
new version tag
Aug 11, 2025
672fdfe
pragma comment
Aug 11, 2025
df11456
Add @ prefix to dual‑use decorator error message
Aug 11, 2025
fa84004
Add test for non‑@ usage of transfunction decorator
Aug 11, 2025
a3685c2
Add tests that disallow calling superfunction without @
Aug 11, 2025
b2489df
delete extra code
Aug 11, 2025
4d63dfd
Add automated Ollama code review workflow
Aug 11, 2025
38ad864
Remove unused AliasedDecoratorSyntaxError import
Aug 11, 2025
c5e37a6
Improve some typing
Aug 11, 2025
4cd9330
rename a workflow
Aug 11, 2025
95a857f
Use local ollama‑code‑review action in workflow
Aug 12, 2025
e4cc5a2
Remove obsolete Ollama code review action and workflow
Aug 12, 2025
c3c65a6
Add Code Review workflow with Ollama LLM
Aug 12, 2025
caed76a
Update code_review workflow to run on pull request to main
Aug 12, 2025
1548ce9
Add node_modules to .gitignore
Aug 12, 2025
f65f7ec
Update code review prompt for style and security
Aug 12, 2025
7d4ce30
Cache and pull codellama model in CI workflow
Aug 12, 2025
2754d4a
Update code review prompt to focus on vulnerabilities
Aug 12, 2025
bb19e92
Update code_review.yml
Aug 12, 2025
7bd1757
Update code review workflow to use Qwen 2.5 Coder Tools model
Aug 12, 2025
6725beb
Refine code review prompt for safety
Aug 12, 2025
3341155
Add model env and bot name to code review
Aug 12, 2025
5e3b4f8
Update code review workflow to use local LLM
Aug 12, 2025
a1a60be
Use MODEL env var for cache key in code_review
Aug 12, 2025
ccd864d
Remove explicit bot author from PR comments
Aug 12, 2025
c197b17
Update CI workflows to newer action versions and correct cache paths
Aug 13, 2025
ccd69c7
Update CODE REVIEW workflow to use new Qwen3 model
Aug 13, 2025
e36aa31
Add detailed code review prompt to workflow
Aug 13, 2025
1cdac79
Remove BOT_NAME from workflow environment variables
Aug 13, 2025
dff2135
Add comment about prompt source to code_review workflow
Aug 13, 2025
dd8a1b1
Update code_review.yml
Aug 13, 2025
6941cc1
Add guidance for “everything fine” comment in Code Review workflow
Aug 13, 2025
e0bd4d8
Add senior developer role to code review prompt
Aug 13, 2025
7a5eb30
Show PROMPT during code review workflow
Aug 13, 2025
469270e
Add workflow dispatch with reason input
Aug 13, 2025
df250c9
Update code review workflow input description to English
Aug 13, 2025
0ec0fe9
try python 3.14 in the CI
Aug 19, 2025
082066b
Update lint workflow to include 3.14‑dev Python version in matrix
Aug 19, 2025
54fc281
Add Python 3.14‑dev to CI matrix
Aug 19, 2025
f16cbbe
Clarify removal/addition wording in README
Oct 16, 2025
a5639b2
Fix typo in issue template labels
Feb 9, 2026
3808361
Update Python versions in CI workflows to remove "3.14-dev" and add
Feb 9, 2026
d9aee0a
Add Python Free Threading classifiers
Feb 9, 2026
2d0ff37
Remove code review workflow using local LLM
Feb 9, 2026
5ff8e91
Bump displayhooks to 0.0.5
Feb 9, 2026
742a077
Add check_decorators parameter to superfunction and transfunction
Feb 9, 2026
bc0064c
Move Ruff config from .ruff.toml to pyproject.toml
Feb 9, 2026
e83d906
Lint issues
Feb 9, 2026
08caaee
Some lint issues
Feb 9, 2026
382b832
Lint issues
Feb 9, 2026
b662ae6
Lint issues
Feb 9, 2026
76fb2f4
Add venv3 to .gitignore
Feb 9, 2026
d0fb1aa
Fix version check for Python 3.10+ compatibility
Feb 9, 2026
9fc6d9e
Lint ignore
Feb 9, 2026
874ea83
Add usage note for yield_from_it replacement
Feb 10, 2026
31c7a8d
Hide badges in readme
Feb 10, 2026
7715c63
Refactor transfunction decorator to support variadic args
Feb 10, 2026
7fae349
Mypy strict mode
Feb 10, 2026
d27729d
Add type ignore comments to suppress type checker warnings in AST
Feb 10, 2026
9494a19
Add overload definitions for transfunction decorator
Feb 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/question.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name: Question or consultation
about: Ask anything about this project
title: ''
labels: guestion
labels: question
assignees: pomponchik

---
Expand Down
60 changes: 37 additions & 23 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -1,36 +1,50 @@
name: Lint

on:
push
on: push

jobs:
build:

runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "3.14t"]

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
shell: bash
run: pip install -r requirements_dev.txt

- name: Install the library
shell: bash
run: pip install .

- name: Run ruff
shell: bash
run: ruff check transfunctions

- name: Run ruff for tests
shell: bash
run: ruff check tests
- name: Cache pip dependencies
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ github.workflow }}-${{ hashFiles('requirements_dev.txt') }}
restore-keys: |
${{ runner.os }}-pip-${{ github.workflow }}-

- name: Install dependencies
shell: bash
run: pip install -r requirements_dev.txt

- name: Install the library
shell: bash
run: pip install .

- name: Run ruff
shell: bash
run: ruff check transfunctions

- name: Run ruff for tests
shell: bash
run: ruff check tests

- name: Run mypy
shell: bash
run: mypy --strict transfunctions

- name: Run mypy for tests
shell: bash
run: mypy tests --exclude typing
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ jobs:
# IMPORTANT: this permission is mandatory for trusted publishing
id-token: write
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
python-version: ${{ matrix.python-version }}
Comment on lines 20 to +23
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix undefined matrix variable for setup-python (actionlint error) and pin a concrete Python version

This job does not define a matrix, yet references matrix.python-version. setup-python will receive an empty version and fail. Also, consider enabling built-in pip cache for speed.

-      - name: Set up Python ${{ matrix.python-version }}
-        uses: actions/setup-python@v5
-        with:
-          python-version: ${{ matrix.python-version }}
+      - name: Set up Python
+        uses: actions/setup-python@v5
+        with:
+          python-version: '3.12'
+          cache: 'pip'
+          cache-dependency-path: 'requirements_dev.txt'

Actionlint hints:

  • Line 20/23: property "python-version" is not defined in object type {} — caused by the undefined matrix variable.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
python-version: ${{ matrix.python-version }}
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
cache-dependency-path: 'requirements_dev.txt'
🧰 Tools
🪛 actionlint (1.7.7)

20-20: property "python-version" is not defined in object type {}

(expression)


23-23: property "python-version" is not defined in object type {}

(expression)

🤖 Prompt for AI Agents
.github/workflows/release.yml lines 20-23: the workflow references
matrix.python-version but no matrix is defined, causing setup-python to receive
an empty value and actionlint to error; fix by replacing the undefined matrix
reference with a concrete python-version (e.g. "3.11") or add a matrix strategy
that defines python-version, and enable actions/setup-python's built-in pip
cache by adding cache: "pip" under the with: block to speed installs.


- name: Install dependencies
shell: bash
Expand Down
78 changes: 42 additions & 36 deletions .github/workflows/tests_and_coverage.yml
Original file line number Diff line number Diff line change
@@ -1,49 +1,55 @@
name: Tests

on:
push
on: push

jobs:
build:

runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "3.14t"]

steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install the library
shell: bash
run: pip install .

- name: Install dependencies
shell: bash
run: pip install -r requirements_dev.txt

- name: Print all libs
shell: bash
run: pip list

- name: Run tests and show coverage on the command line
run: |
coverage run --source=transfunctions --omit="*tests*" -m pytest --cache-clear --assert=plain && coverage report -m --fail-under=90
coverage xml

- name: Upload coverage to Coveralls
if: runner.os == 'Linux'
env:
COVERALLS_REPO_TOKEN: ${{secrets.COVERALLS_REPO_TOKEN}}
uses: coverallsapp/github-action@v2
with:
format: cobertura
file: coverage.xml

- name: Run tests and show the branch coverage on the command line
run: coverage run --branch --source=transfunctions --omit="*tests*" -m pytest --cache-clear --assert=plain && coverage report -m --fail-under=90
- name: Install the library
shell: bash
run: pip install .

- name: Cache pip dependencies
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ github.workflow }}-${{ hashFiles('requirements_dev.txt') }}
restore-keys: |
${{ runner.os }}-pip-${{ github.workflow }}-

- name: Install dependencies
shell: bash
run: pip install -r requirements_dev.txt

Comment on lines +24 to +35
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Reorder installs to leverage cache and prevent double-installs

Currently you install your package before caching and before installing dev deps, which can trigger unnecessary network hits and duplicate dependency resolution. Install dev deps first (after enabling cache), then install the package (preferably editable for test workflows).

-      - name: Install the library
-        shell: bash
-        run: pip install .
-
-      - name: Cache pip dependencies
-        uses: actions/cache@v4
-        with:
-          path: ~/.cache/pip
-          key: ${{ runner.os }}-pip-${{ github.workflow }}-${{ hashFiles('requirements_dev.txt') }}
-          restore-keys: |
-            ${{ runner.os }}-pip-${{ github.workflow }}-
+      # setup-python's built-in cache is enabled above; remove the manual cache step
+
+      - name: Install dependencies
+        shell: bash
+        run: pip install -r requirements_dev.txt
+
+      - name: Install the library (editable)
+        shell: bash
+        run: pip install -e .

Notes:

  • setup-python’s pip cache is cross-platform; the previous ~/.cache/pip path doesn’t help on Windows and is no longer needed.
  • Installing editable eases local imports and speeds iterative testing.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In .github/workflows/tests_and_coverage.yml around lines 24–35, the workflow
currently caches and installs pip deps in the wrong order and uses a hardcoded
~/.cache/pip path; reorder steps so the cache is enabled first (use the
setup-python pip cache mechanism instead of the platform-specific ~/.cache/pip),
then run pip install -r requirements_dev.txt to install dev dependencies, and
only after that install the package in editable mode (pip install -e .) for test
jobs to avoid duplicate dependency resolution and speed iterations; update cache
key/restore-keys accordingly and remove the ~/.cache/pip path entry.

- name: Print all libs
shell: bash
run: pip list

- name: Run tests and show coverage on the command line
run: |
coverage run --source=transfunctions --omit="*tests*" -m pytest --cache-clear --assert=plain && coverage report -m --fail-under=94
coverage xml

Comment on lines +40 to +44
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Run tests once with branch coverage to save CI time

You run the entire test suite twice. Combine into a single run using --branch and produce both the console report and XML.

-      - name: Run tests and show coverage on the command line
-        run: |
-          coverage run --source=transfunctions --omit="*tests*" -m pytest --cache-clear --assert=plain && coverage report -m --fail-under=94
-          coverage xml
+      - name: Run tests and show coverage on the command line (branch coverage)
+        run: |
+          coverage run --branch --source=transfunctions --omit="*tests*" -m pytest --cache-clear --assert=plain
+          coverage report -m --fail-under=94
+          coverage xml
@@
-      - name: Run tests and show the branch coverage on the command line
-        run: coverage run --branch --source=transfunctions --omit="*tests*" -m pytest --cache-clear --assert=plain && coverage report -m --fail-under=94
+      # Removed duplicate full test run; branch coverage is already collected above

Also applies to: 54-55

🤖 Prompt for AI Agents
.github/workflows/tests_and_coverage.yml lines 40-44 (and similarly 54-55): the
workflow currently runs tests twice to produce console coverage and XML; instead
run pytest once with branch coverage and then produce both reports. Replace the
two-step runs with a single command that uses coverage run --branch
--source=transfunctions --omit="*tests*" -m pytest --cache-clear --assert=plain
&& coverage report -m --fail-under=94 && coverage xml so tests execute only once
and both console report and XML are generated.

- name: Upload coverage to Coveralls
if: runner.os == 'Linux'
env:
COVERALLS_REPO_TOKEN: ${{secrets.COVERALLS_REPO_TOKEN}}
uses: coverallsapp/github-action@v2
with:
format: cobertura
file: coverage.xml

- name: Run tests and show the branch coverage on the command line
run: coverage run --branch --source=transfunctions --omit="*tests*" -m pytest --cache-clear --assert=plain && coverage report -m --fail-under=94
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ test.py
dist
venv
.venv
venv3
build
.ruff_cache
.mypy_cache
Expand All @@ -14,4 +15,5 @@ uv.lock
.history
.vscode/
.idea/
temp*
.ropeproject
node_modules
4 changes: 0 additions & 4 deletions .ruff.toml

This file was deleted.

39 changes: 38 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
![logo](https://raw.githubusercontent.com/pomponchik/transfunctions/develop/docs/assets/logo_2.svg)
<details>
<summary>ⓘ</summary>

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

</details>

![logo](https://raw.githubusercontent.com/pomponchik/transfunctions/develop/docs/assets/logo_2.svg)

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.


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


## Quick start
Expand Down Expand Up @@ -297,3 +303,34 @@ However, it is not completely free. The fact is that this mode uses a special tr
- 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.

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.


## Typing

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.

There are 2 main difficulties in developing typing here:

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

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.

If you use the keyword `yield from`, you need to call the function `yield_from_it` instead:

```python
from transfunctions import yield_it

@superfunction
def my_superfunction():
print('so, ', end='')
with sync_context:
print("it's just usual function!")
with async_context:
print("it's an async function!")
with generator_context:
print("it's a generator function!")
yield_from_it([1, 2, 3])
```

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.
24 changes: 14 additions & 10 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@ build-backend = "setuptools.build_meta"

[project]
name = "transfunctions"
version = "0.0.8"
authors = [
{ name="Evgeniy Blinov", email="zheni-b@yandex.ru" },
]
version = "0.0.9"
authors = [{ name = "Evgeniy Blinov", email = "zheni-b@yandex.ru" }]
description = 'Say NO to Python fragmentation on sync and async'
readme = "README.md"
requires-python = ">=3.8"
dependencies = [
'displayhooks>=0.0.4',
'displayhooks>=0.0.5',
'dill==0.4.0',
'typing_extensions ; python_version <= "3.10"',
]
Expand All @@ -30,6 +28,9 @@ classifiers = [
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: 3.13',
'Programming Language :: Python :: 3.14',
'Programming Language :: Python :: Free Threading',
'Programming Language :: Python :: Free Threading :: 3 - Stable',
'License :: OSI Approved :: MIT License',
'Intended Audience :: Developers',
'Topic :: Software Development :: Libraries',
Expand All @@ -50,13 +51,16 @@ keywords = [
"transfunctions" = ["py.typed"]

[tool.mutmut]
paths_to_mutate="transfunctions"
runner="pytest"
paths_to_mutate = "transfunctions"
runner = "pytest"

[tool.pytest.ini_options]
markers = [
"mypy_testing",
]
markers = ["mypy_testing"]

[tool.ruff]
lint.ignore = ['E501', 'E712', 'PTH123', 'PTH118', 'PLR2004', 'PTH107', 'SIM105', 'SIM102', 'RET503', 'PLR0912', 'C901', 'RUF001']
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"]
format.quote-style = "single"

[project.urls]
'Source' = 'https://github.com/pomponchik/transfunctions'
Expand Down
2 changes: 1 addition & 1 deletion requirements_dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ mypy==1.14.1
pytest-mypy-testing==0.1.3
ruff==0.9.9
mutmut==3.2.3
full_match==0.0.2
full_match==0.0.3
12 changes: 6 additions & 6 deletions tests/documentation/test_readme.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@
from contextlib import redirect_stdout

from transfunctions import (
transfunction,
sync_context,
async_context,
generator_context,
sync_context,
transfunction,
)


def test_quick_start():
@transfunction
def template():
print('so, ', end='')
print('so, ', end='') # noqa: T201
with sync_context:
print("it's just usual function!")
print("it's just usual function!") # noqa: T201
with async_context:
print("it's an async function!")
print("it's an async function!") # noqa: T201
with generator_context:
print("it's a generator function!")
print("it's a generator function!") # noqa: T201
yield

buffer = io.StringIO()
Expand Down
1 change: 1 addition & 0 deletions tests/typing/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# noqa: A005
Loading
Loading