pydorust 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pydorust-0.1.0/.forgejo/workflows/ci.yml +42 -0
- pydorust-0.1.0/.forgejo/workflows/release.yml +27 -0
- pydorust-0.1.0/.gitignore +17 -0
- pydorust-0.1.0/.python-version +1 -0
- pydorust-0.1.0/PKG-INFO +213 -0
- pydorust-0.1.0/README.md +195 -0
- pydorust-0.1.0/flake.lock +61 -0
- pydorust-0.1.0/flake.nix +35 -0
- pydorust-0.1.0/pydorust/__init__.py +28 -0
- pydorust-0.1.0/pydorust/_internals/__init__.py +10 -0
- pydorust-0.1.0/pydorust/_internals/num_macros.py +173 -0
- pydorust-0.1.0/pydorust/_internals/num_macros.pyi +15 -0
- pydorust-0.1.0/pydorust/adt.py +72 -0
- pydorust-0.1.0/pydorust/lock/__init__.py +15 -0
- pydorust-0.1.0/pydorust/lock/cell.py +16 -0
- pydorust-0.1.0/pydorust/lock/lazy_lock.py +26 -0
- pydorust-0.1.0/pydorust/lock/once_lock.py +25 -0
- pydorust-0.1.0/pydorust/lock/ref_cell.py +61 -0
- pydorust-0.1.0/pydorust/lock/weak.py +14 -0
- pydorust-0.1.0/pydorust/macros/__init__.py +18 -0
- pydorust-0.1.0/pydorust/macros/__main__.py +205 -0
- pydorust-0.1.0/pydorust/macros/builtins/__init__.py +31 -0
- pydorust-0.1.0/pydorust/macros/builtins/comptime.py +29 -0
- pydorust-0.1.0/pydorust/macros/builtins/declarative.py +123 -0
- pydorust-0.1.0/pydorust/macros/builtins/delete.py +20 -0
- pydorust-0.1.0/pydorust/macros/builtins/env.py +24 -0
- pydorust-0.1.0/pydorust/macros/builtins/ident.py +26 -0
- pydorust-0.1.0/pydorust/macros/builtins/include.py +50 -0
- pydorust-0.1.0/pydorust/macros/builtins/meta.py +46 -0
- pydorust-0.1.0/pydorust/macros/builtins/quote.py +18 -0
- pydorust-0.1.0/pydorust/macros/builtins/str.py +14 -0
- pydorust-0.1.0/pydorust/macros/call.py +27 -0
- pydorust-0.1.0/pydorust/macros/context.py +133 -0
- pydorust-0.1.0/pydorust/macros/expander.py +30 -0
- pydorust-0.1.0/pydorust/macros/importer.py +85 -0
- pydorust-0.1.0/pydorust/macros/macro.py +12 -0
- pydorust-0.1.0/pydorust/macros/registry.py +11 -0
- pydorust-0.1.0/pydorust/macros/sandbox.py +30 -0
- pydorust-0.1.0/pydorust/macros/transformer.py +288 -0
- pydorust-0.1.0/pydorust/mem.py +57 -0
- pydorust-0.1.0/pydorust/num.py +29 -0
- pydorust-0.1.0/pydorust/num.pyi +266 -0
- pydorust-0.1.0/pydorust/panics.py +46 -0
- pydorust-0.1.0/pydorust/plugins/__init__.py +0 -0
- pydorust-0.1.0/pydorust/plugins/hatch.py +68 -0
- pydorust-0.1.0/pydorust/plugins/pytest.py +11 -0
- pydorust-0.1.0/pydorust/py.typed +0 -0
- pydorust-0.1.0/pydorust/sync/__init__.py +10 -0
- pydorust-0.1.0/pydorust/sync/mutex.py +53 -0
- pydorust-0.1.0/pydorust/sync/rw_lock.py +100 -0
- pydorust-0.1.0/pydorust/trait/__init__.py +11 -0
- pydorust-0.1.0/pydorust/trait/constants.py +5 -0
- pydorust-0.1.0/pydorust/trait/core.py +172 -0
- pydorust-0.1.0/pydorust/trait/decorators.py +111 -0
- pydorust-0.1.0/pydorust/types/__init__.py +16 -0
- pydorust-0.1.0/pydorust/types/iter.py +127 -0
- pydorust-0.1.0/pydorust/types/option.py +108 -0
- pydorust-0.1.0/pydorust/types/result.py +139 -0
- pydorust-0.1.0/pydorust/unsafe.py +319 -0
- pydorust-0.1.0/pydorust/utilities.py +36 -0
- pydorust-0.1.0/pyproject.toml +55 -0
- pydorust-0.1.0/scratch/expanded_num.py +1464 -0
- pydorust-0.1.0/scripts/generate_unsafe.py +61 -0
- pydorust-0.1.0/tests/conftest.py +13 -0
- pydorust-0.1.0/tests/test_lock.py +98 -0
- pydorust-0.1.0/tests/test_num.py +98 -0
- pydorust-0.1.0/tests/test_option.py +155 -0
- pydorust-0.1.0/tests/test_result.py +182 -0
- pydorust-0.1.0/tests/test_sync.py +82 -0
- pydorust-0.1.0/uv.lock +313 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
pull_request:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- name: Checkout code
|
|
14
|
+
uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Install uv
|
|
17
|
+
uses: astral-sh/setup-uv@v3
|
|
18
|
+
with:
|
|
19
|
+
version: "latest"
|
|
20
|
+
enable-cache: true
|
|
21
|
+
|
|
22
|
+
- name: Set up Python
|
|
23
|
+
uses: actions/setup-python@v5
|
|
24
|
+
with:
|
|
25
|
+
python-version-file: ".python-version"
|
|
26
|
+
|
|
27
|
+
- name: Install dependencies
|
|
28
|
+
run: uv sync --all-extras --dev
|
|
29
|
+
|
|
30
|
+
- name: Run Ruff
|
|
31
|
+
run: uv run ruff check .
|
|
32
|
+
|
|
33
|
+
- name: Run Mypy
|
|
34
|
+
run: uv run mypy rustipy
|
|
35
|
+
|
|
36
|
+
- name: Run Pylint
|
|
37
|
+
run: uv run pylint rustipy
|
|
38
|
+
|
|
39
|
+
- name: Run Tests with Coverage
|
|
40
|
+
run: uv run pytest --cov=rustipy --cov-report=xml
|
|
41
|
+
env:
|
|
42
|
+
PYTHONPATH: .
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: Publish Package
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
build-and-publish:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- name: Checkout code
|
|
13
|
+
uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- name: Install uv
|
|
16
|
+
uses: astral-sh/setup-uv@v3
|
|
17
|
+
|
|
18
|
+
- name: Set up Python
|
|
19
|
+
uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version-file: ".python-version"
|
|
22
|
+
|
|
23
|
+
- name: Build package
|
|
24
|
+
run: uv build
|
|
25
|
+
|
|
26
|
+
- name: Publish to PyPI
|
|
27
|
+
run: uv publish --token ${{ secrets.PYPI_TOKEN }}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.10
|
pydorust-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: pydorust
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Python library inspired by Rust
|
|
5
|
+
Project-URL: Homepage, https://git.ruject.fun/RuJect/pydorust
|
|
6
|
+
Project-URL: Repository, https://git.ruject.fun/RuJect/pydorust.git
|
|
7
|
+
Project-URL: Issues, https://git.ruject.fun/RuJect/pydorust/issues
|
|
8
|
+
Author-email: rus07tam <rus07tam+contact@ruject.fun>
|
|
9
|
+
License: MIT
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
|
+
Requires-Dist: exceptiongroup>=1.3.1
|
|
15
|
+
Requires-Dist: pytest>=9.1.1
|
|
16
|
+
Requires-Dist: typing-extensions>=4.16.0
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# pydorust
|
|
20
|
+
|
|
21
|
+
A Python library bringing Rust programming language paradigms, semantics, and strict typing patterns to Python. Rather than attempting to enforce strict physical memory safety (which is fundamentally limited by Python's nature), `pydorust` is designed to provide familiar Rust semantics and structural patterns like macros, traits, algebraic data types, strict error handling (panics, `Option`, `Result`), and semantic synchronization primitives.
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install pydorust
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## The Macro System (`pydorust.macros`)
|
|
32
|
+
|
|
33
|
+
The Macro system brings advanced, abstract metaprogramming to Python, conceptually similar to Rust's macro system. It allows you to transform syntax and generate code dynamically in a secure environment.
|
|
34
|
+
|
|
35
|
+
### CLI & Plugins
|
|
36
|
+
|
|
37
|
+
Macros can be evaluated dynamically at runtime, but their true power shines when transforming the Python AST prior to execution. `pydorust` includes a dedicated CLI utility (`python -m pydorust.macros`) and plugins (for `hatch` and `pytest`) to seamlessly expand macros as part of your build step or test suite.
|
|
38
|
+
|
|
39
|
+
### Core Macro APIs
|
|
40
|
+
|
|
41
|
+
- **`@m_define`**: The standard decorator used to register an AST macro. It takes a function that accepts a `MacroContext` and AST nodes, and returns transformed AST nodes. This is an imperative way to write macros (directly mutating AST).
|
|
42
|
+
- **`@m_define_declarative`**: A specialized, built-in macro used to define *other* macros declaratively using a template approach (inspired by Rust's `macro_rules!`). You write a standard Python function body where arguments act as variables. In the template body, variable references prefixed with `__q_` (e.g., `__q_myvar`) are replaced dynamically by the AST inputs.
|
|
43
|
+
- **`@m_decorator`**: A helper that wraps a macro so it can be used directly as a standard Python decorator. Functionally similar to `m_derive`, but utilizes native Python decorator syntax to pass the decorated function/class as the node.
|
|
44
|
+
- **`m_derive`**: Invokes a macro and automatically passes the *next* AST node (the one immediately following the `m_derive` call) as the primary node, alongside any additional arguments.
|
|
45
|
+
- **`m_call`**: Directly invokes a macro with the provided arguments, where the very first argument explicitly acts as the AST node.
|
|
46
|
+
|
|
47
|
+
### Example: Defining a Custom Macro
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
import ast
|
|
51
|
+
from pydorust.macros import m_define, MacroContext, MacroResult
|
|
52
|
+
|
|
53
|
+
@m_define
|
|
54
|
+
def compile_error(ctx: MacroContext, node: ast.AST, /, *args: ast.AST) -> MacroResult:
|
|
55
|
+
# Extracts the string literal argument passed to the macro
|
|
56
|
+
msg = ctx.except_literal(str, node)
|
|
57
|
+
raise RuntimeError(
|
|
58
|
+
f"Compile Error at {ctx.filename}:{getattr(node, 'lineno', 1)}: {msg}"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# When expanded via CLI, plugins, or m_call, this raises an error based on the AST provided.
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Traits System (`pydorust.trait`)
|
|
67
|
+
|
|
68
|
+
`pydorust` introduces Rust-style **Traits**, allowing you to decouple method definitions from class inheritance. This enables true ad-hoc polymorphism.
|
|
69
|
+
|
|
70
|
+
- **`Trait`**: The base class for defining new traits. Use decorators like `@trait_method` (for requirements) and `@default_method` (for defaults).
|
|
71
|
+
- **`@impl`**: Implements a trait for a specific data type.
|
|
72
|
+
|
|
73
|
+
### Example: Creating and Implementing a Trait
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from pydorust.trait import Trait, impl, trait_method, default_method
|
|
77
|
+
|
|
78
|
+
class Display(Trait):
|
|
79
|
+
@trait_method
|
|
80
|
+
def fmt(self) -> str:
|
|
81
|
+
"""Required method for formatting."""
|
|
82
|
+
|
|
83
|
+
@default_method
|
|
84
|
+
def print(self) -> None:
|
|
85
|
+
"""Default provided method."""
|
|
86
|
+
print(self.fmt())
|
|
87
|
+
|
|
88
|
+
# Implement `Display` for the built-in `int` type!
|
|
89
|
+
@impl(Display, for_type=int)
|
|
90
|
+
class IntDisplay:
|
|
91
|
+
def fmt(self: int) -> str:
|
|
92
|
+
return f"Integer value: {self}"
|
|
93
|
+
|
|
94
|
+
# Call the trait method directly
|
|
95
|
+
Display.print(42) # Outputs: Integer value: 42
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Types (`pydorust.types`)
|
|
101
|
+
|
|
102
|
+
Rust is famous for eliminating `NullReferenceException` using the `Option` enum and handling errors gracefully using `Result`. `pydorust` implements these explicitly.
|
|
103
|
+
|
|
104
|
+
### `Option` (Replacing `None`)
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from pydorust.types import Option, Some, Null, ret_option
|
|
108
|
+
|
|
109
|
+
# Using a decorator to convert returns of None into `Null`, and values into `Some`
|
|
110
|
+
@ret_option
|
|
111
|
+
def divide(a: float, b: float) -> float | None:
|
|
112
|
+
if b == 0.0:
|
|
113
|
+
return None
|
|
114
|
+
return a / b
|
|
115
|
+
|
|
116
|
+
result: Option[float] = divide(10.0, 2.0)
|
|
117
|
+
print(result.unwrap_or(0.0)) # 5.0
|
|
118
|
+
|
|
119
|
+
bad_result = divide(10.0, 0.0)
|
|
120
|
+
print(bad_result.is_null()) # True
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### `Result` (Replacing Exceptions)
|
|
124
|
+
|
|
125
|
+
The `Result` module provides `Ok(value)` and `Err(error)`, along with combinators like `.map()`, `.and_then()`, and `.unwrap()` to cleanly chain operations that might fail without massive `try/except` blocks.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Thread Synchronization (`pydorust.sync`)
|
|
130
|
+
|
|
131
|
+
Provides semantic synchronization primitives mirroring Rust's standard library. While these tools cannot physically isolate or protect the inner state of Python objects from being mutated externally, they enforce semantic structure. By utilizing Context Managers as guards, `pydorust` helps prevent lock leaks and logically synchronizes your concurrent code.
|
|
132
|
+
|
|
133
|
+
### Example: `Mutex`
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
import threading
|
|
137
|
+
from pydorust.sync import Mutex
|
|
138
|
+
|
|
139
|
+
# Wrap the data inside the Mutex
|
|
140
|
+
data = Mutex([1, 2, 3])
|
|
141
|
+
|
|
142
|
+
def worker():
|
|
143
|
+
# .lock() returns a MutexGuard. The lock is released when the block exits.
|
|
144
|
+
with data.lock() as guard:
|
|
145
|
+
guard.value.append(4)
|
|
146
|
+
|
|
147
|
+
t = threading.Thread(target=worker)
|
|
148
|
+
t.start(); t.join()
|
|
149
|
+
|
|
150
|
+
with data.lock() as guard:
|
|
151
|
+
print(guard.value) # [1, 2, 3, 4]
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
*(Also includes `RwLock` for multi-reader / single-writer scenarios).*
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Algebraic Data Types (`pydorust.adt`)
|
|
159
|
+
|
|
160
|
+
Using the `Variant` and `Enum` classes, you can build tagged unions/variants to guarantee data shapes securely.
|
|
161
|
+
|
|
162
|
+
### Example
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
from pydorust.adt import Variant, Enum
|
|
166
|
+
|
|
167
|
+
class Point2D(Variant):
|
|
168
|
+
x: int
|
|
169
|
+
y: int
|
|
170
|
+
|
|
171
|
+
class Point3D(Variant):
|
|
172
|
+
x: int
|
|
173
|
+
y: int
|
|
174
|
+
z: int
|
|
175
|
+
|
|
176
|
+
class ShapePoint(Enum):
|
|
177
|
+
D2 = Point2D
|
|
178
|
+
D3 = Point3D
|
|
179
|
+
|
|
180
|
+
p = ShapePoint.D2(x=10, y=20)
|
|
181
|
+
print(isinstance(p, ShapePoint)) # True
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Other Features
|
|
187
|
+
|
|
188
|
+
### Unrecoverable Errors (`pydorust.panics`)
|
|
189
|
+
|
|
190
|
+
Provides deterministic app termination for unrecoverable states.
|
|
191
|
+
|
|
192
|
+
- `panic("message")`: Immediately terminates the execution.
|
|
193
|
+
- `unimplemented()`: Drop-in for missing functionality (crashes if reached).
|
|
194
|
+
- `unreachable()`: For code paths that logically shouldn't be executed.
|
|
195
|
+
|
|
196
|
+
### Unsafe Markers (`pydorust.unsafe`)
|
|
197
|
+
|
|
198
|
+
In Rust, dangerous operations that break memory safety guarantees must be wrapped in `unsafe {}`. `pydorust` emulates this semantic contract using decorators.
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
from pydorust.unsafe import unsafe_func, unsafe_call
|
|
202
|
+
|
|
203
|
+
@unsafe_func
|
|
204
|
+
def dangerous_operation():
|
|
205
|
+
print("Doing something unsafe...")
|
|
206
|
+
|
|
207
|
+
# dangerous_operation() # type error
|
|
208
|
+
unsafe_call(dangerous_operation)
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Low-Level Memory (`pydorust.mem`)
|
|
212
|
+
|
|
213
|
+
Exposes `unsafe` wrappers around `ctypes.pythonapi` memory management tools, granting you direct control over allocations with `alloc(size)` and `free(ptr)`. Always use carefully via `RawBuffer` or explicit `unsafe_call`.
|
pydorust-0.1.0/README.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# pydorust
|
|
2
|
+
|
|
3
|
+
A Python library bringing Rust programming language paradigms, semantics, and strict typing patterns to Python. Rather than attempting to enforce strict physical memory safety (which is fundamentally limited by Python's nature), `pydorust` is designed to provide familiar Rust semantics and structural patterns like macros, traits, algebraic data types, strict error handling (panics, `Option`, `Result`), and semantic synchronization primitives.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install pydorust
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## The Macro System (`pydorust.macros`)
|
|
14
|
+
|
|
15
|
+
The Macro system brings advanced, abstract metaprogramming to Python, conceptually similar to Rust's macro system. It allows you to transform syntax and generate code dynamically in a secure environment.
|
|
16
|
+
|
|
17
|
+
### CLI & Plugins
|
|
18
|
+
|
|
19
|
+
Macros can be evaluated dynamically at runtime, but their true power shines when transforming the Python AST prior to execution. `pydorust` includes a dedicated CLI utility (`python -m pydorust.macros`) and plugins (for `hatch` and `pytest`) to seamlessly expand macros as part of your build step or test suite.
|
|
20
|
+
|
|
21
|
+
### Core Macro APIs
|
|
22
|
+
|
|
23
|
+
- **`@m_define`**: The standard decorator used to register an AST macro. It takes a function that accepts a `MacroContext` and AST nodes, and returns transformed AST nodes. This is an imperative way to write macros (directly mutating AST).
|
|
24
|
+
- **`@m_define_declarative`**: A specialized, built-in macro used to define *other* macros declaratively using a template approach (inspired by Rust's `macro_rules!`). You write a standard Python function body where arguments act as variables. In the template body, variable references prefixed with `__q_` (e.g., `__q_myvar`) are replaced dynamically by the AST inputs.
|
|
25
|
+
- **`@m_decorator`**: A helper that wraps a macro so it can be used directly as a standard Python decorator. Functionally similar to `m_derive`, but utilizes native Python decorator syntax to pass the decorated function/class as the node.
|
|
26
|
+
- **`m_derive`**: Invokes a macro and automatically passes the *next* AST node (the one immediately following the `m_derive` call) as the primary node, alongside any additional arguments.
|
|
27
|
+
- **`m_call`**: Directly invokes a macro with the provided arguments, where the very first argument explicitly acts as the AST node.
|
|
28
|
+
|
|
29
|
+
### Example: Defining a Custom Macro
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
import ast
|
|
33
|
+
from pydorust.macros import m_define, MacroContext, MacroResult
|
|
34
|
+
|
|
35
|
+
@m_define
|
|
36
|
+
def compile_error(ctx: MacroContext, node: ast.AST, /, *args: ast.AST) -> MacroResult:
|
|
37
|
+
# Extracts the string literal argument passed to the macro
|
|
38
|
+
msg = ctx.except_literal(str, node)
|
|
39
|
+
raise RuntimeError(
|
|
40
|
+
f"Compile Error at {ctx.filename}:{getattr(node, 'lineno', 1)}: {msg}"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# When expanded via CLI, plugins, or m_call, this raises an error based on the AST provided.
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Traits System (`pydorust.trait`)
|
|
49
|
+
|
|
50
|
+
`pydorust` introduces Rust-style **Traits**, allowing you to decouple method definitions from class inheritance. This enables true ad-hoc polymorphism.
|
|
51
|
+
|
|
52
|
+
- **`Trait`**: The base class for defining new traits. Use decorators like `@trait_method` (for requirements) and `@default_method` (for defaults).
|
|
53
|
+
- **`@impl`**: Implements a trait for a specific data type.
|
|
54
|
+
|
|
55
|
+
### Example: Creating and Implementing a Trait
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from pydorust.trait import Trait, impl, trait_method, default_method
|
|
59
|
+
|
|
60
|
+
class Display(Trait):
|
|
61
|
+
@trait_method
|
|
62
|
+
def fmt(self) -> str:
|
|
63
|
+
"""Required method for formatting."""
|
|
64
|
+
|
|
65
|
+
@default_method
|
|
66
|
+
def print(self) -> None:
|
|
67
|
+
"""Default provided method."""
|
|
68
|
+
print(self.fmt())
|
|
69
|
+
|
|
70
|
+
# Implement `Display` for the built-in `int` type!
|
|
71
|
+
@impl(Display, for_type=int)
|
|
72
|
+
class IntDisplay:
|
|
73
|
+
def fmt(self: int) -> str:
|
|
74
|
+
return f"Integer value: {self}"
|
|
75
|
+
|
|
76
|
+
# Call the trait method directly
|
|
77
|
+
Display.print(42) # Outputs: Integer value: 42
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Types (`pydorust.types`)
|
|
83
|
+
|
|
84
|
+
Rust is famous for eliminating `NullReferenceException` using the `Option` enum and handling errors gracefully using `Result`. `pydorust` implements these explicitly.
|
|
85
|
+
|
|
86
|
+
### `Option` (Replacing `None`)
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
from pydorust.types import Option, Some, Null, ret_option
|
|
90
|
+
|
|
91
|
+
# Using a decorator to convert returns of None into `Null`, and values into `Some`
|
|
92
|
+
@ret_option
|
|
93
|
+
def divide(a: float, b: float) -> float | None:
|
|
94
|
+
if b == 0.0:
|
|
95
|
+
return None
|
|
96
|
+
return a / b
|
|
97
|
+
|
|
98
|
+
result: Option[float] = divide(10.0, 2.0)
|
|
99
|
+
print(result.unwrap_or(0.0)) # 5.0
|
|
100
|
+
|
|
101
|
+
bad_result = divide(10.0, 0.0)
|
|
102
|
+
print(bad_result.is_null()) # True
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### `Result` (Replacing Exceptions)
|
|
106
|
+
|
|
107
|
+
The `Result` module provides `Ok(value)` and `Err(error)`, along with combinators like `.map()`, `.and_then()`, and `.unwrap()` to cleanly chain operations that might fail without massive `try/except` blocks.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Thread Synchronization (`pydorust.sync`)
|
|
112
|
+
|
|
113
|
+
Provides semantic synchronization primitives mirroring Rust's standard library. While these tools cannot physically isolate or protect the inner state of Python objects from being mutated externally, they enforce semantic structure. By utilizing Context Managers as guards, `pydorust` helps prevent lock leaks and logically synchronizes your concurrent code.
|
|
114
|
+
|
|
115
|
+
### Example: `Mutex`
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
import threading
|
|
119
|
+
from pydorust.sync import Mutex
|
|
120
|
+
|
|
121
|
+
# Wrap the data inside the Mutex
|
|
122
|
+
data = Mutex([1, 2, 3])
|
|
123
|
+
|
|
124
|
+
def worker():
|
|
125
|
+
# .lock() returns a MutexGuard. The lock is released when the block exits.
|
|
126
|
+
with data.lock() as guard:
|
|
127
|
+
guard.value.append(4)
|
|
128
|
+
|
|
129
|
+
t = threading.Thread(target=worker)
|
|
130
|
+
t.start(); t.join()
|
|
131
|
+
|
|
132
|
+
with data.lock() as guard:
|
|
133
|
+
print(guard.value) # [1, 2, 3, 4]
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
*(Also includes `RwLock` for multi-reader / single-writer scenarios).*
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Algebraic Data Types (`pydorust.adt`)
|
|
141
|
+
|
|
142
|
+
Using the `Variant` and `Enum` classes, you can build tagged unions/variants to guarantee data shapes securely.
|
|
143
|
+
|
|
144
|
+
### Example
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
from pydorust.adt import Variant, Enum
|
|
148
|
+
|
|
149
|
+
class Point2D(Variant):
|
|
150
|
+
x: int
|
|
151
|
+
y: int
|
|
152
|
+
|
|
153
|
+
class Point3D(Variant):
|
|
154
|
+
x: int
|
|
155
|
+
y: int
|
|
156
|
+
z: int
|
|
157
|
+
|
|
158
|
+
class ShapePoint(Enum):
|
|
159
|
+
D2 = Point2D
|
|
160
|
+
D3 = Point3D
|
|
161
|
+
|
|
162
|
+
p = ShapePoint.D2(x=10, y=20)
|
|
163
|
+
print(isinstance(p, ShapePoint)) # True
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Other Features
|
|
169
|
+
|
|
170
|
+
### Unrecoverable Errors (`pydorust.panics`)
|
|
171
|
+
|
|
172
|
+
Provides deterministic app termination for unrecoverable states.
|
|
173
|
+
|
|
174
|
+
- `panic("message")`: Immediately terminates the execution.
|
|
175
|
+
- `unimplemented()`: Drop-in for missing functionality (crashes if reached).
|
|
176
|
+
- `unreachable()`: For code paths that logically shouldn't be executed.
|
|
177
|
+
|
|
178
|
+
### Unsafe Markers (`pydorust.unsafe`)
|
|
179
|
+
|
|
180
|
+
In Rust, dangerous operations that break memory safety guarantees must be wrapped in `unsafe {}`. `pydorust` emulates this semantic contract using decorators.
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
from pydorust.unsafe import unsafe_func, unsafe_call
|
|
184
|
+
|
|
185
|
+
@unsafe_func
|
|
186
|
+
def dangerous_operation():
|
|
187
|
+
print("Doing something unsafe...")
|
|
188
|
+
|
|
189
|
+
# dangerous_operation() # type error
|
|
190
|
+
unsafe_call(dangerous_operation)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Low-Level Memory (`pydorust.mem`)
|
|
194
|
+
|
|
195
|
+
Exposes `unsafe` wrappers around `ctypes.pythonapi` memory management tools, granting you direct control over allocations with `alloc(size)` and `free(ptr)`. Always use carefully via `RawBuffer` or explicit `unsafe_call`.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"nodes": {
|
|
3
|
+
"flake-utils": {
|
|
4
|
+
"inputs": {
|
|
5
|
+
"systems": "systems"
|
|
6
|
+
},
|
|
7
|
+
"locked": {
|
|
8
|
+
"lastModified": 1731533236,
|
|
9
|
+
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
|
10
|
+
"owner": "numtide",
|
|
11
|
+
"repo": "flake-utils",
|
|
12
|
+
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
|
13
|
+
"type": "github"
|
|
14
|
+
},
|
|
15
|
+
"original": {
|
|
16
|
+
"owner": "numtide",
|
|
17
|
+
"repo": "flake-utils",
|
|
18
|
+
"type": "github"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"nixpkgs": {
|
|
22
|
+
"locked": {
|
|
23
|
+
"lastModified": 1760524057,
|
|
24
|
+
"narHash": "sha256-EVAqOteLBFmd7pKkb0+FIUyzTF61VKi7YmvP1tw4nEw=",
|
|
25
|
+
"owner": "NixOS",
|
|
26
|
+
"repo": "nixpkgs",
|
|
27
|
+
"rev": "544961dfcce86422ba200ed9a0b00dd4b1486ec5",
|
|
28
|
+
"type": "github"
|
|
29
|
+
},
|
|
30
|
+
"original": {
|
|
31
|
+
"owner": "NixOS",
|
|
32
|
+
"ref": "nixos-unstable",
|
|
33
|
+
"repo": "nixpkgs",
|
|
34
|
+
"type": "github"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"root": {
|
|
38
|
+
"inputs": {
|
|
39
|
+
"flake-utils": "flake-utils",
|
|
40
|
+
"nixpkgs": "nixpkgs"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"systems": {
|
|
44
|
+
"locked": {
|
|
45
|
+
"lastModified": 1681028828,
|
|
46
|
+
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
|
47
|
+
"owner": "nix-systems",
|
|
48
|
+
"repo": "default",
|
|
49
|
+
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
|
50
|
+
"type": "github"
|
|
51
|
+
},
|
|
52
|
+
"original": {
|
|
53
|
+
"owner": "nix-systems",
|
|
54
|
+
"repo": "default",
|
|
55
|
+
"type": "github"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"root": "root",
|
|
60
|
+
"version": 7
|
|
61
|
+
}
|
pydorust-0.1.0/flake.nix
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
inputs = {
|
|
3
|
+
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
|
4
|
+
flake-utils.url = "github:numtide/flake-utils";
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
outputs = {
|
|
8
|
+
nixpkgs,
|
|
9
|
+
flake-utils,
|
|
10
|
+
...
|
|
11
|
+
}:
|
|
12
|
+
flake-utils.lib.eachDefaultSystem (
|
|
13
|
+
system: let
|
|
14
|
+
pkgs = import nixpkgs {
|
|
15
|
+
inherit system;
|
|
16
|
+
};
|
|
17
|
+
in {
|
|
18
|
+
devShells.default = pkgs.mkShell {
|
|
19
|
+
nativeBuildInputs = with pkgs; [
|
|
20
|
+
python310
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
buildInputs = with pkgs; [
|
|
24
|
+
black
|
|
25
|
+
nixfmt
|
|
26
|
+
uv
|
|
27
|
+
isort
|
|
28
|
+
mypy
|
|
29
|
+
pylint
|
|
30
|
+
ruff
|
|
31
|
+
];
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# ruff: noqa
|
|
2
|
+
# pylint: disable=wrong-import-position
|
|
3
|
+
from .macros import enable_runtime_expand
|
|
4
|
+
|
|
5
|
+
enable_runtime_expand()
|
|
6
|
+
|
|
7
|
+
from . import adt, lock, mem, num, sync, trait, types, utilities
|
|
8
|
+
from .panics import PanicMode, panic, panic_mode, unimplemented, unreachable
|
|
9
|
+
from .unsafe import unsafe_call, unsafe_func
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"adt",
|
|
13
|
+
"panics",
|
|
14
|
+
"unimplemented",
|
|
15
|
+
"unreachable",
|
|
16
|
+
"panic",
|
|
17
|
+
"panic_mode",
|
|
18
|
+
"PanicMode",
|
|
19
|
+
"mem",
|
|
20
|
+
"trait",
|
|
21
|
+
"utilities",
|
|
22
|
+
"lock",
|
|
23
|
+
"sync",
|
|
24
|
+
"types",
|
|
25
|
+
"num",
|
|
26
|
+
"unsafe_func",
|
|
27
|
+
"unsafe_call",
|
|
28
|
+
]
|