capper 0.1.0__py3-none-any.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.
capper/__init__.py ADDED
@@ -0,0 +1,46 @@
1
+ """Capper: semantic Faker types with automatic Polyfactory integration.
2
+
3
+ Import types (e.g. ``Name``, ``Email``) and use them in Pydantic models, dataclasses,
4
+ or attrs; Polyfactory will generate values via Faker. Use ``seed(n)`` for reproducibility.
5
+ """
6
+
7
+ from .base import FakerType, faker, seed
8
+ from .commerce import Company, Currency, Price, Product
9
+ from .date_time import Date, DateTime, Time
10
+ from .finance import CreditCardExpiry, CreditCardNumber, CreditCardProvider
11
+ from .geo import Address, City, Country
12
+ from .internet import Email, IP, URL, UserName
13
+ from .person import FirstName, Job, LastName, Name
14
+ from .phone import CountryCallingCode, PhoneNumber
15
+ from .text import Paragraph, Sentence
16
+
17
+ __all__ = [
18
+ "Address",
19
+ "City",
20
+ "Company",
21
+ "Country",
22
+ "CountryCallingCode",
23
+ "CreditCardExpiry",
24
+ "CreditCardNumber",
25
+ "CreditCardProvider",
26
+ "Currency",
27
+ "Date",
28
+ "DateTime",
29
+ "Email",
30
+ "FakerType",
31
+ "FirstName",
32
+ "IP",
33
+ "Job",
34
+ "LastName",
35
+ "Name",
36
+ "Paragraph",
37
+ "PhoneNumber",
38
+ "Price",
39
+ "Product",
40
+ "Sentence",
41
+ "Time",
42
+ "URL",
43
+ "UserName",
44
+ "faker",
45
+ "seed",
46
+ ]
capper/base.py ADDED
@@ -0,0 +1,71 @@
1
+ """FakerType base class and shared Faker instance with automatic Polyfactory registration.
2
+
3
+ The module-level ``faker`` is attached to Polyfactory's BaseFactory so that one seed
4
+ controls both capper types and built-in types. Use ``seed(n)`` for reproducible data.
5
+ """
6
+
7
+ from typing import Any
8
+
9
+ from faker import Faker
10
+ from polyfactory.factories.base import BaseFactory
11
+
12
+ faker = Faker()
13
+
14
+ # Share this Faker with Polyfactory so one seed controls both capper types and built-in types.
15
+ BaseFactory.__faker__ = faker
16
+
17
+
18
+ def seed(seed_value: int) -> None:
19
+ """Seed the shared Faker instance for reproducible data.
20
+
21
+ Args:
22
+ seed_value: Integer seed; same value produces the same sequence of values.
23
+ """
24
+ faker.seed_instance(seed_value)
25
+
26
+
27
+ def _install_pydantic_schema() -> None:
28
+ """If Pydantic is available, attach __get_pydantic_core_schema__ to FakerType."""
29
+ try:
30
+ from pydantic import GetCoreSchemaHandler
31
+ from pydantic_core import CoreSchema, core_schema
32
+ except ImportError:
33
+ return
34
+
35
+ def __get_pydantic_core_schema__(
36
+ source_type: Any, handler: GetCoreSchemaHandler
37
+ ) -> CoreSchema:
38
+ """Validate as str then coerce to the FakerType subclass."""
39
+ return core_schema.no_info_after_validator_function(source_type, handler(str))
40
+
41
+ FakerType.__get_pydantic_core_schema__ = __get_pydantic_core_schema__ # type: ignore[attr-defined]
42
+
43
+
44
+ class FakerType(str):
45
+ """Base class for semantic Faker types; subclasses are auto-registered with Polyfactory.
46
+
47
+ Subclasses must set a non-empty ``faker_provider`` (the Faker method name).
48
+ Optional ``faker_kwargs`` is a dict of keyword arguments passed to that provider
49
+ (e.g. ``faker_kwargs = {"nb_words": 10}`` for ``sentence``).
50
+ """
51
+
52
+ faker_provider: str = ""
53
+
54
+ def __init_subclass__(cls, **kwargs: object) -> None:
55
+ super().__init_subclass__(**kwargs)
56
+ provider = getattr(cls, "faker_provider", None)
57
+ if provider:
58
+ _register(cls, provider)
59
+
60
+
61
+ _install_pydantic_schema()
62
+
63
+
64
+ def _register(cls: type, provider_name: str) -> None:
65
+ """Register a FakerType subclass with Polyfactory so factories can generate values."""
66
+ provider_kwargs = getattr(cls, "faker_kwargs", None) or {}
67
+
68
+ def _provide() -> str:
69
+ return getattr(faker, provider_name)(**provider_kwargs)
70
+
71
+ BaseFactory.add_provider(cls, _provide)
capper/commerce.py ADDED
@@ -0,0 +1,27 @@
1
+ """Commerce-related semantic Faker types (company, product, currency, price)."""
2
+
3
+ from .base import FakerType
4
+
5
+
6
+ class Company(FakerType):
7
+ """Company name. Uses Faker provider ``company``."""
8
+
9
+ faker_provider = "company"
10
+
11
+
12
+ class Product(FakerType):
13
+ """Product/catch phrase. Uses Faker provider ``catch_phrase``."""
14
+
15
+ faker_provider = "catch_phrase"
16
+
17
+
18
+ class Currency(FakerType):
19
+ """Currency code. Uses Faker provider ``currency_code``."""
20
+
21
+ faker_provider = "currency_code"
22
+
23
+
24
+ class Price(FakerType):
25
+ """Price tag string. Uses Faker provider ``pricetag``."""
26
+
27
+ faker_provider = "pricetag"
capper/date_time.py ADDED
@@ -0,0 +1,21 @@
1
+ """Date/time-related semantic Faker types (date, datetime, time as strings)."""
2
+
3
+ from .base import FakerType
4
+
5
+
6
+ class Date(FakerType):
7
+ """Date string. Uses Faker provider ``date``."""
8
+
9
+ faker_provider = "date"
10
+
11
+
12
+ class DateTime(FakerType):
13
+ """ISO 8601 datetime string. Uses Faker provider ``iso8601``."""
14
+
15
+ faker_provider = "iso8601"
16
+
17
+
18
+ class Time(FakerType):
19
+ """Time string. Uses Faker provider ``time``."""
20
+
21
+ faker_provider = "time"
@@ -0,0 +1,29 @@
1
+ """Example: Pydantic model with capper types and Polyfactory ModelFactory.
2
+
3
+ Run: python -m capper.examples.user_factory
4
+ Output varies each run; use capper.seed(n) for reproducible data.
5
+ """
6
+
7
+ from pydantic import BaseModel
8
+ from polyfactory.factories.pydantic_factory import ModelFactory
9
+
10
+ from capper import Email, Name
11
+
12
+
13
+ class User(BaseModel):
14
+ """Example Pydantic model with capper types."""
15
+
16
+ name: Name
17
+ email: Email
18
+
19
+
20
+ class UserFactory(ModelFactory[User]):
21
+ """Polyfactory factory for User; uses capper's Faker for name and email."""
22
+
23
+ pass
24
+
25
+
26
+ if __name__ == "__main__":
27
+ user = UserFactory.build()
28
+ print(user.name)
29
+ print(user.email)
capper/finance.py ADDED
@@ -0,0 +1,21 @@
1
+ """Finance / credit card semantic Faker types."""
2
+
3
+ from .base import FakerType
4
+
5
+
6
+ class CreditCardNumber(FakerType):
7
+ """Credit card number string. Uses Faker provider ``credit_card_number``."""
8
+
9
+ faker_provider = "credit_card_number"
10
+
11
+
12
+ class CreditCardExpiry(FakerType):
13
+ """Credit card expiry string. Uses Faker provider ``credit_card_expire``."""
14
+
15
+ faker_provider = "credit_card_expire"
16
+
17
+
18
+ class CreditCardProvider(FakerType):
19
+ """Credit card provider name. Uses Faker provider ``credit_card_provider``."""
20
+
21
+ faker_provider = "credit_card_provider"
capper/geo.py ADDED
@@ -0,0 +1,21 @@
1
+ """Geography-related semantic Faker types (address, city, country)."""
2
+
3
+ from .base import FakerType
4
+
5
+
6
+ class Address(FakerType):
7
+ """Street address. Uses Faker provider ``address``."""
8
+
9
+ faker_provider = "address"
10
+
11
+
12
+ class City(FakerType):
13
+ """City name. Uses Faker provider ``city``."""
14
+
15
+ faker_provider = "city"
16
+
17
+
18
+ class Country(FakerType):
19
+ """Country name. Uses Faker provider ``country``."""
20
+
21
+ faker_provider = "country"
capper/internet.py ADDED
@@ -0,0 +1,27 @@
1
+ """Internet-related semantic Faker types (email, URL, IP, username)."""
2
+
3
+ from .base import FakerType
4
+
5
+
6
+ class Email(FakerType):
7
+ """Email address. Uses Faker provider ``email``."""
8
+
9
+ faker_provider = "email"
10
+
11
+
12
+ class URL(FakerType):
13
+ """URL. Uses Faker provider ``url``."""
14
+
15
+ faker_provider = "url"
16
+
17
+
18
+ class IP(FakerType):
19
+ """IPv4 address. Uses Faker provider ``ipv4``."""
20
+
21
+ faker_provider = "ipv4"
22
+
23
+
24
+ class UserName(FakerType):
25
+ """Username. Uses Faker provider ``user_name``."""
26
+
27
+ faker_provider = "user_name"
capper/person.py ADDED
@@ -0,0 +1,27 @@
1
+ """Person-related semantic Faker types (name, job)."""
2
+
3
+ from .base import FakerType
4
+
5
+
6
+ class Name(FakerType):
7
+ """Full name. Uses Faker provider ``name``."""
8
+
9
+ faker_provider = "name"
10
+
11
+
12
+ class FirstName(FakerType):
13
+ """First/given name. Uses Faker provider ``first_name``."""
14
+
15
+ faker_provider = "first_name"
16
+
17
+
18
+ class LastName(FakerType):
19
+ """Last/family name. Uses Faker provider ``last_name``."""
20
+
21
+ faker_provider = "last_name"
22
+
23
+
24
+ class Job(FakerType):
25
+ """Job title. Uses Faker provider ``job``."""
26
+
27
+ faker_provider = "job"
capper/phone.py ADDED
@@ -0,0 +1,15 @@
1
+ """Phone-related semantic Faker types (phone number, country calling code)."""
2
+
3
+ from .base import FakerType
4
+
5
+
6
+ class PhoneNumber(FakerType):
7
+ """Phone number string. Uses Faker provider ``phone_number``."""
8
+
9
+ faker_provider = "phone_number"
10
+
11
+
12
+ class CountryCallingCode(FakerType):
13
+ """Country calling code. Uses Faker provider ``country_calling_code``."""
14
+
15
+ faker_provider = "country_calling_code"
@@ -0,0 +1 @@
1
+ """Capper test suite."""
@@ -0,0 +1,11 @@
1
+ """Shared pytest fixtures for capper tests."""
2
+
3
+ import pytest
4
+
5
+
6
+ @pytest.fixture
7
+ def seeded_faker() -> None:
8
+ """Seed the shared Faker (capper + Polyfactory) with 42 for reproducible test data."""
9
+ from capper import seed
10
+
11
+ seed(42)
@@ -0,0 +1,110 @@
1
+ """Tests for Polyfactory integration: ModelFactory, DataclassFactory, shared Faker, auto-registration."""
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from pydantic import BaseModel
6
+ from polyfactory.factories import DataclassFactory
7
+ from polyfactory.factories.pydantic_factory import ModelFactory
8
+
9
+ from capper import Email, Name
10
+
11
+
12
+ class User(BaseModel):
13
+ name: Name
14
+ email: Email
15
+
16
+
17
+ class UserFactory(ModelFactory[User]):
18
+ pass
19
+
20
+
21
+ def test_user_factory_builds_with_capper_types() -> None:
22
+ """Builds a User with Name and Email via ModelFactory; asserts non-empty and valid email."""
23
+ user = UserFactory.build()
24
+ assert isinstance(user.name, (str, Name))
25
+ assert isinstance(user.email, (str, Email))
26
+ assert len(user.name) > 0
27
+ assert "@" in user.email
28
+
29
+
30
+ def test_user_factory_builds_batch() -> None:
31
+ """Builds a batch of users; asserts count and that each has non-empty name and email."""
32
+ users = UserFactory.batch(3)
33
+ assert len(users) == 3
34
+ for user in users:
35
+ assert isinstance(user, User)
36
+ assert len(user.name) > 0
37
+ assert "@" in user.email
38
+
39
+
40
+ def test_seed_random_and_capper_seed_produce_same_value() -> None:
41
+ """Builds with UserFactory.seed_random(42) and with capper.seed(42) yield the same name."""
42
+ from capper import seed
43
+
44
+ UserFactory.seed_random(42)
45
+ user_after_seed_random = UserFactory.build()
46
+ seed(42)
47
+ user_after_capper_seed = UserFactory.build()
48
+ assert user_after_seed_random.name == user_after_capper_seed.name
49
+
50
+
51
+ def test_model_factory_uses_capper_faker() -> None:
52
+ """Asserts ModelFactory.__faker__ is capper's faker to document the shared instance."""
53
+ import capper
54
+ from polyfactory.factories.pydantic_factory import ModelFactory
55
+
56
+ assert ModelFactory.__faker__ is capper.faker
57
+
58
+
59
+ def test_dataclass_factory_builds_with_capper_types() -> None:
60
+ """Builds a dataclass with Name and Email via DataclassFactory; asserts non-empty and valid email."""
61
+
62
+ @dataclass
63
+ class Person:
64
+ name: Name
65
+ email: Email
66
+
67
+ class PersonFactory(DataclassFactory[Person]):
68
+ pass
69
+
70
+ person = PersonFactory.build()
71
+ assert isinstance(person.name, (str, Name))
72
+ assert isinstance(person.email, (str, Email))
73
+ assert len(person.name) > 0
74
+ assert "@" in person.email
75
+
76
+
77
+ def test_dataclass_factory_batch() -> None:
78
+ """Builds a batch of dataclass instances; asserts count and non-empty name and email."""
79
+
80
+ @dataclass
81
+ class Person:
82
+ name: Name
83
+ email: Email
84
+
85
+ class PersonFactory(DataclassFactory[Person]):
86
+ pass
87
+
88
+ people = PersonFactory.batch(2)
89
+ assert len(people) == 2
90
+ for person in people:
91
+ assert isinstance(person, Person)
92
+ assert len(person.name) > 0
93
+ assert "@" in person.email
94
+
95
+
96
+ def test_capper_types_auto_registered_with_polyfactory() -> None:
97
+ """Asserts that importing capper registers types so Polyfactory can build a model with PhoneNumber."""
98
+ from capper import PhoneNumber
99
+ from polyfactory.factories.pydantic_factory import ModelFactory
100
+ from pydantic import BaseModel
101
+
102
+ class Contact(BaseModel):
103
+ phone: PhoneNumber
104
+
105
+ class ContactFactory(ModelFactory[Contact]):
106
+ pass
107
+
108
+ contact = ContactFactory.build()
109
+ assert isinstance(contact.phone, (str, PhoneNumber))
110
+ assert len(contact.phone) > 0
@@ -0,0 +1,140 @@
1
+ """Tests for capper types: Faker provider availability, ModelFactory registration, kwargs, seed."""
2
+
3
+ import pytest
4
+
5
+ from capper import (
6
+ Address,
7
+ City,
8
+ Company,
9
+ Country,
10
+ CountryCallingCode,
11
+ CreditCardExpiry,
12
+ CreditCardNumber,
13
+ CreditCardProvider,
14
+ Currency,
15
+ Date,
16
+ DateTime,
17
+ Email,
18
+ FirstName,
19
+ IP,
20
+ Job,
21
+ LastName,
22
+ Name,
23
+ Paragraph,
24
+ PhoneNumber,
25
+ Price,
26
+ Product,
27
+ Sentence,
28
+ Time,
29
+ URL,
30
+ UserName,
31
+ )
32
+
33
+
34
+ @pytest.mark.parametrize(
35
+ "type_class",
36
+ [
37
+ Name,
38
+ FirstName,
39
+ LastName,
40
+ Job,
41
+ Address,
42
+ City,
43
+ Country,
44
+ Email,
45
+ URL,
46
+ IP,
47
+ UserName,
48
+ Company,
49
+ Product,
50
+ Currency,
51
+ Price,
52
+ Date,
53
+ DateTime,
54
+ Time,
55
+ Paragraph,
56
+ Sentence,
57
+ PhoneNumber,
58
+ CountryCallingCode,
59
+ CreditCardNumber,
60
+ CreditCardExpiry,
61
+ CreditCardProvider,
62
+ ],
63
+ )
64
+ def test_type_generates_non_empty_string(type_class: type) -> None:
65
+ """Asserts each type's Faker provider exists and returns a non-empty value (provider availability)."""
66
+ from faker import Faker
67
+
68
+ faker = Faker()
69
+ provider_name = getattr(type_class, "faker_provider")
70
+ value = getattr(faker, provider_name)()
71
+ assert isinstance(value, (str, type_class))
72
+ assert len(str(value)) > 0
73
+
74
+
75
+ @pytest.mark.parametrize(
76
+ "type_class",
77
+ [Name, Email, PhoneNumber, FirstName, Address, Sentence, CreditCardNumber],
78
+ )
79
+ def test_model_factory_builds_capper_type(type_class: type) -> None:
80
+ """Builds a Pydantic model with a single capper type via ModelFactory; asserts non-empty and type."""
81
+ from typing import Any
82
+
83
+ from polyfactory.factories.pydantic_factory import ModelFactory
84
+ from pydantic import create_model
85
+
86
+ model_cls: type[Any] = create_model("Model", value=(type_class, ...))
87
+
88
+ class ModelFactoryCls(ModelFactory[model_cls]): # type: ignore[valid-type]
89
+ pass
90
+
91
+ instance: Any = ModelFactoryCls.build()
92
+ assert isinstance(instance.value, (str, type_class))
93
+ assert len(str(instance.value)) > 0
94
+
95
+
96
+ def test_faker_kwargs_support() -> None:
97
+ """Asserts types can set faker_kwargs and provider is called with them; builds via ModelFactory and checks value."""
98
+ from capper.base import FakerType
99
+
100
+ class ShortSentence(FakerType):
101
+ faker_provider = "sentence"
102
+ faker_kwargs = {"nb_words": 5}
103
+
104
+ from faker import Faker
105
+
106
+ faker = Faker()
107
+ value = getattr(faker, "sentence")(**ShortSentence.faker_kwargs)
108
+ assert isinstance(value, str) and len(value) > 0
109
+
110
+ # Polyfactory uses the same kwargs via registration
111
+ from polyfactory.factories.pydantic_factory import ModelFactory as PFModelFactory
112
+ from pydantic import BaseModel
113
+
114
+ class FakerKwargsModel(BaseModel):
115
+ text: ShortSentence
116
+
117
+ class FakerKwargsModelFactory(PFModelFactory[FakerKwargsModel]):
118
+ pass
119
+
120
+ instance = FakerKwargsModelFactory.build()
121
+ assert isinstance(instance.text, (str, ShortSentence)) and len(instance.text) > 0
122
+
123
+
124
+ def test_seed_reproducibility(seeded_faker: None) -> None:
125
+ """Builds twice after seeding with same value; asserts identical generated name."""
126
+ from capper import Name, seed
127
+ from polyfactory.factories.pydantic_factory import ModelFactory
128
+ from pydantic import BaseModel
129
+
130
+ class User(BaseModel):
131
+ name: Name
132
+
133
+ class UserFactory(ModelFactory[User]):
134
+ pass
135
+
136
+ seed(42)
137
+ user1 = UserFactory.build()
138
+ seed(42)
139
+ user2 = UserFactory.build()
140
+ assert user1.name == user2.name
capper/text.py ADDED
@@ -0,0 +1,15 @@
1
+ """Text-related semantic Faker types (paragraph, sentence)."""
2
+
3
+ from .base import FakerType
4
+
5
+
6
+ class Paragraph(FakerType):
7
+ """Paragraph of text. Uses Faker provider ``paragraph``."""
8
+
9
+ faker_provider = "paragraph"
10
+
11
+
12
+ class Sentence(FakerType):
13
+ """Single sentence. Uses Faker provider ``sentence``."""
14
+
15
+ faker_provider = "sentence"
@@ -0,0 +1,175 @@
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
+ # Capper
20
+
21
+ [![PyPI](https://img.shields.io/pypi/v/capper.svg)](https://pypi.org/project/capper/)
22
+ [![Python](https://img.shields.io/pypi/pyversions/capper.svg)](https://pypi.org/project/capper/)
23
+ [![CI](https://img.shields.io/github/actions/workflow/status/eddiethedean/capper/publish.yml?branch=main)](https://github.com/eddiethedean/capper/actions/workflows/publish.yml)
24
+ [![Ruff](https://img.shields.io/badge/code%20style-ruff-000000.svg)](https://docs.astral.sh/ruff/)
25
+ [![mypy](https://img.shields.io/badge/mypy-checked-blue.svg)](https://mypy-lang.org/)
26
+
27
+ Semantic, typed wrappers for [Faker](https://faker.readthedocs.io/) with automatic [Polyfactory](https://polyfactory.litestar.dev/) integration.
28
+
29
+ **Source:** [github.com/eddiethedean/capper](https://github.com/eddiethedean/capper)
30
+
31
+ ## Why Capper?
32
+
33
+ - **Zero config** — Import a type; Polyfactory uses the right Faker provider. No manual registration.
34
+ - **Typed** — Use `Name`, `Email`, `PhoneNumber`, etc. in your models for clear intent and IDE support.
35
+ - **Multi-backend** — Works with Pydantic, dataclasses, attrs, and other [Polyfactory-supported](https://polyfactory.litestar.dev/) model types.
36
+ - **Optional Pydantic** — Install `capper` alone for dataclasses/attrs; add `capper[pydantic]` when you use Pydantic models.
37
+
38
+ ## Install
39
+
40
+ ```bash
41
+ pip install capper
42
+ ```
43
+
44
+ Requires **Python 3.9+**, **Faker >= 20.0**, and **Polyfactory >= 2.0**. For **Pydantic** models, install the optional extra:
45
+
46
+ ```bash
47
+ pip install capper[pydantic]
48
+ ```
49
+
50
+ ## Usage
51
+
52
+ **With Pydantic** (requires `capper[pydantic]`):
53
+
54
+ ```python
55
+ from pydantic import BaseModel
56
+ from capper import Name, Email
57
+ from polyfactory.factories.pydantic_factory import ModelFactory
58
+
59
+ class User(BaseModel):
60
+ name: Name
61
+ email: Email
62
+
63
+ class UserFactory(ModelFactory[User]):
64
+ pass
65
+
66
+ user = UserFactory.build()
67
+ print(user.name)
68
+ print(user.email)
69
+ ```
70
+
71
+ Example output (varies each run):
72
+
73
+ ```
74
+ Paul Blair
75
+ linda00@example.net
76
+ ```
77
+
78
+ **With dataclasses** (no Pydantic needed):
79
+
80
+ ```python
81
+ from dataclasses import dataclass
82
+ from capper import Name, Email
83
+ from polyfactory.factories import DataclassFactory
84
+
85
+ @dataclass
86
+ class User:
87
+ name: Name
88
+ email: Email
89
+
90
+ class UserFactory(DataclassFactory[User]):
91
+ pass
92
+
93
+ user = UserFactory.build()
94
+ print(user.name)
95
+ print(user.email)
96
+ ```
97
+
98
+ Example output (varies each run):
99
+
100
+ ```
101
+ Carly Jenkins
102
+ oevans@example.com
103
+ ```
104
+
105
+ Works automatically. No extra steps. IDE autocompletion.
106
+
107
+ ## Available types
108
+
109
+ - **Person**: `Name`, `FirstName`, `LastName`, `Job`
110
+ - **Geo**: `Address`, `City`, `Country`
111
+ - **Internet**: `Email`, `URL`, `IP`, `UserName`
112
+ - **Commerce**: `Company`, `Product`, `Currency`, `Price`
113
+ - **Date/time**: `Date`, `DateTime`, `Time`
114
+ - **Text**: `Paragraph`, `Sentence`
115
+ - **Phone**: `PhoneNumber`, `CountryCallingCode`
116
+ - **Finance**: `CreditCardNumber`, `CreditCardExpiry`, `CreditCardProvider`
117
+
118
+ Import from the top level: `from capper import Name, Email, Address, ...`
119
+ See [docs/FAKER_PROVIDERS.md](https://github.com/eddiethedean/capper/blob/main/docs/FAKER_PROVIDERS.md) for the Faker provider used by each type.
120
+
121
+ **Optional kwargs:** Subclass `FakerType` and set `faker_kwargs` to pass arguments to the Faker provider (e.g. `faker_kwargs = {"nb_words": 10}` for `Sentence`).
122
+
123
+ **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
+
125
+ ## Compatibility
126
+
127
+ 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.
128
+
129
+ ## Development
130
+
131
+ ```bash
132
+ pip install -e ".[dev]"
133
+ pytest capper/tests
134
+ ```
135
+
136
+ Run tests with coverage: `pytest capper/tests --cov=capper --cov-report=term-missing`.
137
+
138
+ **Reproducibility:** Capper and Polyfactory share the same Faker instance, so one seed controls both capper types and built-in types (`str`, `int`, etc.):
139
+
140
+ ```python
141
+ from capper import seed, Name
142
+ from polyfactory.factories.pydantic_factory import ModelFactory
143
+ from pydantic import BaseModel
144
+
145
+ class User(BaseModel):
146
+ name: Name
147
+
148
+ class UserFactory(ModelFactory[User]):
149
+ pass
150
+
151
+ # Either way seeds the shared Faker (same effect):
152
+ seed(42)
153
+ user1 = UserFactory.build()
154
+
155
+ UserFactory.seed_random(42)
156
+ user2 = UserFactory.build() # same data as user1 if you seed the same before each
157
+ ```
158
+
159
+ Use `UserFactory.__random_seed__ = 42` to seed once when the factory class is created, or call `seed(42)` / `UserFactory.seed_random(42)` before each build for identical builds.
160
+
161
+ ## Publishing
162
+
163
+ Releases are built and published to PyPI via [GitHub Actions](https://github.com/eddiethedean/capper/blob/main/.github/workflows/publish.yml). To publish:
164
+
165
+ 1. Add a `PYPI_API_TOKEN` secret (PyPI API token) to the repo.
166
+ 2. Create a GitHub release (tag e.g. `v0.1.0`). The workflow runs tests, builds the package, and uploads to PyPI.
167
+
168
+ To build and upload manually: `pip install build twine`, `python -m build`, `twine upload dist/*`.
169
+
170
+ ## Links
171
+
172
+ - [Documentation index](https://github.com/eddiethedean/capper/blob/main/docs/README.md) — overview of all docs
173
+ - [Package plan](https://github.com/eddiethedean/capper/blob/main/docs/capper_package_plan.md) — design and rationale
174
+ - [Roadmap](https://github.com/eddiethedean/capper/blob/main/docs/ROADMAP.md) — development phases and status
175
+ - [Faker provider mapping](https://github.com/eddiethedean/capper/blob/main/docs/FAKER_PROVIDERS.md) — type-to-provider reference
@@ -0,0 +1,19 @@
1
+ capper/__init__.py,sha256=mFGYiZp_IE-_S6oc_OTbzInOeouqUIUrkZKD9kfP7ug,1155
2
+ capper/base.py,sha256=ISVva0Sp6u2rFykNsewu7QqSty3Mx9K27XjQhb0MotA,2386
3
+ capper/commerce.py,sha256=oyqgxEOP13ZMekGnqvjQ8rtvHdNmtT1UdN8G5liZIqE,614
4
+ capper/date_time.py,sha256=E6Oo-Nn9pmhhIcyCvutBt1mQ2-IcD3UKQ84wtWsTwiI,453
5
+ capper/finance.py,sha256=m_sAf9hYfk2SJQAN2Txz-mqN74Bc81efrJumJssLr4k,568
6
+ capper/geo.py,sha256=0Vpl12EW9S2R0-HMq-ibOB89UYPfy65xg74THOzsrEs,441
7
+ capper/internet.py,sha256=Qv6U4sQICS9xiglIuAnlxbmsbVhqtsCei3Qcw6MyZ64,533
8
+ capper/person.py,sha256=bLY3bgt8-JGJ7gdrcuCHA4ZElkXNW_-HHOjhrG2TlkU,546
9
+ capper/phone.py,sha256=HDwMvYl9IQXDjj-gz2x4BS1_czXINT5ygGKXz7HYAu4,408
10
+ capper/text.py,sha256=9ol4om_2jyc9ef8sMwkUk9ArKNVNFeE40P_P97tjQmw,343
11
+ capper/examples/user_factory.py,sha256=uvl9sDL0ff_QqNp7W-ObP9tFbftvENJHYO7ZCZkgrYs,662
12
+ capper/tests/__init__.py,sha256=OKJbXCMYV4iSqd70wTWJShtXSybyYipOznkNXGAxwQk,25
13
+ capper/tests/conftest.py,sha256=MNmi3C-f0YPDY2ZS1nzgAs6CsBGrTNywqg3B9Syja-I,241
14
+ capper/tests/test_polyfactory_integration.py,sha256=cbCnCF0IU-SkMxNFNopDjdHYhySqDpz3YcysmfSJo0Y,3339
15
+ capper/tests/test_types.py,sha256=aSG5Ssi63shJXnXOicnEfQLdYG1NF-A7Qykec_YY57M,3617
16
+ capper-0.1.0.dist-info/METADATA,sha256=ynqY-vX8saHkVfjKQCQKQ6fcYu28eFe2xIWzm-2tpnw,6367
17
+ capper-0.1.0.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
18
+ capper-0.1.0.dist-info/top_level.txt,sha256=j2RYKSYNTMLv8Qp9F6ZY-Ypb6mz2vxbcGwDBEmw6ShI,7
19
+ capper-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ capper