ProvenPin Install

Generate Hypothesis strategies from polyfactory pydantic models: working versions (tested 2026-10-15)

Bridge a polyfactory ModelFactory into Hypothesis: regex strings, HttpUrl and other pydantic-core types come from the factory while the fields under test stay drawable and shrinkable.

passed6 passed · 0 failed2026-10-15 18:59 UTC
Status
passed
Last passed
2026-10-15 18:59 UTC
Tests
6 / 6
Language
Python
Runtime
Python 3.13.16
pinned versions4
hypothesis@6.169.4polyfactory@3.3.0pydantic@2.14.1pytest@9.1.1
run it
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt
.venv/bin/python -m pytest -q
test environment
Python 3.13.16
linux 6.17.0-1022-azure
x64
runner: github-actions 38790162748

The code

Exactly the files that passed. Your agent can pull them with get_recipe("hypothesis-polyfactory-models") (install), or see them on GitHub.

requirements.txt
hypothesis==6.169.4
polyfactory==3.3.0
pydantic==2.14.1
pytest==9.1.1
models.py
from enum import Enum

from pydantic import BaseModel, Field, HttpUrl


class Plan(str, Enum):
    free = "free"
    pro = "pro"


class Coupon(BaseModel):
    code: str = Field(pattern=r"^[A-Z]{2}-\d{4}$")


class Merchant(BaseModel):
    id: int = Field(ge=1)
    plan: Plan = Plan.free
    seats: int = Field(ge=1, le=100)
    callback_url: HttpUrl
factories.py
from polyfactory.factories.pydantic_factory import ModelFactory

from models import Coupon, Merchant


class CouponFactory(ModelFactory[Coupon]):
    __model__ = Coupon


class MerchantFactory(ModelFactory[Merchant]):
    __model__ = Merchant
strategies.py
from typing import Annotated, Any

import hypothesis.strategies as st
from polyfactory.factories.pydantic_factory import ModelFactory


def field(factory: type[ModelFactory], name: str) -> st.SearchStrategy[Any]:
    # pydantic stores Field(ge=...) as Annotated metadata; rebuild it so
    # Hypothesis can generate values that actually satisfy the constraint.
    info = factory.__model__.model_fields[name]
    if info.metadata:
        return st.from_type(Annotated[info.annotation, *info.metadata])
    return st.from_type(info.annotation)


def from_factory(
    factory: type[ModelFactory], **overrides: st.SearchStrategy[Any]
) -> st.SearchStrategy[Any]:
    # Only the named fields are owned by Hypothesis; the factory fills the rest
    # (regex strings, HttpUrl, custom providers) so pydantic validation passes.
    return st.builds(factory.build, **overrides)
tests/test_strategies.py
import hypothesis.strategies as st
import pytest
from hypothesis import given
from hypothesis.errors import HypothesisWarning, ResolutionFailed
from pydantic import HttpUrl, ValidationError

from factories import CouponFactory, MerchantFactory
from models import Coupon
from strategies import field, from_factory


def test_hypothesis_cannot_resolve_pydantic_url():
    with pytest.raises(ResolutionFailed):
        st.from_type(HttpUrl).validate()


def test_hypothesis_ignores_regex_pattern():
    strategy = st.builds(Coupon)
    with pytest.raises(ValidationError), pytest.warns(HypothesisWarning, match="pattern"):
        strategy.example()


@given(from_factory(MerchantFactory, seats=st.integers(min_value=1, max_value=100)))
def test_factory_fills_fields_hypothesis_cannot(merchant):
    assert isinstance(merchant.callback_url, HttpUrl)
    assert merchant.id >= 1
    assert 1 <= merchant.seats <= 100


@given(field(MerchantFactory, "seats"))
def test_field_helper_preserves_pydantic_bounds(seats):
    assert 1 <= seats <= 100


def test_factory_overrides_are_still_validated():
    with pytest.raises(ValidationError):
        MerchantFactory.build(seats=0)


def test_seed_random_reproduces_factory_offline():
    CouponFactory.seed_random(1234)
    first = CouponFactory.build()
    CouponFactory.seed_random(1234)
    second = CouponFactory.build()
    assert first == second
pytest.ini
[pytest]
pythonpath = .
filterwarnings =
    ignore::hypothesis.errors.NonInteractiveExampleWarning

Connect to ProvenPin

Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.

terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp

Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.