or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

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

0

# Unasync

1

2

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.

3

4

## Package Information

5

6

- **Package Name**: unasync

7

- **Language**: Python

8

- **Installation**: `pip install unasync`

9

- **Dependencies**: `tokenize_rt`, `setuptools`

10

- **Python Support**: 3.8, 3.9, 3.10, 3.11, 3.12

11

- **Platforms**: Linux, macOS, Windows

12

13

## Core Imports

14

15

```python

16

import unasync

17

```

18

19

Specific imports:

20

21

```python

22

from unasync import Rule, unasync_files, cmdclass_build_py

23

```

24

25

## Basic Usage

26

27

### Setuptools Integration (Most Common)

28

29

```python

30

import setuptools

31

import unasync

32

33

setuptools.setup(

34

name="your_package",

35

# ... other setup configuration

36

cmdclass={'build_py': unasync.cmdclass_build_py()},

37

)

38

```

39

40

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

41

42

### Custom Directory Rules

43

44

```python

45

import setuptools

46

import unasync

47

48

# Custom transformation rules

49

rules = [

50

# Transform ahip -> hip instead of _async -> _sync

51

unasync.Rule("/ahip/", "/hip/"),

52

53

# More specific rule with additional token replacements

54

unasync.Rule("/ahip/tests/", "/hip/tests/",

55

additional_replacements={"ahip": "hip"}),

56

]

57

58

setuptools.setup(

59

name="your_package",

60

cmdclass={'build_py': unasync.cmdclass_build_py(rules=rules)},

61

)

62

```

63

64

### Direct File Transformation

65

66

```python

67

import unasync

68

69

# Transform specific files

70

unasync.unasync_files(

71

["/path/to/async/file1.py", "/path/to/async/file2.py"],

72

rules=[unasync.Rule("/async/", "/sync/")]

73

)

74

```

75

76

## Capabilities

77

78

### Rule Creation and Configuration

79

80

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

81

82

```python { .api }

83

class Rule:

84

def __init__(self, fromdir, todir, additional_replacements=None):

85

"""

86

A single set of rules for 'unasync'ing file(s).

87

88

Parameters:

89

- fromdir: Source directory path containing async code (e.g., "/_async/")

90

- todir: Target directory path for generated sync code (e.g., "/_sync/")

91

- additional_replacements: Optional dict of custom token replacements

92

beyond the default async-to-sync mappings

93

94

The Rule handles path matching, file transformation, and token replacement

95

according to the specified directory mapping and token rules.

96

"""

97

```

98

99

### File Transformation

100

101

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

102

103

```python { .api }

104

def unasync_files(fpath_list, rules):

105

"""

106

Transform a list of files from async to sync using provided rules.

107

108

Parameters:

109

- fpath_list: List of file paths to transform

110

- rules: List of Rule instances defining transformation rules

111

112

Returns:

113

None

114

115

The function applies the most specific matching rule for each file,

116

performs tokenization and transformation, and writes sync versions

117

to the appropriate output directories with proper encoding preservation.

118

"""

119

```

120

121

### Setuptools Integration

122

123

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

124

125

```python { .api }

126

def cmdclass_build_py(rules=(Rule("/_async/", "/_sync/"),)):

127

"""

128

Creates a 'build_py' class for use within setuptools 'cmdclass' parameter.

129

130

Parameters:

131

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

132

133

Returns:

134

Custom build_py class for setuptools integration

135

136

The returned class extends setuptools.command.build_py to automatically

137

transform async files to sync versions during the build process.

138

Default rule transforms files from '/_async/' to '/_sync/' directories.

139

"""

140

```

141

142

## Transformation Rules

143

144

### Default Token Mappings

145

146

Unasync applies these default async-to-sync transformations:

147

148

- `__aenter__``__enter__`

149

- `__aexit__``__exit__`

150

- `__aiter__``__iter__`

151

- `__anext__``__next__`

152

- `asynccontextmanager``contextmanager`

153

- `AsyncIterable``Iterable`

154

- `AsyncIterator``Iterator`

155

- `AsyncGenerator``Generator`

156

- `StopAsyncIteration``StopIteration`

157

158

### Async/Await Removal

159

160

- `async` keywords are removed from function definitions and context managers

161

- `await` keywords are removed from expressions

162

- Class names prefixed with `Async` (followed by uppercase letter) become `Sync`

163

164

### Custom Token Replacements

165

166

```python

167

# Example: Replace custom async tokens

168

custom_rule = unasync.Rule(

169

"/my_async/",

170

"/my_sync/",

171

additional_replacements={

172

"AsyncClient": "SyncClient",

173

"async_method": "sync_method",

174

"ASYNC_CONSTANT": "SYNC_CONSTANT"

175

}

176

)

177

```

178

179

## File Organization Patterns

180

181

### Standard Pattern

182

```

183

src/

184

├── mypackage/

185

│ ├── __init__.py

186

│ ├── _async/ # Async source code

187

│ │ ├── __init__.py

188

│ │ ├── client.py

189

│ │ └── utils.py

190

│ └── _sync/ # Generated sync code (auto-created)

191

│ ├── __init__.py

192

│ ├── client.py

193

│ └── utils.py

194

```

195

196

### Custom Pattern

197

```

198

src/

199

├── mypackage/

200

│ ├── __init__.py

201

│ ├── ahip/ # Custom async directory

202

│ │ ├── __init__.py

203

│ │ └── client.py

204

│ └── hip/ # Custom sync directory (auto-created)

205

│ ├── __init__.py

206

│ └── client.py

207

```

208

209

## Error Handling

210

211

Unasync handles various scenarios gracefully:

212

213

- **Encoding Detection**: Automatically detects and preserves file encoding

214

- **Directory Creation**: Creates output directories as needed

215

- **Path Normalization**: Handles cross-platform path separators

216

- **Token Parsing**: Robust tokenization using `tokenize_rt`

217

- **Rule Precedence**: More specific directory patterns take precedence

218

219

## Advanced Usage

220

221

### Multiple Rule Sets

222

223

```python

224

rules = [

225

# General transformation

226

unasync.Rule("/_async/", "/_sync/"),

227

228

# Specific transformation with custom replacements

229

unasync.Rule("/async_tests/", "/sync_tests/",

230

additional_replacements={"TestAsync": "TestSync"}),

231

232

# Most specific rule (takes precedence)

233

unasync.Rule("/async_tests/integration/", "/sync_tests/integration/",

234

additional_replacements={"IntegrationAsync": "IntegrationSync"})

235

]

236

```

237

238

### Rule Matching

239

240

Rules are matched by directory path specificity:

241

- More specific paths (longer directory segments) take precedence

242

- Rule matching searches all subdirectory positions in file paths

243

- First matching rule at the highest specificity level is applied

244

245

### Integration Examples

246

247

```python

248

# Standard setup.py integration

249

from setuptools import setup, find_packages

250

import unasync

251

252

setup(

253

name="my-async-package",

254

packages=find_packages("src"),

255

package_dir={"": "src"},

256

cmdclass={'build_py': unasync.cmdclass_build_py()},

257

install_requires=['unasync'],

258

)

259

```

260

261

```python

262

# pyproject.toml with custom build backend

263

[build-system]

264

requires = ["setuptools", "unasync"]

265

build-backend = "setuptools.build_meta"

266

267

[tool.setuptools.cmdclass]

268

build_py = "unasync:cmdclass_build_py"

269

```