rustest 0.1.0__cp311-cp311-macosx_11_0_arm64.whl → 0.5.0__cp311-cp311-macosx_11_0_arm64.whl

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.
@@ -0,0 +1,208 @@
1
+ Metadata-Version: 2.4
2
+ Name: rustest
3
+ Version: 0.5.0
4
+ Classifier: Development Status :: 3 - Alpha
5
+ Classifier: Intended Audience :: Developers
6
+ Classifier: License :: OSI Approved :: MIT License
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.10
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: Programming Language :: Python :: 3.14
13
+ Classifier: Programming Language :: Rust
14
+ Classifier: Topic :: Software Development :: Testing
15
+ Requires-Dist: typing-extensions>=4.15
16
+ Requires-Dist: basedpyright>=1.19 ; extra == 'dev'
17
+ Requires-Dist: maturin>=1.4,<2 ; extra == 'dev'
18
+ Requires-Dist: poethepoet>=0.22 ; extra == 'dev'
19
+ Requires-Dist: pre-commit>=3.5 ; extra == 'dev'
20
+ Requires-Dist: pytest>=7.0 ; extra == 'dev'
21
+ Requires-Dist: ruff>=0.1.9 ; extra == 'dev'
22
+ Requires-Dist: mkdocs>=1.5.0 ; extra == 'docs'
23
+ Requires-Dist: mkdocs-material>=9.5.0 ; extra == 'docs'
24
+ Requires-Dist: mkdocstrings[python]>=0.24.0 ; extra == 'docs'
25
+ Requires-Dist: mkdocs-autorefs>=0.5.0 ; extra == 'docs'
26
+ Provides-Extra: dev
27
+ Provides-Extra: docs
28
+ License-File: LICENSE
29
+ Summary: Rust powered pytest-compatible runner
30
+ Author: rustest contributors
31
+ Requires-Python: >=3.10
32
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
33
+ Project-URL: Homepage, https://github.com/Apex-Engineers-Inc/rustest
34
+ Project-URL: Repository, https://github.com/Apex-Engineers-Inc/rustest
35
+ Project-URL: Documentation, https://apex-engineers-inc.github.io/rustest
36
+
37
+ # rustest
38
+
39
+ Rustest (pronounced like Russ-Test) is a Rust-powered test runner that aims to provide the most common pytest ergonomics with a focus on raw performance. Get **~2x faster** test execution with familiar syntax and minimal setup.
40
+
41
+ 📚 **[Full Documentation](https://apex-engineers-inc.github.io/rustest)** | [Getting Started](https://apex-engineers-inc.github.io/rustest/getting-started/quickstart/) | [User Guide](https://apex-engineers-inc.github.io/rustest/guide/writing-tests/) | [API Reference](https://apex-engineers-inc.github.io/rustest/api/overview/)
42
+
43
+ ## Why rustest?
44
+
45
+ - 🚀 **About 2x faster** than pytest on the rustest integration test suite
46
+ - ✅ Familiar `@fixture`, `@parametrize`, `@skip`, and `@mark` decorators
47
+ - 🔍 Automatic test discovery (`test_*.py` and `*_test.py` files)
48
+ - 📝 **Built-in markdown code block testing** (like pytest-codeblocks, but faster)
49
+ - 🎯 Simple, clean API—if you know pytest, you already know rustest
50
+ - 🧮 Built-in `approx()` helper for tolerant numeric comparisons
51
+ - 🪤 `raises()` context manager for precise exception assertions
52
+ - 📦 Easy installation with pip or uv
53
+ - ⚡ Low-overhead execution keeps small suites feeling instant
54
+
55
+ ## Performance
56
+
57
+ Rustest is designed for speed. Our latest benchmarks on the rustest integration suite (~200 tests) show a consistent **2.1x wall-clock speedup** over pytest:
58
+
59
+ | Test Runner | Wall Clock | Speedup | Command |
60
+ |-------------|------------|---------|---------|
61
+ | pytest | 1.33–1.59s | 1.0x (baseline) | `pytest tests/ examples/tests/ -q` |
62
+ | rustest | 0.69–0.70s | **~2.1x faster** | `python -m rustest tests/ examples/tests/` |
63
+
64
+ ### Large Parametrized Stress Test
65
+
66
+ With **10,000 parametrized invocations**:
67
+
68
+ | Test Runner | Avg. Wall Clock | Speedup | Command |
69
+ |-------------|-----------------|---------|---------|
70
+ | pytest | 9.72s | 1.0x | `pytest benchmarks/test_large_parametrize.py -q` |
71
+ | rustest | 0.41s | **~24x faster** | `python -m rustest benchmarks/test_large_parametrize.py` |
72
+
73
+ **[📊 View Detailed Performance Analysis →](https://apex-engineers-inc.github.io/rustest/advanced/performance/)**
74
+
75
+ ## Installation
76
+
77
+ Rustest supports Python **3.10 through 3.14**.
78
+
79
+ ```bash
80
+ # Using pip
81
+ pip install rustest
82
+
83
+ # Using uv
84
+ uv add rustest
85
+ ```
86
+
87
+ **[📖 Installation Guide →](https://apex-engineers-inc.github.io/rustest/getting-started/installation/)**
88
+
89
+ ## Quick Start
90
+
91
+ ### 1. Write Your Tests
92
+
93
+ Create a file `test_math.py`:
94
+
95
+ ```python
96
+ from rustest import fixture, parametrize, mark, approx, raises
97
+
98
+ @fixture
99
+ def numbers() -> list[int]:
100
+ return [1, 2, 3, 4, 5]
101
+
102
+ def test_sum(numbers: list[int]) -> None:
103
+ assert sum(numbers) == approx(15)
104
+
105
+ @parametrize("value,expected", [(2, 4), (3, 9), (4, 16)])
106
+ def test_square(value: int, expected: int) -> None:
107
+ assert value ** 2 == expected
108
+
109
+ @mark.slow
110
+ def test_expensive_operation() -> None:
111
+ result = sum(range(1000000))
112
+ assert result > 0
113
+
114
+ def test_division_by_zero() -> None:
115
+ with raises(ZeroDivisionError, match="division by zero"):
116
+ 1 / 0
117
+ ```
118
+
119
+ ### 2. Run Your Tests
120
+
121
+ ```bash
122
+ # Run all tests
123
+ rustest
124
+
125
+ # Run specific tests
126
+ rustest tests/
127
+
128
+ # Filter by test name pattern
129
+ rustest -k "test_sum"
130
+
131
+ # Filter by marks
132
+ rustest -m "slow" # Run only slow tests
133
+ rustest -m "not slow" # Skip slow tests
134
+ rustest -m "slow and integration" # Run tests with both marks
135
+
136
+ # Show output during execution
137
+ rustest --no-capture
138
+ ```
139
+
140
+ **[📖 Full Quick Start Guide →](https://apex-engineers-inc.github.io/rustest/getting-started/quickstart/)**
141
+
142
+ ## Documentation
143
+
144
+ **[📚 Full Documentation](https://apex-engineers-inc.github.io/rustest)**
145
+
146
+ ### Getting Started
147
+ - [Installation](https://apex-engineers-inc.github.io/rustest/getting-started/installation/)
148
+ - [Quick Start](https://apex-engineers-inc.github.io/rustest/getting-started/quickstart/)
149
+
150
+ ### User Guide
151
+ - [Writing Tests](https://apex-engineers-inc.github.io/rustest/guide/writing-tests/)
152
+ - [Fixtures](https://apex-engineers-inc.github.io/rustest/guide/fixtures/)
153
+ - [Parametrization](https://apex-engineers-inc.github.io/rustest/guide/parametrization/)
154
+ - [Marks & Skipping](https://apex-engineers-inc.github.io/rustest/guide/marks/)
155
+ - [Test Classes](https://apex-engineers-inc.github.io/rustest/guide/test-classes/)
156
+ - [Assertion Helpers](https://apex-engineers-inc.github.io/rustest/guide/assertions/)
157
+ - [Markdown Testing](https://apex-engineers-inc.github.io/rustest/guide/markdown-testing/)
158
+ - [CLI Usage](https://apex-engineers-inc.github.io/rustest/guide/cli/)
159
+ - [Python API](https://apex-engineers-inc.github.io/rustest/guide/python-api/)
160
+
161
+ ### API Reference
162
+ - [API Overview](https://apex-engineers-inc.github.io/rustest/api/overview/)
163
+ - [Decorators](https://apex-engineers-inc.github.io/rustest/api/decorators/)
164
+ - [Test Execution](https://apex-engineers-inc.github.io/rustest/api/core/)
165
+ - [Reporting](https://apex-engineers-inc.github.io/rustest/api/reporting/)
166
+ - [Assertion Utilities](https://apex-engineers-inc.github.io/rustest/api/approx/)
167
+
168
+ ### Advanced Topics
169
+ - [Performance](https://apex-engineers-inc.github.io/rustest/advanced/performance/)
170
+ - [Comparison with pytest](https://apex-engineers-inc.github.io/rustest/advanced/comparison/)
171
+ - [Development Guide](https://apex-engineers-inc.github.io/rustest/advanced/development/)
172
+
173
+ ## Feature Comparison with pytest
174
+
175
+ Rustest implements the 20% of pytest features that cover 80% of use cases, with a focus on raw speed and simplicity.
176
+
177
+ **[📋 View Full Feature Comparison →](https://apex-engineers-inc.github.io/rustest/advanced/comparison/)**
178
+
179
+ ✅ **Supported:** Fixtures, parametrization, marks, test classes, conftest.py, markdown testing
180
+ 🚧 **Planned:** Parallel execution, mark filtering, JUnit XML output
181
+ ❌ **Not Planned:** Plugins, hooks, custom collectors (keeps rustest simple)
182
+
183
+ ## Contributing
184
+
185
+ We welcome contributions! See the [Development Guide](https://apex-engineers-inc.github.io/rustest/advanced/development/) for setup instructions.
186
+
187
+ Quick reference:
188
+
189
+ ```bash
190
+ # Setup
191
+ git clone https://github.com/Apex-Engineers-Inc/rustest.git
192
+ cd rustest
193
+ uv sync --all-extras
194
+ uv run maturin develop
195
+
196
+ # Run tests
197
+ uv run poe pytests # Python tests
198
+ cargo test # Rust tests
199
+
200
+ # Format and lint
201
+ uv run pre-commit install # One-time setup
202
+ git commit -m "message" # Pre-commit hooks run automatically
203
+ ```
204
+
205
+ ## License
206
+
207
+ rustest is distributed under the terms of the MIT license. See [LICENSE](LICENSE).
208
+
@@ -0,0 +1,16 @@
1
+ rustest-0.5.0.dist-info/METADATA,sha256=t13FMGBrjwTTq6iJ3xqPuJbO7zMriNL3MS87YAopr5Y,8054
2
+ rustest-0.5.0.dist-info/WHEEL,sha256=dewdenAwp3PDth0u4HpQhcjieEs1_hiwRbm3WvCuoaI,104
3
+ rustest-0.5.0.dist-info/entry_points.txt,sha256=7fUa3LO8vudQ4dKG1sTRaDnxcMdBSZsWs9EyuxFQ7Lk,48
4
+ rustest-0.5.0.dist-info/licenses/LICENSE,sha256=s64ibUGtb6jEDBsYuxUFtMr_c4PaqYP-vj3YY6QtTGw,1075
5
+ rustest/__init__.py,sha256=0CkHfrmIjpGw6DMu2VcZLOUpBVtdwsN-aitn-RzOglo,514
6
+ rustest/__main__.py,sha256=bBvo5gsSluUzlDTDvn5bP_gZZEXMwJQZMqVA5W1M1v8,178
7
+ rustest/approx.py,sha256=MKmuorBBHqpH0h0QaIMVjbm3-mXJ0E90limEgSHHVfw,5744
8
+ rustest/cli.py,sha256=U7mlUghUnkrRcmjUD_R0leNgdC43fzUrki6XzWmUpRE,9201
9
+ rustest/core.py,sha256=KoifFNU3tvOZgLU_zLvpcyEciOE4rAkQfpXWO2_EQmo,1189
10
+ rustest/decorators.py,sha256=yQD6EV3mM2cAx2yns1YM22LfRw5m1k62yZXUZwm3Vz4,13715
11
+ rustest/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ rustest/reporting.py,sha256=3-R8aljv2ZbmjOa9Q9KZeCPsMaitm8nZ96LoJS_NnUQ,1623
13
+ rustest/rust.cpython-311-darwin.so,sha256=TX-2ZgwYJA1alIrRHvI2IR6kiTROIupeknFDg9oe-B8,1502880
14
+ rustest/rust.py,sha256=tCIvjYd06VxoT_rKvv2o8CpXW_pFNua5VgcRDjLgU78,659
15
+ rustest/rust.pyi,sha256=-eAPqXfM9KwXHX_-oEuPwIU1x1mDEw_qkbur3mRouT0,762
16
+ rustest-0.5.0.dist-info/RECORD,,
rustest/_decorators.py DELETED
@@ -1,194 +0,0 @@
1
- """User facing decorators mirroring the most common pytest helpers."""
2
-
3
- from __future__ import annotations
4
-
5
- from collections.abc import Callable, Mapping, Sequence
6
- from typing import Any, TypeVar
7
-
8
- F = TypeVar("F", bound=Callable[..., object])
9
-
10
- # Valid fixture scopes
11
- VALID_SCOPES = frozenset(["function", "class", "module", "session"])
12
-
13
-
14
- def fixture(
15
- func: F | None = None,
16
- *,
17
- scope: str = "function",
18
- ) -> F | Callable[[F], F]:
19
- """Mark a function as a fixture with a specific scope.
20
-
21
- Args:
22
- func: The function to decorate (when used without parentheses)
23
- scope: The scope of the fixture. One of:
24
- - "function": New instance for each test function (default)
25
- - "class": Shared across all test methods in a class
26
- - "module": Shared across all tests in a module
27
- - "session": Shared across all tests in the session
28
-
29
- Usage:
30
- @fixture
31
- def my_fixture():
32
- return 42
33
-
34
- @fixture(scope="module")
35
- def shared_fixture():
36
- return expensive_setup()
37
- """
38
- if scope not in VALID_SCOPES:
39
- valid = ", ".join(sorted(VALID_SCOPES))
40
- msg = f"Invalid fixture scope '{scope}'. Must be one of: {valid}"
41
- raise ValueError(msg)
42
-
43
- def decorator(f: F) -> F:
44
- setattr(f, "__rustest_fixture__", True)
45
- setattr(f, "__rustest_fixture_scope__", scope)
46
- return f
47
-
48
- # Support both @fixture and @fixture(scope="...")
49
- if func is not None:
50
- return decorator(func)
51
- return decorator
52
-
53
-
54
- def skip(reason: str | None = None) -> Callable[[F], F]:
55
- """Skip a test or fixture."""
56
-
57
- def decorator(func: F) -> F:
58
- setattr(func, "__rustest_skip__", reason or "skipped via rustest.skip")
59
- return func
60
-
61
- return decorator
62
-
63
-
64
- def parametrize(
65
- arg_names: str | Sequence[str],
66
- values: Sequence[Sequence[object] | Mapping[str, object]],
67
- *,
68
- ids: Sequence[str] | None = None,
69
- ) -> Callable[[F], F]:
70
- """Parametrise a test function."""
71
-
72
- normalized_names = _normalize_arg_names(arg_names)
73
-
74
- def decorator(func: F) -> F:
75
- cases = _build_cases(normalized_names, values, ids)
76
- setattr(func, "__rustest_parametrization__", cases)
77
- return func
78
-
79
- return decorator
80
-
81
-
82
- def _normalize_arg_names(arg_names: str | Sequence[str]) -> tuple[str, ...]:
83
- if isinstance(arg_names, str):
84
- parts = [part.strip() for part in arg_names.split(",") if part.strip()]
85
- if not parts:
86
- msg = "parametrize() expected at least one argument name"
87
- raise ValueError(msg)
88
- return tuple(parts)
89
- return tuple(arg_names)
90
-
91
-
92
- def _build_cases(
93
- names: tuple[str, ...],
94
- values: Sequence[Sequence[object] | Mapping[str, object]],
95
- ids: Sequence[str] | None,
96
- ) -> tuple[dict[str, object], ...]:
97
- case_payloads: list[dict[str, object]] = []
98
- if ids is not None and len(ids) != len(values):
99
- msg = "ids must match the number of value sets"
100
- raise ValueError(msg)
101
-
102
- for index, case in enumerate(values):
103
- # Mappings are only treated as parameter mappings when there are multiple parameters
104
- # For single parameters, dicts/mappings are treated as values
105
- if isinstance(case, Mapping) and len(names) > 1:
106
- data = {name: case[name] for name in names}
107
- elif isinstance(case, tuple) and len(case) == len(names):
108
- # Tuples are unpacked to match parameter names (pytest convention)
109
- # This handles both single and multiple parameters
110
- data = {name: case[pos] for pos, name in enumerate(names)}
111
- else:
112
- # Everything else is treated as a single value
113
- # This includes: primitives, lists (even if len==names), dicts (single param), objects
114
- if len(names) == 1:
115
- data = {names[0]: case}
116
- else:
117
- raise ValueError("Parametrized value does not match argument names")
118
- case_id = ids[index] if ids is not None else f"case_{index}"
119
- case_payloads.append({"id": case_id, "values": data})
120
- return tuple(case_payloads)
121
-
122
-
123
- class MarkDecorator:
124
- """A decorator for applying a mark to a test function."""
125
-
126
- def __init__(self, name: str, args: tuple[Any, ...], kwargs: dict[str, Any]) -> None:
127
- super().__init__()
128
- self.name = name
129
- self.args = args
130
- self.kwargs = kwargs
131
-
132
- def __call__(self, func: F) -> F:
133
- """Apply this mark to the given function."""
134
- # Get existing marks or create a new list
135
- existing_marks: list[dict[str, Any]] = getattr(func, "__rustest_marks__", [])
136
-
137
- # Add this mark to the list
138
- mark_data = {
139
- "name": self.name,
140
- "args": self.args,
141
- "kwargs": self.kwargs,
142
- }
143
- existing_marks.append(mark_data)
144
-
145
- # Store the marks list on the function
146
- setattr(func, "__rustest_marks__", existing_marks)
147
- return func
148
-
149
- def __repr__(self) -> str:
150
- return f"Mark({self.name!r}, {self.args!r}, {self.kwargs!r})"
151
-
152
-
153
- class MarkGenerator:
154
- """Namespace for dynamically creating marks like pytest.mark.
155
-
156
- Usage:
157
- @mark.slow
158
- @mark.integration
159
- @mark.timeout(seconds=30)
160
- """
161
-
162
- def __getattr__(self, name: str) -> Any:
163
- """Create a mark decorator for the given name."""
164
- # Return a callable that can be used as @mark.name or @mark.name(args)
165
- return self._create_mark(name)
166
-
167
- def _create_mark(self, name: str) -> Any:
168
- """Create a MarkDecorator that can be called with or without arguments."""
169
-
170
- class _MarkDecoratorFactory:
171
- """Factory that allows @mark.name or @mark.name(args)."""
172
-
173
- def __init__(self, mark_name: str) -> None:
174
- super().__init__()
175
- self.mark_name = mark_name
176
-
177
- def __call__(self, *args: Any, **kwargs: Any) -> Any:
178
- # If called with a single argument that's a function, it's @mark.name
179
- if (
180
- len(args) == 1
181
- and not kwargs
182
- and callable(args[0])
183
- and hasattr(args[0], "__name__")
184
- ):
185
- decorator = MarkDecorator(self.mark_name, (), {})
186
- return decorator(args[0])
187
- # Otherwise it's @mark.name(args) - return a decorator
188
- return MarkDecorator(self.mark_name, args, kwargs)
189
-
190
- return _MarkDecoratorFactory(name)
191
-
192
-
193
- # Create a singleton instance
194
- mark = MarkGenerator()
Binary file