capper 0.1.0__tar.gz → 0.2.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.
- {capper-0.1.0/capper.egg-info → capper-0.2.0}/PKG-INFO +40 -14
- capper-0.1.0/PKG-INFO → capper-0.2.0/README.md +33 -31
- {capper-0.1.0 → capper-0.2.0}/capper/__init__.py +3 -1
- {capper-0.1.0 → capper-0.2.0}/capper/base.py +5 -5
- capper-0.2.0/capper/cli.py +79 -0
- capper-0.2.0/capper/strategies.py +80 -0
- capper-0.2.0/capper/tests/test_docs_examples.py +34 -0
- capper-0.2.0/capper/tests/test_hypothesis_strategies.py +34 -0
- {capper-0.1.0 → capper-0.2.0}/capper/tests/test_polyfactory_integration.py +8 -6
- {capper-0.1.0 → capper-0.2.0}/capper/tests/test_types.py +9 -8
- capper-0.1.0/README.md → capper-0.2.0/capper.egg-info/PKG-INFO +57 -13
- {capper-0.1.0 → capper-0.2.0}/capper.egg-info/SOURCES.txt +5 -0
- capper-0.2.0/capper.egg-info/entry_points.txt +2 -0
- {capper-0.1.0 → capper-0.2.0}/capper.egg-info/requires.txt +7 -0
- {capper-0.1.0 → capper-0.2.0}/pyproject.toml +25 -1
- {capper-0.1.0 → capper-0.2.0}/capper/commerce.py +0 -0
- {capper-0.1.0 → capper-0.2.0}/capper/date_time.py +0 -0
- {capper-0.1.0 → capper-0.2.0}/capper/examples/user_factory.py +1 -1
- {capper-0.1.0 → capper-0.2.0}/capper/finance.py +0 -0
- {capper-0.1.0 → capper-0.2.0}/capper/geo.py +0 -0
- {capper-0.1.0 → capper-0.2.0}/capper/internet.py +0 -0
- {capper-0.1.0 → capper-0.2.0}/capper/person.py +0 -0
- {capper-0.1.0 → capper-0.2.0}/capper/phone.py +0 -0
- {capper-0.1.0 → capper-0.2.0}/capper/tests/__init__.py +0 -0
- {capper-0.1.0 → capper-0.2.0}/capper/tests/conftest.py +0 -0
- {capper-0.1.0 → capper-0.2.0}/capper/text.py +0 -0
- {capper-0.1.0 → capper-0.2.0}/capper.egg-info/dependency_links.txt +0 -0
- {capper-0.1.0 → capper-0.2.0}/capper.egg-info/top_level.txt +0 -0
- {capper-0.1.0 → capper-0.2.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: capper
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Semantic, typed wrappers for Faker with automatic Polyfactory integration
|
|
5
5
|
Author-email: Odos Matthews <odosmatthews@gmail.com>
|
|
6
6
|
Project-URL: Documentation, https://github.com/eddiethedean/capper#readme
|
|
@@ -11,16 +11,22 @@ Requires-Dist: Faker>=20.0
|
|
|
11
11
|
Requires-Dist: Polyfactory>=2.0
|
|
12
12
|
Provides-Extra: pydantic
|
|
13
13
|
Requires-Dist: pydantic>=2.0; extra == "pydantic"
|
|
14
|
+
Provides-Extra: hypothesis
|
|
15
|
+
Requires-Dist: hypothesis>=6.0; extra == "hypothesis"
|
|
14
16
|
Provides-Extra: dev
|
|
15
17
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
16
18
|
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
19
|
+
Requires-Dist: pytest-xdist>=3.0; extra == "dev"
|
|
17
20
|
Requires-Dist: pydantic>=2.0; extra == "dev"
|
|
21
|
+
Requires-Dist: ruff>=0.4; extra == "dev"
|
|
22
|
+
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
23
|
+
Requires-Dist: hypothesis>=6.0; extra == "dev"
|
|
18
24
|
|
|
19
25
|
# Capper
|
|
20
26
|
|
|
21
27
|
[](https://pypi.org/project/capper/)
|
|
22
|
-
[](https://pypi.org/project/capper/)
|
|
29
|
+
[](https://github.com/eddiethedean/capper/actions/workflows/ci.yml)
|
|
24
30
|
[](https://docs.astral.sh/ruff/)
|
|
25
31
|
[](https://mypy-lang.org/)
|
|
26
32
|
|
|
@@ -41,11 +47,10 @@ Semantic, typed wrappers for [Faker](https://faker.readthedocs.io/) with automat
|
|
|
41
47
|
pip install capper
|
|
42
48
|
```
|
|
43
49
|
|
|
44
|
-
Requires **Python 3.9+**, **Faker >= 20.0**, and **Polyfactory >= 2.0**.
|
|
50
|
+
Requires **Python 3.9+**, **Faker >= 20.0**, and **Polyfactory >= 2.0**. Optional extras:
|
|
45
51
|
|
|
46
|
-
|
|
47
|
-
pip install capper[
|
|
48
|
-
```
|
|
52
|
+
- **Pydantic** (for Pydantic models): `pip install capper[pydantic]`
|
|
53
|
+
- **Hypothesis** (for property-based tests with `st.from_type(...)`): `pip install capper[hypothesis]`
|
|
49
54
|
|
|
50
55
|
## Usage
|
|
51
56
|
|
|
@@ -104,6 +109,8 @@ oevans@example.com
|
|
|
104
109
|
|
|
105
110
|
Works automatically. No extra steps. IDE autocompletion.
|
|
106
111
|
|
|
112
|
+
**New to Capper?** See the [Getting started](docs/user_guides/getting_started.md) guide and run the examples in `docs/examples/`.
|
|
113
|
+
|
|
107
114
|
## Available types
|
|
108
115
|
|
|
109
116
|
- **Person**: `Name`, `FirstName`, `LastName`, `Job`
|
|
@@ -122,6 +129,17 @@ See [docs/FAKER_PROVIDERS.md](https://github.com/eddiethedean/capper/blob/main/d
|
|
|
122
129
|
|
|
123
130
|
**Custom types:** Subclass `FakerType`, set `faker_provider` to the Faker method name (e.g. `"company"`), and optionally `faker_kwargs`. The type auto-registers with Polyfactory when the class is defined.
|
|
124
131
|
|
|
132
|
+
## CLI
|
|
133
|
+
|
|
134
|
+
Generate fake values from the command line:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
capper generate Name Email --count 5
|
|
138
|
+
capper generate Name Email --count 3 --seed 42
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Use `-n`/`--count` for the number of rows and `-s`/`--seed` for reproducible output. Type names are the same as the Python types (e.g. `Name`, `Email`, `Address`).
|
|
142
|
+
|
|
125
143
|
## Compatibility
|
|
126
144
|
|
|
127
145
|
Capper targets **Faker >= 20.0** and **Polyfactory >= 2.0**. Major Faker upgrades may change or rename provider methods; if a type fails, check [Faker's changelog](https://faker.readthedocs.io/en/stable/changelog.html) and [docs/FAKER_PROVIDERS.md](https://github.com/eddiethedean/capper/blob/main/docs/FAKER_PROVIDERS.md) and update the provider name if needed.
|
|
@@ -133,6 +151,8 @@ pip install -e ".[dev]"
|
|
|
133
151
|
pytest capper/tests
|
|
134
152
|
```
|
|
135
153
|
|
|
154
|
+
Lint and type-check: `ruff check .`, `ruff format .`, `mypy capper`.
|
|
155
|
+
|
|
136
156
|
Run tests with coverage: `pytest capper/tests --cov=capper --cov-report=term-missing`.
|
|
137
157
|
|
|
138
158
|
**Reproducibility:** Capper and Polyfactory share the same Faker instance, so one seed controls both capper types and built-in types (`str`, `int`, etc.):
|
|
@@ -163,13 +183,19 @@ Use `UserFactory.__random_seed__ = 42` to seed once when the factory class is cr
|
|
|
163
183
|
Releases are built and published to PyPI via [GitHub Actions](https://github.com/eddiethedean/capper/blob/main/.github/workflows/publish.yml). To publish:
|
|
164
184
|
|
|
165
185
|
1. Add a `PYPI_API_TOKEN` secret (PyPI API token) to the repo.
|
|
166
|
-
2. Create a GitHub release (tag e.g. `v0.
|
|
186
|
+
2. Create a GitHub release (tag e.g. `v0.2.0`). The workflow runs tests, builds the package, and uploads to PyPI.
|
|
167
187
|
|
|
168
188
|
To build and upload manually: `pip install build twine`, `python -m build`, `twine upload dist/*`.
|
|
169
189
|
|
|
170
|
-
##
|
|
171
|
-
|
|
172
|
-
- [
|
|
173
|
-
-
|
|
174
|
-
- [
|
|
175
|
-
- [
|
|
190
|
+
## Documentation
|
|
191
|
+
|
|
192
|
+
- **[Docs index](docs/README.md)** — overview and links to all documentation
|
|
193
|
+
- **User guides** (step-by-step, with runnable examples):
|
|
194
|
+
- [Getting started](docs/user_guides/getting_started.md) — install, first model, first factory
|
|
195
|
+
- [Models and factories](docs/user_guides/models_and_factories.md) — Pydantic, dataclasses, batches
|
|
196
|
+
- [Reproducible data](docs/user_guides/reproducible_data.md) — seeding for tests and demos
|
|
197
|
+
- [Custom types](docs/user_guides/custom_types.md) — `FakerType` subclasses and `faker_kwargs`
|
|
198
|
+
- [Package plan](docs/capper_package_plan.md) — design and rationale
|
|
199
|
+
- [Roadmap](docs/ROADMAP.md) — development phases and status
|
|
200
|
+
- [Faker provider mapping](docs/FAKER_PROVIDERS.md) — which Faker method each type uses
|
|
201
|
+
- [Example notebooks](docs/notebooks/README.md) — Jupyter notebooks in `docs/notebooks/`
|
|
@@ -1,26 +1,8 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: capper
|
|
3
|
-
Version: 0.1.0
|
|
4
|
-
Summary: Semantic, typed wrappers for Faker with automatic Polyfactory integration
|
|
5
|
-
Author-email: Odos Matthews <odosmatthews@gmail.com>
|
|
6
|
-
Project-URL: Documentation, https://github.com/eddiethedean/capper#readme
|
|
7
|
-
Project-URL: Repository, https://github.com/eddiethedean/capper
|
|
8
|
-
Requires-Python: >=3.9
|
|
9
|
-
Description-Content-Type: text/markdown
|
|
10
|
-
Requires-Dist: Faker>=20.0
|
|
11
|
-
Requires-Dist: Polyfactory>=2.0
|
|
12
|
-
Provides-Extra: pydantic
|
|
13
|
-
Requires-Dist: pydantic>=2.0; extra == "pydantic"
|
|
14
|
-
Provides-Extra: dev
|
|
15
|
-
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
16
|
-
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
17
|
-
Requires-Dist: pydantic>=2.0; extra == "dev"
|
|
18
|
-
|
|
19
1
|
# Capper
|
|
20
2
|
|
|
21
3
|
[](https://pypi.org/project/capper/)
|
|
22
|
-
[](https://pypi.org/project/capper/)
|
|
5
|
+
[](https://github.com/eddiethedean/capper/actions/workflows/ci.yml)
|
|
24
6
|
[](https://docs.astral.sh/ruff/)
|
|
25
7
|
[](https://mypy-lang.org/)
|
|
26
8
|
|
|
@@ -41,11 +23,10 @@ Semantic, typed wrappers for [Faker](https://faker.readthedocs.io/) with automat
|
|
|
41
23
|
pip install capper
|
|
42
24
|
```
|
|
43
25
|
|
|
44
|
-
Requires **Python 3.9+**, **Faker >= 20.0**, and **Polyfactory >= 2.0**.
|
|
26
|
+
Requires **Python 3.9+**, **Faker >= 20.0**, and **Polyfactory >= 2.0**. Optional extras:
|
|
45
27
|
|
|
46
|
-
|
|
47
|
-
pip install capper[
|
|
48
|
-
```
|
|
28
|
+
- **Pydantic** (for Pydantic models): `pip install capper[pydantic]`
|
|
29
|
+
- **Hypothesis** (for property-based tests with `st.from_type(...)`): `pip install capper[hypothesis]`
|
|
49
30
|
|
|
50
31
|
## Usage
|
|
51
32
|
|
|
@@ -104,6 +85,8 @@ oevans@example.com
|
|
|
104
85
|
|
|
105
86
|
Works automatically. No extra steps. IDE autocompletion.
|
|
106
87
|
|
|
88
|
+
**New to Capper?** See the [Getting started](docs/user_guides/getting_started.md) guide and run the examples in `docs/examples/`.
|
|
89
|
+
|
|
107
90
|
## Available types
|
|
108
91
|
|
|
109
92
|
- **Person**: `Name`, `FirstName`, `LastName`, `Job`
|
|
@@ -122,6 +105,17 @@ See [docs/FAKER_PROVIDERS.md](https://github.com/eddiethedean/capper/blob/main/d
|
|
|
122
105
|
|
|
123
106
|
**Custom types:** Subclass `FakerType`, set `faker_provider` to the Faker method name (e.g. `"company"`), and optionally `faker_kwargs`. The type auto-registers with Polyfactory when the class is defined.
|
|
124
107
|
|
|
108
|
+
## CLI
|
|
109
|
+
|
|
110
|
+
Generate fake values from the command line:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
capper generate Name Email --count 5
|
|
114
|
+
capper generate Name Email --count 3 --seed 42
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Use `-n`/`--count` for the number of rows and `-s`/`--seed` for reproducible output. Type names are the same as the Python types (e.g. `Name`, `Email`, `Address`).
|
|
118
|
+
|
|
125
119
|
## Compatibility
|
|
126
120
|
|
|
127
121
|
Capper targets **Faker >= 20.0** and **Polyfactory >= 2.0**. Major Faker upgrades may change or rename provider methods; if a type fails, check [Faker's changelog](https://faker.readthedocs.io/en/stable/changelog.html) and [docs/FAKER_PROVIDERS.md](https://github.com/eddiethedean/capper/blob/main/docs/FAKER_PROVIDERS.md) and update the provider name if needed.
|
|
@@ -133,6 +127,8 @@ pip install -e ".[dev]"
|
|
|
133
127
|
pytest capper/tests
|
|
134
128
|
```
|
|
135
129
|
|
|
130
|
+
Lint and type-check: `ruff check .`, `ruff format .`, `mypy capper`.
|
|
131
|
+
|
|
136
132
|
Run tests with coverage: `pytest capper/tests --cov=capper --cov-report=term-missing`.
|
|
137
133
|
|
|
138
134
|
**Reproducibility:** Capper and Polyfactory share the same Faker instance, so one seed controls both capper types and built-in types (`str`, `int`, etc.):
|
|
@@ -163,13 +159,19 @@ Use `UserFactory.__random_seed__ = 42` to seed once when the factory class is cr
|
|
|
163
159
|
Releases are built and published to PyPI via [GitHub Actions](https://github.com/eddiethedean/capper/blob/main/.github/workflows/publish.yml). To publish:
|
|
164
160
|
|
|
165
161
|
1. Add a `PYPI_API_TOKEN` secret (PyPI API token) to the repo.
|
|
166
|
-
2. Create a GitHub release (tag e.g. `v0.
|
|
162
|
+
2. Create a GitHub release (tag e.g. `v0.2.0`). The workflow runs tests, builds the package, and uploads to PyPI.
|
|
167
163
|
|
|
168
164
|
To build and upload manually: `pip install build twine`, `python -m build`, `twine upload dist/*`.
|
|
169
165
|
|
|
170
|
-
##
|
|
171
|
-
|
|
172
|
-
- [
|
|
173
|
-
-
|
|
174
|
-
- [
|
|
175
|
-
- [
|
|
166
|
+
## Documentation
|
|
167
|
+
|
|
168
|
+
- **[Docs index](docs/README.md)** — overview and links to all documentation
|
|
169
|
+
- **User guides** (step-by-step, with runnable examples):
|
|
170
|
+
- [Getting started](docs/user_guides/getting_started.md) — install, first model, first factory
|
|
171
|
+
- [Models and factories](docs/user_guides/models_and_factories.md) — Pydantic, dataclasses, batches
|
|
172
|
+
- [Reproducible data](docs/user_guides/reproducible_data.md) — seeding for tests and demos
|
|
173
|
+
- [Custom types](docs/user_guides/custom_types.md) — `FakerType` subclasses and `faker_kwargs`
|
|
174
|
+
- [Package plan](docs/capper_package_plan.md) — design and rationale
|
|
175
|
+
- [Roadmap](docs/ROADMAP.md) — development phases and status
|
|
176
|
+
- [Faker provider mapping](docs/FAKER_PROVIDERS.md) — which Faker method each type uses
|
|
177
|
+
- [Example notebooks](docs/notebooks/README.md) — Jupyter notebooks in `docs/notebooks/`
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Import types (e.g. ``Name``, ``Email``) and use them in Pydantic models, dataclasses,
|
|
4
4
|
or attrs; Polyfactory will generate values via Faker. Use ``seed(n)`` for reproducibility.
|
|
5
|
+
With Hypothesis installed (pip install capper[hypothesis]), import capper.strategies
|
|
6
|
+
and use st.from_type(Name) for property-based tests.
|
|
5
7
|
"""
|
|
6
8
|
|
|
7
9
|
from .base import FakerType, faker, seed
|
|
@@ -9,7 +11,7 @@ from .commerce import Company, Currency, Price, Product
|
|
|
9
11
|
from .date_time import Date, DateTime, Time
|
|
10
12
|
from .finance import CreditCardExpiry, CreditCardNumber, CreditCardProvider
|
|
11
13
|
from .geo import Address, City, Country
|
|
12
|
-
from .internet import
|
|
14
|
+
from .internet import IP, URL, Email, UserName
|
|
13
15
|
from .person import FirstName, Job, LastName, Name
|
|
14
16
|
from .phone import CountryCallingCode, PhoneNumber
|
|
15
17
|
from .text import Paragraph, Sentence
|
|
@@ -32,9 +32,7 @@ def _install_pydantic_schema() -> None:
|
|
|
32
32
|
except ImportError:
|
|
33
33
|
return
|
|
34
34
|
|
|
35
|
-
def __get_pydantic_core_schema__(
|
|
36
|
-
source_type: Any, handler: GetCoreSchemaHandler
|
|
37
|
-
) -> CoreSchema:
|
|
35
|
+
def __get_pydantic_core_schema__(source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema:
|
|
38
36
|
"""Validate as str then coerce to the FakerType subclass."""
|
|
39
37
|
return core_schema.no_info_after_validator_function(source_type, handler(str))
|
|
40
38
|
|
|
@@ -42,11 +40,12 @@ def _install_pydantic_schema() -> None:
|
|
|
42
40
|
|
|
43
41
|
|
|
44
42
|
class FakerType(str):
|
|
45
|
-
"""Base class for semantic Faker types; subclasses
|
|
43
|
+
"""Base class for semantic Faker types; subclasses auto-register with Polyfactory.
|
|
46
44
|
|
|
47
45
|
Subclasses must set a non-empty ``faker_provider`` (the Faker method name).
|
|
48
46
|
Optional ``faker_kwargs`` is a dict of keyword arguments passed to that provider
|
|
49
47
|
(e.g. ``faker_kwargs = {"nb_words": 10}`` for ``sentence``).
|
|
48
|
+
When Hypothesis is installed, use ``st.from_type(YourFakerType)`` for property-based tests.
|
|
50
49
|
"""
|
|
51
50
|
|
|
52
51
|
faker_provider: str = ""
|
|
@@ -66,6 +65,7 @@ def _register(cls: type, provider_name: str) -> None:
|
|
|
66
65
|
provider_kwargs = getattr(cls, "faker_kwargs", None) or {}
|
|
67
66
|
|
|
68
67
|
def _provide() -> str:
|
|
69
|
-
|
|
68
|
+
value = getattr(faker, provider_name)(**provider_kwargs)
|
|
69
|
+
return str(value)
|
|
70
70
|
|
|
71
71
|
BaseFactory.add_provider(cls, _provide)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""CLI for ad-hoc fake data generation."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import sys
|
|
5
|
+
from typing import Type
|
|
6
|
+
|
|
7
|
+
import capper
|
|
8
|
+
|
|
9
|
+
from .base import FakerType, faker, seed
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _type_registry() -> dict[str, Type[FakerType]]:
|
|
13
|
+
"""Map type name -> FakerType subclass (only types with faker_provider)."""
|
|
14
|
+
registry: dict[str, Type[FakerType]] = {}
|
|
15
|
+
for name in getattr(capper, "__all__", []):
|
|
16
|
+
if name in ("FakerType", "faker", "seed"):
|
|
17
|
+
continue
|
|
18
|
+
obj = getattr(capper, name, None)
|
|
19
|
+
provider = getattr(obj, "faker_provider", None)
|
|
20
|
+
if (
|
|
21
|
+
isinstance(obj, type)
|
|
22
|
+
and issubclass(obj, FakerType)
|
|
23
|
+
and isinstance(provider, str)
|
|
24
|
+
and len(provider) > 0
|
|
25
|
+
):
|
|
26
|
+
registry[name] = obj
|
|
27
|
+
return registry
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _generate_one(typ: Type[FakerType]) -> str:
|
|
31
|
+
"""Generate a single value for the given FakerType."""
|
|
32
|
+
provider = getattr(typ, "faker_provider", "")
|
|
33
|
+
kwargs = getattr(typ, "faker_kwargs", None) or {}
|
|
34
|
+
value = getattr(faker, provider)(**kwargs)
|
|
35
|
+
return str(value)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def cmd_generate(args: argparse.Namespace) -> int:
|
|
39
|
+
"""Run generate subcommand."""
|
|
40
|
+
registry = _type_registry()
|
|
41
|
+
types: list[Type[FakerType]] = []
|
|
42
|
+
for name in args.types:
|
|
43
|
+
if name not in registry:
|
|
44
|
+
known = ", ".join(sorted(registry))
|
|
45
|
+
print(f"Unknown type: {name}. Known: {known}", file=sys.stderr)
|
|
46
|
+
return 1
|
|
47
|
+
types.append(registry[name])
|
|
48
|
+
|
|
49
|
+
if args.seed is not None:
|
|
50
|
+
seed(args.seed)
|
|
51
|
+
|
|
52
|
+
count = max(0, args.count)
|
|
53
|
+
for _ in range(count):
|
|
54
|
+
row = [_generate_one(t) for t in types]
|
|
55
|
+
print("\t".join(row))
|
|
56
|
+
return 0
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def main() -> int:
|
|
60
|
+
"""Entry point for the capper CLI."""
|
|
61
|
+
parser = argparse.ArgumentParser(
|
|
62
|
+
prog="capper", description="Capper: fake data via Faker types."
|
|
63
|
+
)
|
|
64
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
65
|
+
|
|
66
|
+
gen = subparsers.add_parser("generate", help="Generate fake values for one or more types.")
|
|
67
|
+
gen.add_argument(
|
|
68
|
+
"types",
|
|
69
|
+
nargs="+",
|
|
70
|
+
metavar="TYPE",
|
|
71
|
+
help="Type names (e.g. Name, Email).",
|
|
72
|
+
)
|
|
73
|
+
gen.add_argument("-n", "--count", type=int, default=1, help="Number of rows (default: 1).")
|
|
74
|
+
gen.add_argument("-s", "--seed", type=int, default=None, help="Seed for reproducible output.")
|
|
75
|
+
gen.set_defaults(func=cmd_generate)
|
|
76
|
+
|
|
77
|
+
args = parser.parse_args()
|
|
78
|
+
result: int = args.func(args)
|
|
79
|
+
return result
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Hypothesis strategies for Capper types (optional; requires hypothesis>=6.0).
|
|
2
|
+
|
|
3
|
+
Use ``st.from_type(Name)`` after importing capper and capper.strategies, or call
|
|
4
|
+
``strategies.for_type(Name)`` to get a strategy that generates instances of that type.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Type, TypeVar
|
|
8
|
+
|
|
9
|
+
from hypothesis import strategies as st
|
|
10
|
+
|
|
11
|
+
from .base import FakerType, faker
|
|
12
|
+
from .commerce import Company, Currency, Price, Product
|
|
13
|
+
from .date_time import Date, DateTime, Time
|
|
14
|
+
from .finance import CreditCardExpiry, CreditCardNumber, CreditCardProvider
|
|
15
|
+
from .geo import Address, City, Country
|
|
16
|
+
from .internet import IP, URL, Email, UserName
|
|
17
|
+
from .person import FirstName, Job, LastName, Name
|
|
18
|
+
from .phone import CountryCallingCode, PhoneNumber
|
|
19
|
+
from .text import Paragraph, Sentence
|
|
20
|
+
|
|
21
|
+
T = TypeVar("T", bound=FakerType)
|
|
22
|
+
|
|
23
|
+
# All built-in FakerType subclasses for registration
|
|
24
|
+
_BUILTIN_TYPES: tuple[type[FakerType], ...] = (
|
|
25
|
+
Address,
|
|
26
|
+
City,
|
|
27
|
+
Company,
|
|
28
|
+
Country,
|
|
29
|
+
CountryCallingCode,
|
|
30
|
+
CreditCardExpiry,
|
|
31
|
+
CreditCardNumber,
|
|
32
|
+
CreditCardProvider,
|
|
33
|
+
Currency,
|
|
34
|
+
Date,
|
|
35
|
+
DateTime,
|
|
36
|
+
Email,
|
|
37
|
+
FirstName,
|
|
38
|
+
IP,
|
|
39
|
+
Job,
|
|
40
|
+
LastName,
|
|
41
|
+
Name,
|
|
42
|
+
Paragraph,
|
|
43
|
+
PhoneNumber,
|
|
44
|
+
Price,
|
|
45
|
+
Product,
|
|
46
|
+
Sentence,
|
|
47
|
+
Time,
|
|
48
|
+
URL,
|
|
49
|
+
UserName,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def for_type(cls: Type[T]) -> st.SearchStrategy[T]:
|
|
54
|
+
"""Return a Hypothesis strategy that generates instances of the given FakerType subclass."""
|
|
55
|
+
provider = getattr(cls, "faker_provider", None)
|
|
56
|
+
if not provider:
|
|
57
|
+
raise ValueError(f"{cls.__name__} has no faker_provider")
|
|
58
|
+
kwargs = getattr(cls, "faker_kwargs", None) or {}
|
|
59
|
+
|
|
60
|
+
@st.composite
|
|
61
|
+
def _draw(draw: st.DrawFn) -> T:
|
|
62
|
+
seed_val = draw(st.integers(min_value=0, max_value=2**32 - 1))
|
|
63
|
+
faker.seed_instance(seed_val)
|
|
64
|
+
value = getattr(faker, provider)(**kwargs)
|
|
65
|
+
return cls(str(value))
|
|
66
|
+
|
|
67
|
+
return _draw()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _register_strategies() -> None:
|
|
71
|
+
"""Register all built-in Capper types with Hypothesis so st.from_type() works."""
|
|
72
|
+
from hypothesis.strategies import register_type_strategy
|
|
73
|
+
|
|
74
|
+
for typ in _BUILTIN_TYPES:
|
|
75
|
+
register_type_strategy(typ, for_type(typ))
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
_register_strategies()
|
|
79
|
+
|
|
80
|
+
__all__ = ["for_type"]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Ensure docs/examples/*.py run without error (user guide code)."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
|
|
10
|
+
# Repo root: parent of capper/tests
|
|
11
|
+
REPO_ROOT = Path(__file__).resolve().parent.parent.parent
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@pytest.mark.parametrize(
|
|
15
|
+
"script",
|
|
16
|
+
["getting_started", "models_and_factories", "reproducible_data", "custom_types"],
|
|
17
|
+
)
|
|
18
|
+
def test_docs_example_runs(script: str) -> None:
|
|
19
|
+
"""Run each docs example script; fail if it exits non-zero."""
|
|
20
|
+
path = REPO_ROOT / "docs" / "examples" / f"{script}.py"
|
|
21
|
+
assert path.exists(), f"Missing {path}"
|
|
22
|
+
env = os.environ.copy()
|
|
23
|
+
env["PYTHONPATH"] = str(REPO_ROOT) + os.pathsep + env.get("PYTHONPATH", "")
|
|
24
|
+
result = subprocess.run(
|
|
25
|
+
[sys.executable, str(path)],
|
|
26
|
+
cwd=str(REPO_ROOT),
|
|
27
|
+
env=env,
|
|
28
|
+
capture_output=True,
|
|
29
|
+
text=True,
|
|
30
|
+
timeout=10,
|
|
31
|
+
)
|
|
32
|
+
assert result.returncode == 0, (
|
|
33
|
+
f"docs/examples/{script}.py failed:\nstdout:\n{result.stdout}\nstderr:\n{result.stderr}"
|
|
34
|
+
)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Tests for Hypothesis strategies (require capper[hypothesis])."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
pytest.importorskip("hypothesis")
|
|
6
|
+
|
|
7
|
+
from hypothesis import given
|
|
8
|
+
from hypothesis import strategies as st
|
|
9
|
+
|
|
10
|
+
import capper.strategies # noqa: F401 - register types with Hypothesis
|
|
11
|
+
from capper import Email, Name
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@given(st.from_type(Name))
|
|
15
|
+
def test_from_type_name_produces_non_empty_string(name: Name) -> None:
|
|
16
|
+
"""st.from_type(Name) yields non-empty Name instances."""
|
|
17
|
+
assert isinstance(name, Name)
|
|
18
|
+
assert isinstance(name, str)
|
|
19
|
+
assert len(name) > 0
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@given(st.from_type(Email))
|
|
23
|
+
def test_from_type_email_contains_at(email: Email) -> None:
|
|
24
|
+
"""st.from_type(Email) yields strings containing '@'."""
|
|
25
|
+
assert isinstance(email, Email)
|
|
26
|
+
assert "@" in email
|
|
27
|
+
assert len(email) > 0
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@given(capper.strategies.for_type(Name))
|
|
31
|
+
def test_for_type_name_produces_name(name: Name) -> None:
|
|
32
|
+
"""strategies.for_type(Name) yields Name instances."""
|
|
33
|
+
assert isinstance(name, Name)
|
|
34
|
+
assert len(name) > 0
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
"""Tests for Polyfactory
|
|
1
|
+
"""Tests for Polyfactory: ModelFactory, DataclassFactory, shared Faker, auto-registration."""
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
|
|
5
|
-
from pydantic import BaseModel
|
|
6
5
|
from polyfactory.factories import DataclassFactory
|
|
7
6
|
from polyfactory.factories.pydantic_factory import ModelFactory
|
|
7
|
+
from pydantic import BaseModel
|
|
8
8
|
|
|
9
9
|
from capper import Email, Name
|
|
10
10
|
|
|
@@ -50,14 +50,15 @@ def test_seed_random_and_capper_seed_produce_same_value() -> None:
|
|
|
50
50
|
|
|
51
51
|
def test_model_factory_uses_capper_faker() -> None:
|
|
52
52
|
"""Asserts ModelFactory.__faker__ is capper's faker to document the shared instance."""
|
|
53
|
-
import capper
|
|
54
53
|
from polyfactory.factories.pydantic_factory import ModelFactory
|
|
55
54
|
|
|
55
|
+
import capper
|
|
56
|
+
|
|
56
57
|
assert ModelFactory.__faker__ is capper.faker
|
|
57
58
|
|
|
58
59
|
|
|
59
60
|
def test_dataclass_factory_builds_with_capper_types() -> None:
|
|
60
|
-
"""
|
|
61
|
+
"""Dataclass with Name/Email via DataclassFactory; asserts non-empty and valid email."""
|
|
61
62
|
|
|
62
63
|
@dataclass
|
|
63
64
|
class Person:
|
|
@@ -94,11 +95,12 @@ def test_dataclass_factory_batch() -> None:
|
|
|
94
95
|
|
|
95
96
|
|
|
96
97
|
def test_capper_types_auto_registered_with_polyfactory() -> None:
|
|
97
|
-
"""
|
|
98
|
-
from capper import PhoneNumber
|
|
98
|
+
"""Importing capper registers types so Polyfactory can build a model with PhoneNumber."""
|
|
99
99
|
from polyfactory.factories.pydantic_factory import ModelFactory
|
|
100
100
|
from pydantic import BaseModel
|
|
101
101
|
|
|
102
|
+
from capper import PhoneNumber
|
|
103
|
+
|
|
102
104
|
class Contact(BaseModel):
|
|
103
105
|
phone: PhoneNumber
|
|
104
106
|
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
import pytest
|
|
4
4
|
|
|
5
5
|
from capper import (
|
|
6
|
+
IP,
|
|
7
|
+
URL,
|
|
6
8
|
Address,
|
|
7
9
|
City,
|
|
8
10
|
Company,
|
|
@@ -16,7 +18,6 @@ from capper import (
|
|
|
16
18
|
DateTime,
|
|
17
19
|
Email,
|
|
18
20
|
FirstName,
|
|
19
|
-
IP,
|
|
20
21
|
Job,
|
|
21
22
|
LastName,
|
|
22
23
|
Name,
|
|
@@ -26,7 +27,6 @@ from capper import (
|
|
|
26
27
|
Product,
|
|
27
28
|
Sentence,
|
|
28
29
|
Time,
|
|
29
|
-
URL,
|
|
30
30
|
UserName,
|
|
31
31
|
)
|
|
32
32
|
|
|
@@ -62,7 +62,7 @@ from capper import (
|
|
|
62
62
|
],
|
|
63
63
|
)
|
|
64
64
|
def test_type_generates_non_empty_string(type_class: type) -> None:
|
|
65
|
-
"""
|
|
65
|
+
"""Each type's Faker provider exists and returns a non-empty value."""
|
|
66
66
|
from faker import Faker
|
|
67
67
|
|
|
68
68
|
faker = Faker()
|
|
@@ -77,13 +77,13 @@ def test_type_generates_non_empty_string(type_class: type) -> None:
|
|
|
77
77
|
[Name, Email, PhoneNumber, FirstName, Address, Sentence, CreditCardNumber],
|
|
78
78
|
)
|
|
79
79
|
def test_model_factory_builds_capper_type(type_class: type) -> None:
|
|
80
|
-
"""
|
|
81
|
-
from typing import Any
|
|
80
|
+
"""Pydantic model with one capper type via ModelFactory; asserts non-empty and type."""
|
|
81
|
+
from typing import Any, Type
|
|
82
82
|
|
|
83
83
|
from polyfactory.factories.pydantic_factory import ModelFactory
|
|
84
84
|
from pydantic import create_model
|
|
85
85
|
|
|
86
|
-
model_cls:
|
|
86
|
+
model_cls: Type[Any] = create_model("Model", value=(type_class, ...))
|
|
87
87
|
|
|
88
88
|
class ModelFactoryCls(ModelFactory[model_cls]): # type: ignore[valid-type]
|
|
89
89
|
pass
|
|
@@ -94,7 +94,7 @@ def test_model_factory_builds_capper_type(type_class: type) -> None:
|
|
|
94
94
|
|
|
95
95
|
|
|
96
96
|
def test_faker_kwargs_support() -> None:
|
|
97
|
-
"""
|
|
97
|
+
"""faker_kwargs passed to provider; ModelFactory builds and checks value."""
|
|
98
98
|
from capper.base import FakerType
|
|
99
99
|
|
|
100
100
|
class ShortSentence(FakerType):
|
|
@@ -123,10 +123,11 @@ def test_faker_kwargs_support() -> None:
|
|
|
123
123
|
|
|
124
124
|
def test_seed_reproducibility(seeded_faker: None) -> None:
|
|
125
125
|
"""Builds twice after seeding with same value; asserts identical generated name."""
|
|
126
|
-
from capper import Name, seed
|
|
127
126
|
from polyfactory.factories.pydantic_factory import ModelFactory
|
|
128
127
|
from pydantic import BaseModel
|
|
129
128
|
|
|
129
|
+
from capper import Name, seed
|
|
130
|
+
|
|
130
131
|
class User(BaseModel):
|
|
131
132
|
name: Name
|
|
132
133
|
|
|
@@ -1,8 +1,32 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: capper
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Semantic, typed wrappers for Faker with automatic Polyfactory integration
|
|
5
|
+
Author-email: Odos Matthews <odosmatthews@gmail.com>
|
|
6
|
+
Project-URL: Documentation, https://github.com/eddiethedean/capper#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/eddiethedean/capper
|
|
8
|
+
Requires-Python: >=3.9
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Requires-Dist: Faker>=20.0
|
|
11
|
+
Requires-Dist: Polyfactory>=2.0
|
|
12
|
+
Provides-Extra: pydantic
|
|
13
|
+
Requires-Dist: pydantic>=2.0; extra == "pydantic"
|
|
14
|
+
Provides-Extra: hypothesis
|
|
15
|
+
Requires-Dist: hypothesis>=6.0; extra == "hypothesis"
|
|
16
|
+
Provides-Extra: dev
|
|
17
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
18
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
19
|
+
Requires-Dist: pytest-xdist>=3.0; extra == "dev"
|
|
20
|
+
Requires-Dist: pydantic>=2.0; extra == "dev"
|
|
21
|
+
Requires-Dist: ruff>=0.4; extra == "dev"
|
|
22
|
+
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
23
|
+
Requires-Dist: hypothesis>=6.0; extra == "dev"
|
|
24
|
+
|
|
1
25
|
# Capper
|
|
2
26
|
|
|
3
27
|
[](https://pypi.org/project/capper/)
|
|
4
|
-
[](https://pypi.org/project/capper/)
|
|
29
|
+
[](https://github.com/eddiethedean/capper/actions/workflows/ci.yml)
|
|
6
30
|
[](https://docs.astral.sh/ruff/)
|
|
7
31
|
[](https://mypy-lang.org/)
|
|
8
32
|
|
|
@@ -23,11 +47,10 @@ Semantic, typed wrappers for [Faker](https://faker.readthedocs.io/) with automat
|
|
|
23
47
|
pip install capper
|
|
24
48
|
```
|
|
25
49
|
|
|
26
|
-
Requires **Python 3.9+**, **Faker >= 20.0**, and **Polyfactory >= 2.0**.
|
|
50
|
+
Requires **Python 3.9+**, **Faker >= 20.0**, and **Polyfactory >= 2.0**. Optional extras:
|
|
27
51
|
|
|
28
|
-
|
|
29
|
-
pip install capper[
|
|
30
|
-
```
|
|
52
|
+
- **Pydantic** (for Pydantic models): `pip install capper[pydantic]`
|
|
53
|
+
- **Hypothesis** (for property-based tests with `st.from_type(...)`): `pip install capper[hypothesis]`
|
|
31
54
|
|
|
32
55
|
## Usage
|
|
33
56
|
|
|
@@ -86,6 +109,8 @@ oevans@example.com
|
|
|
86
109
|
|
|
87
110
|
Works automatically. No extra steps. IDE autocompletion.
|
|
88
111
|
|
|
112
|
+
**New to Capper?** See the [Getting started](docs/user_guides/getting_started.md) guide and run the examples in `docs/examples/`.
|
|
113
|
+
|
|
89
114
|
## Available types
|
|
90
115
|
|
|
91
116
|
- **Person**: `Name`, `FirstName`, `LastName`, `Job`
|
|
@@ -104,6 +129,17 @@ See [docs/FAKER_PROVIDERS.md](https://github.com/eddiethedean/capper/blob/main/d
|
|
|
104
129
|
|
|
105
130
|
**Custom types:** Subclass `FakerType`, set `faker_provider` to the Faker method name (e.g. `"company"`), and optionally `faker_kwargs`. The type auto-registers with Polyfactory when the class is defined.
|
|
106
131
|
|
|
132
|
+
## CLI
|
|
133
|
+
|
|
134
|
+
Generate fake values from the command line:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
capper generate Name Email --count 5
|
|
138
|
+
capper generate Name Email --count 3 --seed 42
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Use `-n`/`--count` for the number of rows and `-s`/`--seed` for reproducible output. Type names are the same as the Python types (e.g. `Name`, `Email`, `Address`).
|
|
142
|
+
|
|
107
143
|
## Compatibility
|
|
108
144
|
|
|
109
145
|
Capper targets **Faker >= 20.0** and **Polyfactory >= 2.0**. Major Faker upgrades may change or rename provider methods; if a type fails, check [Faker's changelog](https://faker.readthedocs.io/en/stable/changelog.html) and [docs/FAKER_PROVIDERS.md](https://github.com/eddiethedean/capper/blob/main/docs/FAKER_PROVIDERS.md) and update the provider name if needed.
|
|
@@ -115,6 +151,8 @@ pip install -e ".[dev]"
|
|
|
115
151
|
pytest capper/tests
|
|
116
152
|
```
|
|
117
153
|
|
|
154
|
+
Lint and type-check: `ruff check .`, `ruff format .`, `mypy capper`.
|
|
155
|
+
|
|
118
156
|
Run tests with coverage: `pytest capper/tests --cov=capper --cov-report=term-missing`.
|
|
119
157
|
|
|
120
158
|
**Reproducibility:** Capper and Polyfactory share the same Faker instance, so one seed controls both capper types and built-in types (`str`, `int`, etc.):
|
|
@@ -145,13 +183,19 @@ Use `UserFactory.__random_seed__ = 42` to seed once when the factory class is cr
|
|
|
145
183
|
Releases are built and published to PyPI via [GitHub Actions](https://github.com/eddiethedean/capper/blob/main/.github/workflows/publish.yml). To publish:
|
|
146
184
|
|
|
147
185
|
1. Add a `PYPI_API_TOKEN` secret (PyPI API token) to the repo.
|
|
148
|
-
2. Create a GitHub release (tag e.g. `v0.
|
|
186
|
+
2. Create a GitHub release (tag e.g. `v0.2.0`). The workflow runs tests, builds the package, and uploads to PyPI.
|
|
149
187
|
|
|
150
188
|
To build and upload manually: `pip install build twine`, `python -m build`, `twine upload dist/*`.
|
|
151
189
|
|
|
152
|
-
##
|
|
153
|
-
|
|
154
|
-
- [
|
|
155
|
-
-
|
|
156
|
-
- [
|
|
157
|
-
- [
|
|
190
|
+
## Documentation
|
|
191
|
+
|
|
192
|
+
- **[Docs index](docs/README.md)** — overview and links to all documentation
|
|
193
|
+
- **User guides** (step-by-step, with runnable examples):
|
|
194
|
+
- [Getting started](docs/user_guides/getting_started.md) — install, first model, first factory
|
|
195
|
+
- [Models and factories](docs/user_guides/models_and_factories.md) — Pydantic, dataclasses, batches
|
|
196
|
+
- [Reproducible data](docs/user_guides/reproducible_data.md) — seeding for tests and demos
|
|
197
|
+
- [Custom types](docs/user_guides/custom_types.md) — `FakerType` subclasses and `faker_kwargs`
|
|
198
|
+
- [Package plan](docs/capper_package_plan.md) — design and rationale
|
|
199
|
+
- [Roadmap](docs/ROADMAP.md) — development phases and status
|
|
200
|
+
- [Faker provider mapping](docs/FAKER_PROVIDERS.md) — which Faker method each type uses
|
|
201
|
+
- [Example notebooks](docs/notebooks/README.md) — Jupyter notebooks in `docs/notebooks/`
|
|
@@ -2,6 +2,7 @@ README.md
|
|
|
2
2
|
pyproject.toml
|
|
3
3
|
capper/__init__.py
|
|
4
4
|
capper/base.py
|
|
5
|
+
capper/cli.py
|
|
5
6
|
capper/commerce.py
|
|
6
7
|
capper/date_time.py
|
|
7
8
|
capper/finance.py
|
|
@@ -9,14 +10,18 @@ capper/geo.py
|
|
|
9
10
|
capper/internet.py
|
|
10
11
|
capper/person.py
|
|
11
12
|
capper/phone.py
|
|
13
|
+
capper/strategies.py
|
|
12
14
|
capper/text.py
|
|
13
15
|
capper.egg-info/PKG-INFO
|
|
14
16
|
capper.egg-info/SOURCES.txt
|
|
15
17
|
capper.egg-info/dependency_links.txt
|
|
18
|
+
capper.egg-info/entry_points.txt
|
|
16
19
|
capper.egg-info/requires.txt
|
|
17
20
|
capper.egg-info/top_level.txt
|
|
18
21
|
capper/examples/user_factory.py
|
|
19
22
|
capper/tests/__init__.py
|
|
20
23
|
capper/tests/conftest.py
|
|
24
|
+
capper/tests/test_docs_examples.py
|
|
25
|
+
capper/tests/test_hypothesis_strategies.py
|
|
21
26
|
capper/tests/test_polyfactory_integration.py
|
|
22
27
|
capper/tests/test_types.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "capper"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.0"
|
|
8
8
|
description = "Semantic, typed wrappers for Faker with automatic Polyfactory integration"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -20,16 +20,26 @@ dependencies = [
|
|
|
20
20
|
pydantic = [
|
|
21
21
|
"pydantic>=2.0",
|
|
22
22
|
]
|
|
23
|
+
hypothesis = [
|
|
24
|
+
"hypothesis>=6.0",
|
|
25
|
+
]
|
|
23
26
|
dev = [
|
|
24
27
|
"pytest>=7.0",
|
|
25
28
|
"pytest-cov>=4.0",
|
|
29
|
+
"pytest-xdist>=3.0",
|
|
26
30
|
"pydantic>=2.0",
|
|
31
|
+
"ruff>=0.4",
|
|
32
|
+
"mypy>=1.0",
|
|
33
|
+
"hypothesis>=6.0",
|
|
27
34
|
]
|
|
28
35
|
|
|
29
36
|
[project.urls]
|
|
30
37
|
Documentation = "https://github.com/eddiethedean/capper#readme"
|
|
31
38
|
Repository = "https://github.com/eddiethedean/capper"
|
|
32
39
|
|
|
40
|
+
[project.scripts]
|
|
41
|
+
capper = "capper.cli:main"
|
|
42
|
+
|
|
33
43
|
[tool.setuptools.packages.find]
|
|
34
44
|
where = ["."]
|
|
35
45
|
include = ["capper*"]
|
|
@@ -38,3 +48,17 @@ include = ["capper*"]
|
|
|
38
48
|
testpaths = ["capper/tests"]
|
|
39
49
|
pythonpath = ["."]
|
|
40
50
|
addopts = ["-v", "--tb=short"]
|
|
51
|
+
|
|
52
|
+
[tool.ruff]
|
|
53
|
+
line-length = 100
|
|
54
|
+
target-version = "py39"
|
|
55
|
+
exclude = ["docs/notebooks"]
|
|
56
|
+
|
|
57
|
+
[tool.ruff.lint]
|
|
58
|
+
select = ["E", "F", "I"]
|
|
59
|
+
|
|
60
|
+
[tool.mypy]
|
|
61
|
+
python_version = "3.9"
|
|
62
|
+
warn_return_any = true
|
|
63
|
+
warn_unused_configs = true
|
|
64
|
+
disallow_untyped_defs = false
|
|
File without changes
|
|
File without changes
|
|
@@ -4,8 +4,8 @@ Run: python -m capper.examples.user_factory
|
|
|
4
4
|
Output varies each run; use capper.seed(n) for reproducible data.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from pydantic import BaseModel
|
|
8
7
|
from polyfactory.factories.pydantic_factory import ModelFactory
|
|
8
|
+
from pydantic import BaseModel
|
|
9
9
|
|
|
10
10
|
from capper import Email, Name
|
|
11
11
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|