summaryrefslogtreecommitdiff
path: root/venv/lib/python3.11/site-packages/polyfactory/value_generators
diff options
context:
space:
mode:
Diffstat (limited to 'venv/lib/python3.11/site-packages/polyfactory/value_generators')
-rw-r--r--venv/lib/python3.11/site-packages/polyfactory/value_generators/__init__.py0
-rw-r--r--venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/__init__.cpython-311.pycbin0 -> 212 bytes
-rw-r--r--venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/complex_types.cpython-311.pycbin0 -> 6066 bytes
-rw-r--r--venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/constrained_collections.cpython-311.pycbin0 -> 4270 bytes
-rw-r--r--venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/constrained_dates.cpython-311.pycbin0 -> 1998 bytes
-rw-r--r--venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/constrained_numbers.cpython-311.pycbin0 -> 13729 bytes
-rw-r--r--venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/constrained_path.cpython-311.pycbin0 -> 1126 bytes
-rw-r--r--venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/constrained_strings.cpython-311.pycbin0 -> 4463 bytes
-rw-r--r--venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/constrained_url.cpython-311.pycbin0 -> 974 bytes
-rw-r--r--venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/constrained_uuid.cpython-311.pycbin0 -> 1694 bytes
-rw-r--r--venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/primitives.cpython-311.pycbin0 -> 5019 bytes
-rw-r--r--venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/regex.cpython-311.pycbin0 -> 12629 bytes
-rw-r--r--venv/lib/python3.11/site-packages/polyfactory/value_generators/complex_types.py111
-rw-r--r--venv/lib/python3.11/site-packages/polyfactory/value_generators/constrained_collections.py92
-rw-r--r--venv/lib/python3.11/site-packages/polyfactory/value_generators/constrained_dates.py41
-rw-r--r--venv/lib/python3.11/site-packages/polyfactory/value_generators/constrained_numbers.py440
-rw-r--r--venv/lib/python3.11/site-packages/polyfactory/value_generators/constrained_path.py13
-rw-r--r--venv/lib/python3.11/site-packages/polyfactory/value_generators/constrained_strings.py138
-rw-r--r--venv/lib/python3.11/site-packages/polyfactory/value_generators/constrained_url.py10
-rw-r--r--venv/lib/python3.11/site-packages/polyfactory/value_generators/constrained_uuid.py31
-rw-r--r--venv/lib/python3.11/site-packages/polyfactory/value_generators/primitives.py129
-rw-r--r--venv/lib/python3.11/site-packages/polyfactory/value_generators/regex.py150
22 files changed, 1155 insertions, 0 deletions
diff --git a/venv/lib/python3.11/site-packages/polyfactory/value_generators/__init__.py b/venv/lib/python3.11/site-packages/polyfactory/value_generators/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/polyfactory/value_generators/__init__.py
diff --git a/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/__init__.cpython-311.pyc b/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/__init__.cpython-311.pyc
new file mode 100644
index 0000000..c95bde0
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/__init__.cpython-311.pyc
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/complex_types.cpython-311.pyc b/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/complex_types.cpython-311.pyc
new file mode 100644
index 0000000..b99a526
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/complex_types.cpython-311.pyc
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/constrained_collections.cpython-311.pyc b/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/constrained_collections.cpython-311.pyc
new file mode 100644
index 0000000..bb33c8a
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/constrained_collections.cpython-311.pyc
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/constrained_dates.cpython-311.pyc b/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/constrained_dates.cpython-311.pyc
new file mode 100644
index 0000000..f649c36
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/constrained_dates.cpython-311.pyc
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/constrained_numbers.cpython-311.pyc b/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/constrained_numbers.cpython-311.pyc
new file mode 100644
index 0000000..926ca52
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/constrained_numbers.cpython-311.pyc
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/constrained_path.cpython-311.pyc b/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/constrained_path.cpython-311.pyc
new file mode 100644
index 0000000..5808e81
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/constrained_path.cpython-311.pyc
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/constrained_strings.cpython-311.pyc b/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/constrained_strings.cpython-311.pyc
new file mode 100644
index 0000000..53efa2f
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/constrained_strings.cpython-311.pyc
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/constrained_url.cpython-311.pyc b/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/constrained_url.cpython-311.pyc
new file mode 100644
index 0000000..4dd4ad5
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/constrained_url.cpython-311.pyc
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/constrained_uuid.cpython-311.pyc b/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/constrained_uuid.cpython-311.pyc
new file mode 100644
index 0000000..e653a72
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/constrained_uuid.cpython-311.pyc
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/primitives.cpython-311.pyc b/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/primitives.cpython-311.pyc
new file mode 100644
index 0000000..97787ad
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/primitives.cpython-311.pyc
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/regex.cpython-311.pyc b/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/regex.cpython-311.pyc
new file mode 100644
index 0000000..319510e
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/polyfactory/value_generators/__pycache__/regex.cpython-311.pyc
Binary files differ
diff --git a/venv/lib/python3.11/site-packages/polyfactory/value_generators/complex_types.py b/venv/lib/python3.11/site-packages/polyfactory/value_generators/complex_types.py
new file mode 100644
index 0000000..2706891
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/polyfactory/value_generators/complex_types.py
@@ -0,0 +1,111 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, AbstractSet, Any, Iterable, MutableMapping, MutableSequence, Set, Tuple, cast
+
+from typing_extensions import is_typeddict
+
+from polyfactory.constants import INSTANTIABLE_TYPE_MAPPING, PY_38
+from polyfactory.field_meta import FieldMeta
+from polyfactory.utils.model_coverage import CoverageContainer
+
+if TYPE_CHECKING:
+ from polyfactory.factories.base import BaseFactory
+
+
+def handle_collection_type(field_meta: FieldMeta, container_type: type, factory: type[BaseFactory[Any]]) -> Any:
+ """Handle generation of container types recursively.
+
+ :param container_type: A type that can accept type arguments.
+ :param factory: A factory.
+ :param field_meta: A field meta instance.
+
+ :returns: A built result.
+ """
+
+ if PY_38 and container_type in INSTANTIABLE_TYPE_MAPPING:
+ container_type = INSTANTIABLE_TYPE_MAPPING[container_type] # type: ignore[assignment]
+
+ container = container_type()
+ if not field_meta.children:
+ return container
+
+ if issubclass(container_type, MutableMapping) or is_typeddict(container_type):
+ for key_field_meta, value_field_meta in cast(
+ Iterable[Tuple[FieldMeta, FieldMeta]],
+ zip(field_meta.children[::2], field_meta.children[1::2]),
+ ):
+ key = factory.get_field_value(key_field_meta)
+ value = factory.get_field_value(value_field_meta)
+ container[key] = value
+ return container
+
+ if issubclass(container_type, MutableSequence):
+ container.extend([factory.get_field_value(subfield_meta) for subfield_meta in field_meta.children])
+ return container
+
+ if issubclass(container_type, Set):
+ for subfield_meta in field_meta.children:
+ container.add(factory.get_field_value(subfield_meta))
+ return container
+
+ if issubclass(container_type, AbstractSet):
+ return container.union(handle_collection_type(field_meta, set, factory))
+
+ if issubclass(container_type, tuple):
+ return container_type(map(factory.get_field_value, field_meta.children))
+
+ msg = f"Unsupported container type: {container_type}"
+ raise NotImplementedError(msg)
+
+
+def handle_collection_type_coverage(
+ field_meta: FieldMeta,
+ container_type: type,
+ factory: type[BaseFactory[Any]],
+) -> Any:
+ """Handle coverage generation of container types recursively.
+
+ :param container_type: A type that can accept type arguments.
+ :param factory: A factory.
+ :param field_meta: A field meta instance.
+
+ :returns: An unresolved built result.
+ """
+ container = container_type()
+ if not field_meta.children:
+ return container
+
+ if issubclass(container_type, MutableMapping) or is_typeddict(container_type):
+ for key_field_meta, value_field_meta in cast(
+ Iterable[Tuple[FieldMeta, FieldMeta]],
+ zip(field_meta.children[::2], field_meta.children[1::2]),
+ ):
+ key = CoverageContainer(factory.get_field_value_coverage(key_field_meta))
+ value = CoverageContainer(factory.get_field_value_coverage(value_field_meta))
+ container[key] = value
+ return container
+
+ if issubclass(container_type, MutableSequence):
+ container_instance = container_type()
+ for subfield_meta in field_meta.children:
+ container_instance.extend(factory.get_field_value_coverage(subfield_meta))
+
+ return container_instance
+
+ if issubclass(container_type, Set):
+ set_instance = container_type()
+ for subfield_meta in field_meta.children:
+ set_instance = set_instance.union(factory.get_field_value_coverage(subfield_meta))
+
+ return set_instance
+
+ if issubclass(container_type, AbstractSet):
+ return container.union(handle_collection_type_coverage(field_meta, set, factory))
+
+ if issubclass(container_type, tuple):
+ return container_type(
+ CoverageContainer(factory.get_field_value_coverage(subfield_meta)) for subfield_meta in field_meta.children
+ )
+
+ msg = f"Unsupported container type: {container_type}"
+ raise NotImplementedError(msg)
diff --git a/venv/lib/python3.11/site-packages/polyfactory/value_generators/constrained_collections.py b/venv/lib/python3.11/site-packages/polyfactory/value_generators/constrained_collections.py
new file mode 100644
index 0000000..405d02d
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/polyfactory/value_generators/constrained_collections.py
@@ -0,0 +1,92 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Callable, List, Mapping, TypeVar, cast
+
+from polyfactory.exceptions import ParameterException
+from polyfactory.field_meta import FieldMeta
+
+if TYPE_CHECKING:
+ from polyfactory.factories.base import BaseFactory
+
+T = TypeVar("T", list, set, frozenset)
+
+
+def handle_constrained_collection(
+ collection_type: Callable[..., T],
+ factory: type[BaseFactory[Any]],
+ field_meta: FieldMeta,
+ item_type: Any,
+ max_items: int | None = None,
+ min_items: int | None = None,
+ unique_items: bool = False,
+) -> T:
+ """Generate a constrained list or set.
+
+ :param collection_type: A type that can accept type arguments.
+ :param factory: A factory.
+ :param field_meta: A field meta instance.
+ :param item_type: Type of the collection items.
+ :param max_items: Maximal number of items.
+ :param min_items: Minimal number of items.
+ :param unique_items: Whether the items should be unique.
+
+ :returns: A collection value.
+ """
+ min_items = abs(min_items if min_items is not None else (max_items or 0))
+ max_items = abs(max_items if max_items is not None else min_items + 1)
+
+ if max_items < min_items:
+ msg = "max_items must be larger or equal to min_items"
+ raise ParameterException(msg)
+
+ collection: set[T] | list[T] = set() if (collection_type in (frozenset, set) or unique_items) else []
+
+ try:
+ length = factory.__random__.randint(min_items, max_items) or 1
+ while len(collection) < length:
+ value = factory.get_field_value(field_meta)
+ if isinstance(collection, set):
+ collection.add(value)
+ else:
+ collection.append(value)
+ return collection_type(collection)
+ except TypeError as e:
+ msg = f"cannot generate a constrained collection of type: {item_type}"
+ raise ParameterException(msg) from e
+
+
+def handle_constrained_mapping(
+ factory: type[BaseFactory[Any]],
+ field_meta: FieldMeta,
+ max_items: int | None = None,
+ min_items: int | None = None,
+) -> Mapping[Any, Any]:
+ """Generate a constrained mapping.
+
+ :param factory: A factory.
+ :param field_meta: A field meta instance.
+ :param max_items: Maximal number of items.
+ :param min_items: Minimal number of items.
+
+ :returns: A mapping instance.
+ """
+ min_items = abs(min_items if min_items is not None else (max_items or 0))
+ max_items = abs(max_items if max_items is not None else min_items + 1)
+
+ if max_items < min_items:
+ msg = "max_items must be larger or equal to min_items"
+ raise ParameterException(msg)
+
+ length = factory.__random__.randint(min_items, max_items) or 1
+
+ collection: dict[Any, Any] = {}
+
+ children = cast(List[FieldMeta], field_meta.children)
+ key_field_meta = children[0]
+ value_field_meta = children[1]
+ while len(collection) < length:
+ key = factory.get_field_value(key_field_meta)
+ value = factory.get_field_value(value_field_meta)
+ collection[key] = value
+
+ return collection
diff --git a/venv/lib/python3.11/site-packages/polyfactory/value_generators/constrained_dates.py b/venv/lib/python3.11/site-packages/polyfactory/value_generators/constrained_dates.py
new file mode 100644
index 0000000..4e92601
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/polyfactory/value_generators/constrained_dates.py
@@ -0,0 +1,41 @@
+from __future__ import annotations
+
+from datetime import date, datetime, timedelta, timezone, tzinfo
+from typing import TYPE_CHECKING, cast
+
+if TYPE_CHECKING:
+ from faker import Faker
+
+
+def handle_constrained_date(
+ faker: Faker,
+ ge: date | None = None,
+ gt: date | None = None,
+ le: date | None = None,
+ lt: date | None = None,
+ tz: tzinfo = timezone.utc,
+) -> date:
+ """Generates a date value fulfilling the expected constraints.
+
+ :param faker: An instance of faker.
+ :param lt: Less than value.
+ :param le: Less than or equal value.
+ :param gt: Greater than value.
+ :param ge: Greater than or equal value.
+ :param tz: A timezone.
+
+ :returns: A date instance.
+ """
+ start_date = datetime.now(tz=tz).date() - timedelta(days=100)
+ if ge:
+ start_date = ge
+ elif gt:
+ start_date = gt + timedelta(days=1)
+
+ end_date = datetime.now(tz=timezone.utc).date() + timedelta(days=100)
+ if le:
+ end_date = le
+ elif lt:
+ end_date = lt - timedelta(days=1)
+
+ return cast("date", faker.date_between(start_date=start_date, end_date=end_date))
diff --git a/venv/lib/python3.11/site-packages/polyfactory/value_generators/constrained_numbers.py b/venv/lib/python3.11/site-packages/polyfactory/value_generators/constrained_numbers.py
new file mode 100644
index 0000000..23516ce
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/polyfactory/value_generators/constrained_numbers.py
@@ -0,0 +1,440 @@
+from __future__ import annotations
+
+from decimal import Decimal
+from sys import float_info
+from typing import TYPE_CHECKING, Any, Protocol, TypeVar, cast
+
+from polyfactory.exceptions import ParameterException
+from polyfactory.value_generators.primitives import create_random_decimal, create_random_float, create_random_integer
+
+if TYPE_CHECKING:
+ from random import Random
+
+T = TypeVar("T", Decimal, int, float)
+
+
+class NumberGeneratorProtocol(Protocol[T]):
+ """Protocol for custom callables used to generate numerical values"""
+
+ def __call__(self, random: "Random", minimum: T | None = None, maximum: T | None = None) -> T:
+ """Signature of the callable.
+
+ :param random: An instance of random.
+ :param minimum: A minimum value.
+ :param maximum: A maximum value.
+ :return: The generated numeric value.
+ """
+ ...
+
+
+def almost_equal_floats(value_1: float, value_2: float, *, delta: float = 1e-8) -> bool:
+ """Return True if two floats are almost equal
+
+ :param value_1: A float value.
+ :param value_2: A float value.
+ :param delta: A minimal delta.
+
+ :returns: Boolean dictating whether the floats can be considered equal - given python's problematic comparison of floats.
+ """
+ return abs(value_1 - value_2) <= delta
+
+
+def is_multiply_of_multiple_of_in_range(
+ minimum: T,
+ maximum: T,
+ multiple_of: T,
+) -> bool:
+ """Determine if at least one multiply of `multiple_of` lies in the given range.
+
+ :param minimum: T: A minimum value.
+ :param maximum: T: A maximum value.
+ :param multiple_of: T: A value to use as a base for multiplication.
+
+ :returns: Boolean dictating whether at least one multiply of `multiple_of` lies in the given range between minimum and maximum.
+ """
+
+ # if the range has infinity on one of its ends then infinite number of multipliers
+ # can be found within the range
+
+ # if we were given floats and multiple_of is really close to zero then it doesn't make sense
+ # to continue trying to check the range
+ if (
+ isinstance(minimum, float)
+ and isinstance(multiple_of, float)
+ and minimum / multiple_of in [float("+inf"), float("-inf")]
+ ):
+ return False
+
+ multiplier = round(minimum / multiple_of)
+ step = 1 if multiple_of > 0 else -1
+
+ # since rounding can go either up or down we may end up in a situation when
+ # minimum is less or equal to `multiplier * multiple_of`
+ # or when it is greater than `multiplier * multiple_of`
+ # (in this case minimum is less than `(multiplier + 1)* multiple_of`). So we need to check
+ # that any of two values is inside the given range. ASCII graphic below explain this
+ #
+ # minimum
+ # -----------------+-------+-----------------------------------+----------------------------
+ #
+ #
+ # minimum
+ # -------------------------+--------+--------------------------+----------------------------
+ #
+ # since `multiple_of` can be a negative number adding +1 to `multiplier` drives `(multiplier + 1) * multiple_of``
+ # away from `minimum` to the -infinity. It looks like this:
+ # minimum
+ # -----------------------+--------------------------------+------------------------+--------
+ #
+ # so for negative `multiple_of` we want to subtract 1 from multiplier
+ for multiply in [multiplier * multiple_of, (multiplier + step) * multiple_of]:
+ multiply_float = float(multiply)
+ if (
+ almost_equal_floats(multiply_float, float(minimum))
+ or almost_equal_floats(multiply_float, float(maximum))
+ or minimum < multiply < maximum
+ ):
+ return True
+
+ return False
+
+
+def passes_pydantic_multiple_validator(value: T, multiple_of: T) -> bool:
+ """Determine whether a given value passes the pydantic multiple_of validation.
+
+ :param value: A numeric value.
+ :param multiple_of: Another numeric value.
+
+ :returns: Boolean dictating whether value is a multiple of value.
+
+ """
+ if multiple_of == 0:
+ return True
+ mod = float(value) / float(multiple_of) % 1
+ return almost_equal_floats(mod, 0.0) or almost_equal_floats(mod, 1.0)
+
+
+def get_increment(t_type: type[T]) -> T:
+ """Get a small increment base to add to constrained values, i.e. lt/gt entries.
+
+ :param t_type: A value of type T.
+
+ :returns: An increment T.
+ """
+ values: dict[Any, Any] = {
+ int: 1,
+ float: float_info.epsilon,
+ Decimal: Decimal("0.001"),
+ }
+ return cast("T", values[t_type])
+
+
+def get_value_or_none(
+ t_type: type[T],
+ lt: T | None = None,
+ le: T | None = None,
+ gt: T | None = None,
+ ge: T | None = None,
+) -> tuple[T | None, T | None]:
+ """Return an optional value.
+
+ :param equal_value: An GE/LE value.
+ :param constrained: An GT/LT value.
+ :param increment: increment
+
+ :returns: Optional T.
+ """
+ if ge is not None:
+ minimum_value = ge
+ elif gt is not None:
+ minimum_value = gt + get_increment(t_type)
+ else:
+ minimum_value = None
+
+ if le is not None:
+ maximum_value = le
+ elif lt is not None:
+ maximum_value = lt - get_increment(t_type)
+ else:
+ maximum_value = None
+ return minimum_value, maximum_value
+
+
+def get_constrained_number_range(
+ t_type: type[T],
+ random: Random,
+ lt: T | None = None,
+ le: T | None = None,
+ gt: T | None = None,
+ ge: T | None = None,
+ multiple_of: T | None = None,
+) -> tuple[T | None, T | None]:
+ """Return the minimum and maximum values given a field_meta's constraints.
+
+ :param t_type: A primitive constructor - int, float or Decimal.
+ :param random: An instance of Random.
+ :param lt: Less than value.
+ :param le: Less than or equal value.
+ :param gt: Greater than value.
+ :param ge: Greater than or equal value.
+ :param multiple_of: Multiple of value.
+
+ :returns: a tuple of optional minimum and maximum values.
+ """
+ seed = t_type(random.random() * 10)
+ minimum, maximum = get_value_or_none(lt=lt, le=le, gt=gt, ge=ge, t_type=t_type)
+
+ if minimum is not None and maximum is not None and maximum < minimum:
+ msg = "maximum value must be greater than minimum value"
+ raise ParameterException(msg)
+
+ if multiple_of is None:
+ if minimum is not None and maximum is None:
+ return (
+ (minimum, seed) if minimum == 0 else (minimum, minimum + seed)
+ ) # pyright: ignore[reportGeneralTypeIssues]
+ if maximum is not None and minimum is None:
+ return maximum - seed, maximum
+ else:
+ if multiple_of == 0.0: # TODO: investigate @guacs # noqa: PLR2004, FIX002
+ msg = "multiple_of can not be zero"
+ raise ParameterException(msg)
+ if (
+ minimum is not None
+ and maximum is not None
+ and not is_multiply_of_multiple_of_in_range(minimum=minimum, maximum=maximum, multiple_of=multiple_of)
+ ):
+ msg = "given range should include at least one multiply of multiple_of"
+ raise ParameterException(msg)
+
+ return minimum, maximum
+
+
+def generate_constrained_number(
+ random: Random,
+ minimum: T | None,
+ maximum: T | None,
+ multiple_of: T | None,
+ method: "NumberGeneratorProtocol[T]",
+) -> T:
+ """Generate a constrained number, output depends on the passed in callbacks.
+
+ :param random: An instance of random.
+ :param minimum: A minimum value.
+ :param maximum: A maximum value.
+ :param multiple_of: A multiple of value.
+ :param method: A function that generates numbers of type T.
+
+ :returns: A value of type T.
+ """
+ if minimum is None or maximum is None:
+ return multiple_of if multiple_of is not None else method(random=random)
+ if multiple_of is None:
+ return method(random=random, minimum=minimum, maximum=maximum)
+ if multiple_of >= minimum:
+ return multiple_of
+ result = minimum
+ while not passes_pydantic_multiple_validator(result, multiple_of):
+ result = round(method(random=random, minimum=minimum, maximum=maximum) / multiple_of) * multiple_of
+ return result
+
+
+def handle_constrained_int(
+ random: Random,
+ multiple_of: int | None = None,
+ gt: int | None = None,
+ ge: int | None = None,
+ lt: int | None = None,
+ le: int | None = None,
+) -> int:
+ """Handle constrained integers.
+
+ :param random: An instance of Random.
+ :param lt: Less than value.
+ :param le: Less than or equal value.
+ :param gt: Greater than value.
+ :param ge: Greater than or equal value.
+ :param multiple_of: Multiple of value.
+
+ :returns: An integer.
+
+ """
+
+ minimum, maximum = get_constrained_number_range(
+ gt=gt,
+ ge=ge,
+ lt=lt,
+ le=le,
+ t_type=int,
+ multiple_of=multiple_of,
+ random=random,
+ )
+ return generate_constrained_number(
+ random=random,
+ minimum=minimum,
+ maximum=maximum,
+ multiple_of=multiple_of,
+ method=create_random_integer,
+ )
+
+
+def handle_constrained_float(
+ random: Random,
+ multiple_of: float | None = None,
+ gt: float | None = None,
+ ge: float | None = None,
+ lt: float | None = None,
+ le: float | None = None,
+) -> float:
+ """Handle constrained floats.
+
+ :param random: An instance of Random.
+ :param lt: Less than value.
+ :param le: Less than or equal value.
+ :param gt: Greater than value.
+ :param ge: Greater than or equal value.
+ :param multiple_of: Multiple of value.
+
+ :returns: A float.
+ """
+
+ minimum, maximum = get_constrained_number_range(
+ gt=gt,
+ ge=ge,
+ lt=lt,
+ le=le,
+ t_type=float,
+ multiple_of=multiple_of,
+ random=random,
+ )
+
+ return generate_constrained_number(
+ random=random,
+ minimum=minimum,
+ maximum=maximum,
+ multiple_of=multiple_of,
+ method=create_random_float,
+ )
+
+
+def validate_max_digits(
+ max_digits: int,
+ minimum: Decimal | None,
+ decimal_places: int | None,
+) -> None:
+ """Validate that max digits is greater than minimum and decimal places.
+
+ :param max_digits: The maximal number of digits for the decimal.
+ :param minimum: Minimal value.
+ :param decimal_places: Number of decimal places
+
+ :returns: 'None'
+
+ """
+ if max_digits <= 0:
+ msg = "max_digits must be greater than 0"
+ raise ParameterException(msg)
+
+ if minimum is not None:
+ min_str = str(minimum).split(".")[1] if "." in str(minimum) else str(minimum)
+
+ if max_digits <= len(min_str):
+ msg = "minimum is greater than max_digits"
+ raise ParameterException(msg)
+
+ if decimal_places is not None and max_digits <= decimal_places:
+ msg = "max_digits must be greater than decimal places"
+ raise ParameterException(msg)
+
+
+def handle_decimal_length(
+ generated_decimal: Decimal,
+ decimal_places: int | None,
+ max_digits: int | None,
+) -> Decimal:
+ """Handle the length of the decimal.
+
+ :param generated_decimal: A decimal value.
+ :param decimal_places: Number of decimal places.
+ :param max_digits: Maximal number of digits.
+
+ """
+ string_number = str(generated_decimal)
+ sign = "-" if "-" in string_number else "+"
+ string_number = string_number.replace("-", "")
+ whole_numbers, decimals = string_number.split(".")
+
+ if (
+ max_digits is not None
+ and decimal_places is not None
+ and len(whole_numbers) + decimal_places > max_digits
+ or (max_digits is None or decimal_places is None)
+ and max_digits is not None
+ ):
+ max_decimals = max_digits - len(whole_numbers)
+ elif max_digits is not None:
+ max_decimals = decimal_places # type: ignore[assignment]
+ else:
+ max_decimals = cast("int", decimal_places)
+
+ if max_decimals < 0: # pyright: ignore[reportOptionalOperand]
+ return Decimal(sign + whole_numbers[:max_decimals])
+
+ decimals = decimals[:max_decimals]
+ return Decimal(sign + whole_numbers + "." + decimals[:decimal_places])
+
+
+def handle_constrained_decimal(
+ random: Random,
+ multiple_of: Decimal | None = None,
+ decimal_places: int | None = None,
+ max_digits: int | None = None,
+ gt: Decimal | None = None,
+ ge: Decimal | None = None,
+ lt: Decimal | None = None,
+ le: Decimal | None = None,
+) -> Decimal:
+ """Handle a constrained decimal.
+
+ :param random: An instance of Random.
+ :param multiple_of: Multiple of value.
+ :param decimal_places: Number of decimal places.
+ :param max_digits: Maximal number of digits.
+ :param lt: Less than value.
+ :param le: Less than or equal value.
+ :param gt: Greater than value.
+ :param ge: Greater than or equal value.
+
+ :returns: A decimal.
+
+ """
+
+ minimum, maximum = get_constrained_number_range(
+ gt=gt,
+ ge=ge,
+ lt=lt,
+ le=le,
+ multiple_of=multiple_of,
+ t_type=Decimal,
+ random=random,
+ )
+
+ if max_digits is not None:
+ validate_max_digits(max_digits=max_digits, minimum=minimum, decimal_places=decimal_places)
+
+ generated_decimal = generate_constrained_number(
+ random=random,
+ minimum=minimum,
+ maximum=maximum,
+ multiple_of=multiple_of,
+ method=create_random_decimal,
+ )
+
+ if max_digits is not None or decimal_places is not None:
+ return handle_decimal_length(
+ generated_decimal=generated_decimal,
+ max_digits=max_digits,
+ decimal_places=decimal_places,
+ )
+
+ return generated_decimal
diff --git a/venv/lib/python3.11/site-packages/polyfactory/value_generators/constrained_path.py b/venv/lib/python3.11/site-packages/polyfactory/value_generators/constrained_path.py
new file mode 100644
index 0000000..debaf86
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/polyfactory/value_generators/constrained_path.py
@@ -0,0 +1,13 @@
+from os.path import realpath
+from pathlib import Path
+from typing import Literal, cast
+
+from faker import Faker
+
+
+def handle_constrained_path(constraint: Literal["file", "dir", "new"], faker: Faker) -> Path:
+ if constraint == "new":
+ return cast("Path", faker.file_path(depth=1, category=None, extension=None))
+ if constraint == "file":
+ return Path(realpath(__file__))
+ return Path(realpath(__file__)).parent
diff --git a/venv/lib/python3.11/site-packages/polyfactory/value_generators/constrained_strings.py b/venv/lib/python3.11/site-packages/polyfactory/value_generators/constrained_strings.py
new file mode 100644
index 0000000..c7da72b
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/polyfactory/value_generators/constrained_strings.py
@@ -0,0 +1,138 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Callable, Pattern, TypeVar, Union, cast
+
+from polyfactory.exceptions import ParameterException
+from polyfactory.value_generators.primitives import create_random_bytes, create_random_string
+from polyfactory.value_generators.regex import RegexFactory
+
+T = TypeVar("T", bound=Union[bytes, str])
+
+if TYPE_CHECKING:
+ from random import Random
+
+
+def _validate_length(
+ min_length: int | None = None,
+ max_length: int | None = None,
+) -> None:
+ """Validate the length parameters make sense.
+
+ :param min_length: Minimum length.
+ :param max_length: Maximum length.
+
+ :raises: ParameterException.
+
+ :returns: None.
+ """
+ if min_length is not None and min_length < 0:
+ msg = "min_length must be greater or equal to 0"
+ raise ParameterException(msg)
+
+ if max_length is not None and max_length < 0:
+ msg = "max_length must be greater or equal to 0"
+ raise ParameterException(msg)
+
+ if max_length is not None and min_length is not None and max_length < min_length:
+ msg = "max_length must be greater than min_length"
+ raise ParameterException(msg)
+
+
+def _generate_pattern(
+ random: Random,
+ pattern: str | Pattern,
+ lower_case: bool = False,
+ upper_case: bool = False,
+ min_length: int | None = None,
+ max_length: int | None = None,
+) -> str:
+ """Generate a regex.
+
+ :param random: An instance of random.
+ :param pattern: A regex or string pattern.
+ :param lower_case: Whether to lowercase the result.
+ :param upper_case: Whether to uppercase the result.
+ :param min_length: A minimum length.
+ :param max_length: A maximum length.
+
+ :returns: A string matching the given pattern.
+ """
+ regex_factory = RegexFactory(random=random)
+ result = regex_factory(pattern)
+ if min_length:
+ while len(result) < min_length:
+ result += regex_factory(pattern)
+
+ if max_length is not None and len(result) > max_length:
+ result = result[:max_length]
+
+ if lower_case:
+ result = result.lower()
+
+ if upper_case:
+ result = result.upper()
+
+ return result
+
+
+def handle_constrained_string_or_bytes(
+ random: Random,
+ t_type: Callable[[], T],
+ lower_case: bool = False,
+ upper_case: bool = False,
+ min_length: int | None = None,
+ max_length: int | None = None,
+ pattern: str | Pattern | None = None,
+) -> T:
+ """Handle constrained string or bytes, for example - pydantic `constr` or `conbytes`.
+
+ :param random: An instance of random.
+ :param t_type: A type (str or bytes)
+ :param lower_case: Whether to lowercase the result.
+ :param upper_case: Whether to uppercase the result.
+ :param min_length: A minimum length.
+ :param max_length: A maximum length.
+ :param pattern: A regex or string pattern.
+
+ :returns: A value of type T.
+ """
+ _validate_length(min_length=min_length, max_length=max_length)
+
+ if max_length == 0:
+ return t_type()
+
+ if pattern:
+ return cast(
+ "T",
+ _generate_pattern(
+ random=random,
+ pattern=pattern,
+ lower_case=lower_case,
+ upper_case=upper_case,
+ min_length=min_length,
+ max_length=max_length,
+ ),
+ )
+
+ if t_type is str:
+ return cast(
+ "T",
+ create_random_string(
+ min_length=min_length,
+ max_length=max_length,
+ lower_case=lower_case,
+ upper_case=upper_case,
+ random=random,
+ ),
+ )
+
+ return cast(
+ "T",
+ create_random_bytes(
+ min_length=min_length,
+ max_length=max_length,
+ lower_case=lower_case,
+ upper_case=upper_case,
+ random=random,
+ ),
+ )
diff --git a/venv/lib/python3.11/site-packages/polyfactory/value_generators/constrained_url.py b/venv/lib/python3.11/site-packages/polyfactory/value_generators/constrained_url.py
new file mode 100644
index 0000000..d29555e
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/polyfactory/value_generators/constrained_url.py
@@ -0,0 +1,10 @@
+from polyfactory.field_meta import UrlConstraints
+
+
+def handle_constrained_url(constraints: UrlConstraints) -> str:
+ schema = (constraints.get("allowed_schemes") or ["http", "https"])[0]
+ default_host = constraints.get("default_host") or "localhost"
+ default_port = constraints.get("default_port") or 80
+ default_path = constraints.get("default_path") or ""
+
+ return f"{schema}://{default_host}:{default_port}{default_path}"
diff --git a/venv/lib/python3.11/site-packages/polyfactory/value_generators/constrained_uuid.py b/venv/lib/python3.11/site-packages/polyfactory/value_generators/constrained_uuid.py
new file mode 100644
index 0000000..053f047
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/polyfactory/value_generators/constrained_uuid.py
@@ -0,0 +1,31 @@
+from typing import Literal, cast
+from uuid import NAMESPACE_DNS, UUID, uuid1, uuid3, uuid5
+
+from faker import Faker
+
+UUID_VERSION_1 = 1
+UUID_VERSION_3 = 3
+UUID_VERSION_4 = 4
+UUID_VERSION_5 = 5
+
+
+def handle_constrained_uuid(uuid_version: Literal[1, 3, 4, 5], faker: Faker) -> UUID:
+ """Generate a UUID based on the version specified.
+
+ Args:
+ uuid_version: The version of the UUID to generate.
+ faker: The Faker instance to use.
+
+ Returns:
+ The generated UUID.
+ """
+ if uuid_version == UUID_VERSION_1:
+ return uuid1()
+ if uuid_version == UUID_VERSION_3:
+ return uuid3(NAMESPACE_DNS, faker.pystr())
+ if uuid_version == UUID_VERSION_4:
+ return cast("UUID", faker.uuid4())
+ if uuid_version == UUID_VERSION_5:
+ return uuid5(NAMESPACE_DNS, faker.pystr())
+ msg = f"Unknown UUID version: {uuid_version}"
+ raise ValueError(msg)
diff --git a/venv/lib/python3.11/site-packages/polyfactory/value_generators/primitives.py b/venv/lib/python3.11/site-packages/polyfactory/value_generators/primitives.py
new file mode 100644
index 0000000..2cf6b41
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/polyfactory/value_generators/primitives.py
@@ -0,0 +1,129 @@
+from __future__ import annotations
+
+from binascii import hexlify
+from decimal import Decimal
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+ from random import Random
+
+
+def create_random_float(
+ random: Random,
+ minimum: Decimal | float | None = None,
+ maximum: Decimal | float | None = None,
+) -> float:
+ """Generate a random float given the constraints.
+
+ :param random: An instance of random.
+ :param minimum: A minimum value
+ :param maximum: A maximum value.
+
+ :returns: A random float.
+ """
+ if minimum is None:
+ minimum = float(random.randint(0, 100)) if maximum is None else float(maximum) - 100.0
+ if maximum is None:
+ maximum = float(minimum) + 1.0 * 2.0 if minimum >= 0 else float(minimum) + 1.0 / 2.0
+ return random.uniform(float(minimum), float(maximum))
+
+
+def create_random_integer(random: Random, minimum: int | None = None, maximum: int | None = None) -> int:
+ """Generate a random int given the constraints.
+
+ :param random: An instance of random.
+ :param minimum: A minimum value
+ :param maximum: A maximum value.
+
+ :returns: A random integer.
+ """
+ return round(create_random_float(random=random, minimum=minimum, maximum=maximum))
+
+
+def create_random_decimal(
+ random: Random,
+ minimum: Decimal | None = None,
+ maximum: Decimal | None = None,
+) -> Decimal:
+ """Generate a random Decimal given the constraints.
+
+ :param random: An instance of random.
+ :param minimum: A minimum value
+ :param maximum: A maximum value.
+
+ :returns: A random decimal.
+ """
+ return Decimal(str(create_random_float(random=random, minimum=minimum, maximum=maximum)))
+
+
+def create_random_bytes(
+ random: Random,
+ min_length: int | None = None,
+ max_length: int | None = None,
+ lower_case: bool = False,
+ upper_case: bool = False,
+) -> bytes:
+ """Generate a random bytes given the constraints.
+
+ :param random: An instance of random.
+ :param min_length: A minimum length.
+ :param max_length: A maximum length.
+ :param lower_case: Whether to lowercase the result.
+ :param upper_case: Whether to uppercase the result.
+
+ :returns: A random byte-string.
+ """
+ if min_length is None:
+ min_length = 0
+ if max_length is None:
+ max_length = min_length + 1 * 2
+
+ length = random.randint(min_length, max_length)
+ result = b"" if length == 0 else hexlify(random.getrandbits(length * 8).to_bytes(length, "little"))
+
+ if lower_case:
+ result = result.lower()
+ elif upper_case:
+ result = result.upper()
+
+ if max_length and len(result) > max_length:
+ end = random.randint(min_length or 0, max_length)
+ return result[:end]
+
+ return result
+
+
+def create_random_string(
+ random: Random,
+ min_length: int | None = None,
+ max_length: int | None = None,
+ lower_case: bool = False,
+ upper_case: bool = False,
+) -> str:
+ """Generate a random string given the constraints.
+
+ :param random: An instance of random.
+ :param min_length: A minimum length.
+ :param max_length: A maximum length.
+ :param lower_case: Whether to lowercase the result.
+ :param upper_case: Whether to uppercase the result.
+
+ :returns: A random string.
+ """
+ return create_random_bytes(
+ random=random,
+ min_length=min_length,
+ max_length=max_length,
+ lower_case=lower_case,
+ upper_case=upper_case,
+ ).decode("utf-8")
+
+
+def create_random_boolean(random: Random) -> bool:
+ """Generate a random boolean value.
+
+ :param random: An instance of random.
+
+ :returns: A random boolean.
+ """
+ return bool(random.getrandbits(1))
diff --git a/venv/lib/python3.11/site-packages/polyfactory/value_generators/regex.py b/venv/lib/python3.11/site-packages/polyfactory/value_generators/regex.py
new file mode 100644
index 0000000..eab9bd0
--- /dev/null
+++ b/venv/lib/python3.11/site-packages/polyfactory/value_generators/regex.py
@@ -0,0 +1,150 @@
+"""The code in this files is adapted from https://github.com/crdoconnor/xeger/blob/master/xeger/xeger.py.Which in turn
+adapted it from https://bitbucket.org/leapfrogdevelopment/rstr/.
+
+Copyright (C) 2015, Colm O'Connor
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the Leapfrog Direct Response, LLC, including
+ its subsidiaries and affiliates nor the names of its
+ contributors, may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL LEAPFROG DIRECT
+RESPONSE, LLC, INCLUDING ITS SUBSIDIARIES AND AFFILIATES, BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"""
+from __future__ import annotations
+
+from itertools import chain
+from string import (
+ ascii_letters,
+ ascii_lowercase,
+ ascii_uppercase,
+ digits,
+ printable,
+ punctuation,
+ whitespace,
+)
+from typing import TYPE_CHECKING, Any, Pattern
+
+try: # >=3.11
+ from re._parser import SubPattern, parse
+except ImportError: # < 3.11
+ from sre_parse import SubPattern, parse # pylint: disable=deprecated-module
+
+if TYPE_CHECKING:
+ from random import Random
+
+_alphabets = {
+ "printable": printable,
+ "letters": ascii_letters,
+ "uppercase": ascii_uppercase,
+ "lowercase": ascii_lowercase,
+ "digits": digits,
+ "punctuation": punctuation,
+ "nondigits": ascii_letters + punctuation,
+ "nonletters": digits + punctuation,
+ "whitespace": whitespace,
+ "nonwhitespace": printable.strip(),
+ "normal": ascii_letters + digits + " ",
+ "word": ascii_letters + digits + "_",
+ "nonword": "".join(set(printable).difference(ascii_letters + digits + "_")),
+ "postalsafe": ascii_letters + digits + " .-#/",
+ "urlsafe": ascii_letters + digits + "-._~",
+ "domainsafe": ascii_letters + digits + "-",
+}
+
+_categories = {
+ "category_digit": _alphabets["digits"],
+ "category_not_digit": _alphabets["nondigits"],
+ "category_space": _alphabets["whitespace"],
+ "category_not_space": _alphabets["nonwhitespace"],
+ "category_word": _alphabets["word"],
+ "category_not_word": _alphabets["nonword"],
+}
+
+
+class RegexFactory:
+ """Factory for regexes."""
+
+ def __init__(self, random: Random, limit: int = 10) -> None:
+ """Create a RegexFactory"""
+ self._limit = limit
+ self._cache: dict[str, Any] = {}
+ self._random = random
+
+ self._cases = {
+ "literal": chr,
+ "not_literal": lambda x: self._random.choice(printable.replace(chr(x), "")),
+ "at": lambda x: "",
+ "in": self._handle_in,
+ "any": lambda x: self._random.choice(printable.replace("\n", "")),
+ "range": lambda x: [chr(i) for i in range(x[0], x[1] + 1)],
+ "category": lambda x: _categories[str(x).lower()],
+ "branch": lambda x: "".join(self._handle_state(i) for i in self._random.choice(x[1])),
+ "subpattern": self._handle_group,
+ "assert": lambda x: "".join(self._handle_state(i) for i in x[1]),
+ "assert_not": lambda x: "",
+ "groupref": lambda x: self._cache[x],
+ "min_repeat": lambda x: self._handle_repeat(*x),
+ "max_repeat": lambda x: self._handle_repeat(*x),
+ "negate": lambda x: [False],
+ }
+
+ def __call__(self, string_or_regex: str | Pattern) -> str:
+ """Generate a string matching a regex.
+
+ :param string_or_regex: A string or pattern.
+
+ :return: The generated string.
+ """
+ pattern = string_or_regex.pattern if isinstance(string_or_regex, Pattern) else string_or_regex
+ parsed = parse(pattern)
+ result = self._build_string(parsed) # pyright: ignore[reportGeneralTypeIssues]
+ self._cache.clear()
+ return result
+
+ def _build_string(self, parsed: SubPattern) -> str:
+ return "".join([self._handle_state(state) for state in parsed]) # pyright:ignore[reportGeneralTypeIssues]
+
+ def _handle_state(self, state: tuple[SubPattern, tuple[Any, ...]]) -> Any:
+ opcode, value = state
+ return self._cases[str(opcode).lower()](value) # type: ignore[no-untyped-call]
+
+ def _handle_group(self, value: tuple[Any, ...]) -> str:
+ result = "".join(self._handle_state(i) for i in value[3])
+ if value[0]:
+ self._cache[value[0]] = result
+ return result
+
+ def _handle_in(self, value: tuple[Any, ...]) -> Any:
+ candidates = list(chain(*(self._handle_state(i) for i in value)))
+ if candidates and candidates[0] is False:
+ candidates = list(set(printable).difference(candidates[1:]))
+ return self._random.choice(candidates)
+ return self._random.choice(candidates)
+
+ def _handle_repeat(self, start_range: int, end_range: Any, value: SubPattern) -> str:
+ end_range = min(end_range, self._limit)
+
+ result = [
+ "".join(self._handle_state(v) for v in list(value)) # pyright: ignore[reportGeneralTypeIssues]
+ for _ in range(self._random.randint(start_range, max(start_range, end_range)))
+ ]
+ return "".join(result)