or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/pypi-unasync

A code transformation tool that automatically converts asynchronous Python code into synchronous equivalents.

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
pypipkg:pypi/unasync@0.6.x

To install, run

npx @tessl/cli install tessl/pypi-unasync@0.6.0

index.mddocs/

Unasync

A code transformation tool that automatically converts asynchronous Python code into synchronous equivalents by parsing and rewriting Python source files. Unasync enables developers to maintain a single asynchronous codebase while generating both async and sync versions of their libraries, supporting customizable transformation rules and seamless integration with setuptools build process.

Package Information

  • Package Name: unasync
  • Language: Python
  • Installation: pip install unasync
  • Dependencies: tokenize_rt, setuptools
  • Python Support: 3.8, 3.9, 3.10, 3.11, 3.12
  • Platforms: Linux, macOS, Windows

Core Imports

import unasync

Specific imports:

from unasync import Rule, unasync_files, cmdclass_build_py

Basic Usage

Setuptools Integration (Most Common)

import setuptools
import unasync

setuptools.setup(
    name="your_package",
    # ... other setup configuration
    cmdclass={'build_py': unasync.cmdclass_build_py()},
)

This automatically transforms files from _async/ directories to _sync/ directories during package build.

Custom Directory Rules

import setuptools
import unasync

# Custom transformation rules
rules = [
    # Transform ahip -> hip instead of _async -> _sync
    unasync.Rule("/ahip/", "/hip/"),
    
    # More specific rule with additional token replacements
    unasync.Rule("/ahip/tests/", "/hip/tests/", 
                 additional_replacements={"ahip": "hip"}),
]

setuptools.setup(
    name="your_package",
    cmdclass={'build_py': unasync.cmdclass_build_py(rules=rules)},
)

Direct File Transformation

import unasync

# Transform specific files
unasync.unasync_files(
    ["/path/to/async/file1.py", "/path/to/async/file2.py"],
    rules=[unasync.Rule("/async/", "/sync/")]
)

Capabilities

Rule Creation and Configuration

Creates transformation rules that define how async code should be converted to sync code, including directory mappings and custom token replacements.

class Rule:
    def __init__(self, fromdir, todir, additional_replacements=None):
        """
        A single set of rules for 'unasync'ing file(s).

        Parameters:
        - fromdir: Source directory path containing async code (e.g., "/_async/")
        - todir: Target directory path for generated sync code (e.g., "/_sync/")
        - additional_replacements: Optional dict of custom token replacements
                                 beyond the default async-to-sync mappings

        The Rule handles path matching, file transformation, and token replacement
        according to the specified directory mapping and token rules.
        """

File Transformation

Transforms a list of Python files from async to sync using specified rules, handling tokenization, parsing, and code rewriting.

def unasync_files(fpath_list, rules):
    """
    Transform a list of files from async to sync using provided rules.

    Parameters:
    - fpath_list: List of file paths to transform
    - rules: List of Rule instances defining transformation rules

    Returns:
    None

    The function applies the most specific matching rule for each file,
    performs tokenization and transformation, and writes sync versions
    to the appropriate output directories with proper encoding preservation.
    """

Setuptools Integration

Creates a custom build_py class for seamless integration with setuptools, automatically transforming async code during the package build process.

def cmdclass_build_py(rules=(Rule("/_async/", "/_sync/"),)):
    """
    Creates a 'build_py' class for use within setuptools 'cmdclass' parameter.

    Parameters:
    - rules: Tuple of Rule instances (defaults to (Rule("/_async/", "/_sync/"),))

    Returns:
    Custom build_py class for setuptools integration

    The returned class extends setuptools.command.build_py to automatically
    transform async files to sync versions during the build process.
    Default rule transforms files from '/_async/' to '/_sync/' directories.
    """

Transformation Rules

Default Token Mappings

Unasync applies these default async-to-sync transformations:

  • __aenter____enter__
  • __aexit____exit__
  • __aiter____iter__
  • __anext____next__
  • asynccontextmanagercontextmanager
  • AsyncIterableIterable
  • AsyncIteratorIterator
  • AsyncGeneratorGenerator
  • StopAsyncIterationStopIteration

Async/Await Removal

  • async keywords are removed from function definitions and context managers
  • await keywords are removed from expressions
  • Class names prefixed with Async (followed by uppercase letter) become Sync

Custom Token Replacements

# Example: Replace custom async tokens
custom_rule = unasync.Rule(
    "/my_async/", 
    "/my_sync/", 
    additional_replacements={
        "AsyncClient": "SyncClient",
        "async_method": "sync_method",
        "ASYNC_CONSTANT": "SYNC_CONSTANT"
    }
)

File Organization Patterns

Standard Pattern

src/
├── mypackage/
│   ├── __init__.py
│   ├── _async/           # Async source code
│   │   ├── __init__.py
│   │   ├── client.py
│   │   └── utils.py
│   └── _sync/            # Generated sync code (auto-created)
│       ├── __init__.py
│       ├── client.py
│       └── utils.py

Custom Pattern

src/
├── mypackage/
│   ├── __init__.py
│   ├── ahip/             # Custom async directory
│   │   ├── __init__.py
│   │   └── client.py
│   └── hip/              # Custom sync directory (auto-created)
│       ├── __init__.py
│       └── client.py

Error Handling

Unasync handles various scenarios gracefully:

  • Encoding Detection: Automatically detects and preserves file encoding
  • Directory Creation: Creates output directories as needed
  • Path Normalization: Handles cross-platform path separators
  • Token Parsing: Robust tokenization using tokenize_rt
  • Rule Precedence: More specific directory patterns take precedence

Advanced Usage

Multiple Rule Sets

rules = [
    # General transformation
    unasync.Rule("/_async/", "/_sync/"),
    
    # Specific transformation with custom replacements
    unasync.Rule("/async_tests/", "/sync_tests/", 
                 additional_replacements={"TestAsync": "TestSync"}),
    
    # Most specific rule (takes precedence)
    unasync.Rule("/async_tests/integration/", "/sync_tests/integration/",
                 additional_replacements={"IntegrationAsync": "IntegrationSync"})
]

Rule Matching

Rules are matched by directory path specificity:

  • More specific paths (longer directory segments) take precedence
  • Rule matching searches all subdirectory positions in file paths
  • First matching rule at the highest specificity level is applied

Integration Examples

# Standard setup.py integration
from setuptools import setup, find_packages
import unasync

setup(
    name="my-async-package",
    packages=find_packages("src"),
    package_dir={"": "src"},
    cmdclass={'build_py': unasync.cmdclass_build_py()},
    install_requires=['unasync'],
)
# pyproject.toml with custom build backend
[build-system]
requires = ["setuptools", "unasync"]
build-backend = "setuptools.build_meta"

[tool.setuptools.cmdclass]
build_py = "unasync:cmdclass_build_py"