Repository: HypothesisWorks/hypothesis
Pull request #4332: Rename ChoiceNode.kwargs to ChoiceNode.constraints
Author: login: jobh
Created: 2025-04-01T14:13:22Z | Merged: 2025-04-02T05:17:02Z

Ref #4166, renames `kwargs` to `constraints`


Diff:
diff --git a/hypothesis-python/RELEASE.rst b/hypothesis-python/RELEASE.rst
new file mode 100644
index 0000000000..98bb9312de
--- /dev/null
+++ b/hypothesis-python/RELEASE.rst
@@ -0,0 +1,3 @@
+RELEASE_TYPE: patch
+
+Rename internal variables for clarity.
diff --git a/hypothesis-python/src/hypothesis/internal/conjecture/choice.py b/hypothesis-python/src/hypothesis/internal/conjecture/choice.py
index fe52e1e5fc..ca12c009b9 100644
--- a/hypothesis-python/src/hypothesis/internal/conjecture/choice.py
+++ b/hypothesis-python/src/hypothesis/internal/conjecture/choice.py
@@ -35,38 +35,42 @@
     from typing import TypeAlias
 
 
-class IntegerKWargs(TypedDict):
+class IntegerConstraints(TypedDict):
     min_value: Optional[int]
     max_value: Optional[int]
     weights: Optional[dict[int, float]]
     shrink_towards: int
 
 
-class FloatKWargs(TypedDict):
+class FloatConstraints(TypedDict):
     min_value: float
     max_value: float
     allow_nan: bool
     smallest_nonzero_magnitude: float
 
 
-class StringKWargs(TypedDict):
+class StringConstraints(TypedDict):
     intervals: IntervalSet
     min_size: int
     max_size: int
 
 
-class BytesKWargs(TypedDict):
+class BytesConstraints(TypedDict):
     min_size: int
     max_size: int
 
 
-class BooleanKWargs(TypedDict):
+class BooleanConstraints(TypedDict):
     p: float
 
 
 ChoiceT: "TypeAlias" = Union[int, str, bool, float, bytes]
-ChoiceKwargsT: "TypeAlias" = Union[
-    IntegerKWargs, FloatKWargs, StringKWargs, BytesKWargs, BooleanKWargs
+ChoiceConstraintsT: "TypeAlias" = Union[
+    IntegerConstraints,
+    FloatConstraints,
+    StringConstraints,
+    BytesConstraints,
+    BooleanConstraints,
 ]
 ChoiceTypeT: "TypeAlias" = Literal["integer", "string", "boolean", "float", "bytes"]
 ChoiceKeyT: "TypeAlias" = Union[
@@ -88,7 +92,7 @@ def __attrs_post_init__(self) -> None:
 class ChoiceNode:
     type: ChoiceTypeT = attr.ib()
     value: ChoiceT = attr.ib()
-    kwargs: ChoiceKwargsT = attr.ib()
+    constraints: ChoiceConstraintsT = attr.ib()
     was_forced: bool = attr.ib()
     index: Optional[int] = attr.ib(default=None)
 
@@ -96,7 +100,7 @@ def copy(
         self,
         *,
         with_value: Optional[ChoiceT] = None,
-        with_kwargs: Optional[ChoiceKwargsT] = None,
+        with_constraints: Optional[ChoiceConstraintsT] = None,
     ) -> "ChoiceNode":
         # we may want to allow this combination in the future, but for now it's
         # a footgun.
@@ -108,7 +112,9 @@ def copy(
         return ChoiceNode(
             type=self.type,
             value=self.value if with_value is None else with_value,
-            kwargs=self.kwargs if with_kwargs is None else with_kwargs,
+            constraints=(
+                self.constraints if with_constraints is None else with_constraints
+            ),
             was_forced=self.was_forced,
         )
 
@@ -124,12 +130,12 @@ def trivial(self) -> bool:
             return True
 
         if self.type != "float":
-            zero_value = choice_from_index(0, self.type, self.kwargs)
+            zero_value = choice_from_index(0, self.type, self.constraints)
             return choice_equal(self.value, zero_value)
         else:
-            kwargs = cast(FloatKWargs, self.kwargs)
-            min_value = kwargs["min_value"]
-            max_value = kwargs["max_value"]
+            constraints = cast(FloatConstraints, self.constraints)
+            min_value = constraints["min_value"]
+            max_value = constraints["max_value"]
             shrink_towards = 0.0
 
             if min_value == -math.inf and max_value == math.inf:
@@ -159,7 +165,7 @@ def __eq__(self, other: object) -> bool:
         return (
             self.type == other.type
             and choice_equal(self.value, other.value)
-            and choice_kwargs_equal(self.type, self.kwargs, other.kwargs)
+            and choice_constraints_equal(self.type, self.constraints, other.constraints)
             and self.was_forced == other.was_forced
         )
 
@@ -168,14 +174,14 @@ def __hash__(self) -> int:
             (
                 self.type,
                 choice_key(self.value),
-                choice_kwargs_key(self.type, self.kwargs),
+                choice_constraints_key(self.type, self.constraints),
                 self.was_forced,
             )
         )
 
     def __repr__(self) -> str:
         forced_marker = " [forced]" if self.was_forced else ""
-        return f"{self.type} {self.value!r}{forced_marker} {self.kwargs!r}"
+        return f"{self.type} {self.value!r}{forced_marker} {self.constraints!r}"
 
 
 def _size_to_index(size: int, *, alphabet_size: int) -> int:
@@ -305,12 +311,12 @@ def zigzag_value(index: int, *, shrink_towards: int) -> int:
     return shrink_towards + n
 
 
-def choice_to_index(choice: ChoiceT, kwargs: ChoiceKwargsT) -> int:
+def choice_to_index(choice: ChoiceT, constraints: ChoiceConstraintsT) -> int:
     # This function takes a choice in the choice sequence and returns the
     # complexity index of that choice from among its possible values, where 0
     # is the simplest.
     #
-    # Note that the index of a choice depends on its kwargs. The simplest value
+    # Note that the index of a choice depends on its constraints. The simplest value
     # (at index 0) for {"min_value": None, "max_value": None} is 0, while for
     # {"min_value": 1, "max_value": None} the simplest value is 1.
     #
@@ -333,10 +339,10 @@ def choice_to_index(choice: ChoiceT, kwargs: ChoiceKwargsT) -> int:
         # the most common case where 0 is first (a = 0). We deviate from this only
         # rarely, e.g. for datetimes, where we generally want year 2000 to be
         # simpler than year 0.
-        kwargs = cast(IntegerKWargs, kwargs)
-        shrink_towards = kwargs["shrink_towards"]
-        min_value = kwargs["min_value"]
-        max_value = kwargs["max_value"]
+        constraints = cast(IntegerConstraints, constraints)
+        shrink_towards = constraints["shrink_towards"]
+        min_value = constraints["min_value"]
+        max_value = constraints["max_value"]
 
         if min_value is not None:
             shrink_towards = max(min_value, shrink_towards)
@@ -374,8 +380,8 @@ def choice_to_index(choice: ChoiceT, kwargs: ChoiceKwargsT) -> int:
 
             assert min_value is not None
             assert max_value is not None
-            assert kwargs["weights"] is None or all(
-                w > 0 for w in kwargs["weights"].values()
+            assert constraints["weights"] is None or all(
+                w > 0 for w in constraints["weights"].values()
             ), "technically possible but really annoying to support zero weights"
 
             # check which side gets exhausted first
@@ -392,27 +398,27 @@ def choice_to_index(choice: ChoiceT, kwargs: ChoiceKwargsT) -> int:
                     return zigzag_index(choice, shrink_towards=shrink_towards)
                 return max_value - choice
     elif isinstance(choice, bool):
-        kwargs = cast(BooleanKWargs, kwargs)
+        constraints = cast(BooleanConstraints, constraints)
         # Ordered by [False, True].
-        p = kwargs["p"]
+        p = constraints["p"]
         if not (2 ** (-64) < p < (1 - 2 ** (-64))):
             # only one option is possible, so whatever it is is first.
             return 0
         return int(choice)
     elif isinstance(choice, bytes):
-        kwargs = cast(BytesKWargs, kwargs)
+        constraints = cast(BytesConstraints, constraints)
         return collection_index(
             list(choice),
-            min_size=kwargs["min_size"],
+            min_size=constraints["min_size"],
             alphabet_size=2**8,
             to_order=identity,
         )
     elif isinstance(choice, str):
-        kwargs = cast(StringKWargs, kwargs)
-        intervals = kwargs["intervals"]
+        constraints = cast(StringConstraints, constraints)
+        intervals = constraints["intervals"]
         return collection_index(
             choice,
-            min_size=kwargs["min_size"],
+            min_size=constraints["min_size"],
             alphabet_size=len(intervals),
             to_order=intervals.index_from_char_in_shrink_order,
         )
@@ -424,14 +430,14 @@ def choice_to_index(choice: ChoiceT, kwargs: ChoiceKwargsT) -> int:
 
 
 def choice_from_index(
-    index: int, choice_type: ChoiceTypeT, kwargs: ChoiceKwargsT
+    index: int, choice_type: ChoiceTypeT, constraints: ChoiceConstraintsT
 ) -> ChoiceT:
     assert index >= 0
     if choice_type == "integer":
-        kwargs = cast(IntegerKWargs, kwargs)
-        shrink_towards = kwargs["shrink_towards"]
-        min_value = kwargs["min_value"]
-        max_value = kwargs["max_value"]
+        constraints = cast(IntegerConstraints, constraints)
+        shrink_towards = constraints["shrink_towards"]
+        min_value = constraints["min_value"]
+        max_value = constraints["max_value"]
 
         if min_value is not None:
             shrink_towards = max(min_value, shrink_towards)
@@ -455,8 +461,8 @@ def choice_from_index(
             # case: bounded
             assert min_value is not None
             assert max_value is not None
-            assert kwargs["weights"] is None or all(
-                w > 0 for w in kwargs["weights"].values()
+            assert constraints["weights"] is None or all(
+                w > 0 for w in constraints["weights"].values()
             ), "possible but really annoying to support zero weights"
 
             if (shrink_towards - min_value) < (max_value - shrink_towards):
@@ -470,9 +476,9 @@ def choice_from_index(
                     return zigzag_value(index, shrink_towards=shrink_towards)
                 return max_value - index
     elif choice_type == "boolean":
-        kwargs = cast(BooleanKWargs, kwargs)
+        constraints = cast(BooleanConstraints, constraints)
         # Ordered by [False, True].
-        p = kwargs["p"]
+        p = constraints["p"]
         only = None
         if p <= 2 ** (-64):
             only = False
@@ -486,72 +492,78 @@ def choice_from_index(
             return only
         return bool(index)
     elif choice_type == "bytes":
-        kwargs = cast(BytesKWargs, kwargs)
+        constraints = cast(BytesConstraints, constraints)
         value_b = collection_value(
-            index, min_size=kwargs["min_size"], alphabet_size=2**8, from_order=identity
+            index,
+            min_size=constraints["min_size"],
+            alphabet_size=2**8,
+            from_order=identity,
         )
         return bytes(value_b)
     elif choice_type == "string":
-        kwargs = cast(StringKWargs, kwargs)
-        intervals = kwargs["intervals"]
+        constraints = cast(StringConstraints, constraints)
+        intervals = constraints["intervals"]
         # _s because mypy is unhappy with reusing different-typed names in branches,
         # even if the branches are disjoint.
         value_s = collection_value(
             index,
-            min_size=kwargs["min_size"],
+            min_size=constraints["min_size"],
             alphabet_size=len(intervals),
             from_order=intervals.char_in_shrink_order,
         )
         return "".join(value_s)
     elif choice_type == "float":
-        kwargs = cast(FloatKWargs, kwargs)
+        constraints = cast(FloatConstraints, constraints)
         sign = -1 if index >> 64 else 1
         result = sign * lex_to_float(index & ((1 << 64) - 1))
 
         clamper = make_float_clamper(
-            min_value=kwargs["min_value"],
-            max_value=kwargs["max_value"],
-            smallest_nonzero_magnitude=kwargs["smallest_nonzero_magnitude"],
-            allow_nan=kwargs["allow_nan"],
+            min_value=constraints["min_value"],
+            max_value=constraints["max_value"],
+            smallest_nonzero_magnitude=constraints["smallest_nonzero_magnitude"],
+            allow_nan=constraints["allow_nan"],
         )
         return clamper(result)
     else:
         raise NotImplementedError
 
 
-def choice_permitted(choice: ChoiceT, kwargs: ChoiceKwargsT) -> bool:
+def choice_permitted(choice: ChoiceT, constraints: ChoiceConstraintsT) -> bool:
     if isinstance(choice, int) and not isinstance(choice, bool):
-        kwargs = cast(IntegerKWargs, kwargs)
-        min_value = kwargs["min_value"]
-        max_value = kwargs["max_value"]
+        constraints = cast(IntegerConstraints, constraints)
+        min_value = constraints["min_value"]
+        max_value = constraints["max_value"]
         if min_value is not None and choice < min_value:
             return False
         return not (max_value is not None and choice > max_value)
     elif isinstance(choice, float):
-        kwargs = cast(FloatKWargs, kwargs)
+        constraints = cast(FloatConstraints, constraints)
         if math.isnan(choice):
-            return kwargs["allow_nan"]
+            return constraints["allow_nan"]
         return (
-            sign_aware_lte(kwargs["min_value"], choice)
-            and sign_aware_lte(choice, kwargs["max_value"])
-        ) and not (0 < abs(choice) < kwargs["smallest_nonzero_magnitude"])
+            sign_aware_lte(constraints["min_value"], choice)
+            and sign_aware_lte(choice, constraints["max_value"])
+        ) and not (0 < abs(choice) < constraints["smallest_nonzero_magnitude"])
     elif isinstance(choice, str):
-        kwargs = cast(StringKWargs, kwargs)
-        if len(choice) < kwargs["min_size"]:
+        constraints = cast(StringConstraints, constraints)
+        if len(choice) < constraints["min_size"]:
             return False
-        if kwargs["max_size"] is not None and len(choice) > kwargs["max_size"]:
+        if (
+            constraints["max_size"] is not None
+            and len(choice) > constraints["max_size"]
+        ):
             return False
-        return all(ord(c) in kwargs["intervals"] for c in choice)
+        return all(ord(c) in constraints["intervals"] for c in choice)
     elif isinstance(choice, bytes):
-        kwargs = cast(BytesKWargs, kwargs)
-        if len(choice) < kwargs["min_size"]:
+        constraints = cast(BytesConstraints, constraints)
+        if len(choice) < constraints["min_size"]:
             return False
-        return kwargs["max_size"] is None or len(choice) <= kwargs["max_size"]
+        return constraints["max_size"] is None or len(choice) <= constraints["max_size"]
     elif isinstance(choice, bool):
-        kwargs = cast(BooleanKWargs, kwargs)
-        if kwargs["p"] <= 0:
+        constraints = cast(BooleanConstraints, constraints)
+        if constraints["p"] <= 0:
             return choice is False
-        if kwargs["p"] >= 1:
+        if constraints["p"] >= 1:
             return choice is True
         return True
     else:
@@ -578,30 +590,32 @@ def choice_equal(choice1: ChoiceT, choice2: ChoiceT) -> bool:
     return choice_key(choice1) == choice_key(choice2)
 
 
-def choice_kwargs_equal(
-    choice_type: ChoiceTypeT, kwargs1: ChoiceKwargsT, kwargs2: ChoiceKwargsT
+def choice_constraints_equal(
+    choice_type: ChoiceTypeT,
+    constraints1: ChoiceConstraintsT,
+    constraints2: ChoiceConstraintsT,
 ) -> bool:
-    return choice_kwargs_key(choice_type, kwargs1) == choice_kwargs_key(
-        choice_type, kwargs2
+    return choice_constraints_key(choice_type, constraints1) == choice_constraints_key(
+        choice_type, constraints2
     )
 
 
-def choice_kwargs_key(choice_type, kwargs):
+def choice_constraints_key(choice_type, constraints):
     if choice_type == "float":
         return (
-            float_to_int(kwargs["min_value"]),
-            float_to_int(kwargs["max_value"]),
-            kwargs["allow_nan"],
-            kwargs["smallest_nonzero_magnitude"],
+            float_to_int(constraints["min_value"]),
+            float_to_int(constraints["max_value"]),
+            constraints["allow_nan"],
+            constraints["smallest_nonzero_magnitude"],
         )
     if choice_type == "integer":
         return (
-            kwargs["min_value"],
-            kwargs["max_value"],
-            None if kwargs["weights"] is None else tuple(kwargs["weights"]),
-            kwargs["shrink_towards"],
+            constraints["min_value"],
+            constraints["max_value"],
+            None if constraints["weights"] is None else tuple(constraints["weights"]),
+            constraints["shrink_towards"],
         )
-    return tuple(kwargs[key] for key in sorted(kwargs))
+    return tuple(constraints[key] for key in sorted(constraints))
 
 
 def choices_size(choices: Iterable[ChoiceT]) -> int:
diff --git a/hypothesis-python/src/hypothesis/internal/conjecture/data.py b/hypothesis-python/src/hypothesis/internal/conjecture/data.py
index 00eef64e99..2f87e84f70 100644
--- a/hypothesis-python/src/hypothesis/internal/conjecture/data.py
+++ b/hypothesis-python/src/hypothesis/internal/conjecture/data.py
@@ -29,18 +29,18 @@
 from hypothesis.internal.cache import LRUCache
 from hypothesis.internal.compat import add_note
 from hypothesis.internal.conjecture.choice import (
-    BooleanKWargs,
-    BytesKWargs,
-    ChoiceKwargsT,
+    BooleanConstraints,
+    BytesConstraints,
+    ChoiceConstraintsT,
     ChoiceNode,
     ChoiceT,
     ChoiceTemplate,
     ChoiceTypeT,
-    FloatKWargs,
-    IntegerKWargs,
-    StringKWargs,
+    FloatConstraints,
+    IntegerConstraints,
+    StringConstraints,
+    choice_constraints_key,
     choice_from_index,
-    choice_kwargs_key,
     choice_permitted,
     choices_size,
 )
@@ -89,8 +89,10 @@ def __getattr__(name: str) -> Any:
 
 T = TypeVar("T")
 TargetObservations = dict[str, Union[int, float]]
-# index, choice_type, kwargs, forced value
-MisalignedAt: "TypeAlias" = tuple[int, ChoiceTypeT, ChoiceKwargsT, Optional[ChoiceT]]
+# index, choice_type, constraints, forced value
+MisalignedAt: "TypeAlias" = tuple[
+    int, ChoiceTypeT, ChoiceConstraintsT, Optional[ChoiceT]
+]
 
 TOP_LABEL = calc_label_from_name("top")
 
@@ -135,7 +137,7 @@ def structural_coverage(label: int) -> StructuralCoverageTag:
 
 # This cache can be quite hot and so we prefer LRUCache over LRUReusedCache for
 # performance. We lose scan resistance, but that's probably fine here.
-POOLED_KWARGS_CACHE = LRUCache(4096)
+POOLED_CONSTRAINTS_CACHE = LRUCache(4096)
 
 
 class Span:
@@ -544,27 +546,27 @@ def kill_branch(self) -> None:
         """Mark this part of the tree as not worth re-exploring."""
 
     def draw_integer(
-        self, value: int, *, kwargs: IntegerKWargs, was_forced: bool
+        self, value: int, *, constraints: IntegerConstraints, was_forced: bool
     ) -> None:
         pass
 
     def draw_float(
-        self, value: float, *, kwargs: FloatKWargs, was_forced: bool
+        self, value: float, *, constraints: FloatConstraints, was_forced: bool
     ) -> None:
         pass
 
     def draw_string(
-        self, value: str, *, kwargs: StringKWargs, was_forced: bool
+        self, value: str, *, constraints: StringConstraints, was_forced: bool
     ) -> None:
         pass
 
     def draw_bytes(
-        self, value: bytes, *, kwargs: BytesKWargs, was_forced: bool
+        self, value: bytes, *, constraints: BytesConstraints, was_forced: bool
     ) -> None:
         pass
 
     def draw_boolean(
-        self, value: bool, *, kwargs: BooleanKWargs, was_forced: bool
+        self, value: bool, *, constraints: BooleanConstraints, was_forced: bool
     ) -> None:
         pass
 
@@ -735,7 +737,7 @@ def choices(self) -> tuple[ChoiceT, ...]:
     #
     # `observe` formalizes this. The choice will only be written to the choice
     # sequence if observe is True.
-    def _draw(self, choice_type, kwargs, *, observe, forced):
+    def _draw(self, choice_type, constraints, *, observe, forced):
         # this is somewhat redundant with the length > max_length check at the
         # end of the function, but avoids trying to use a null self.random when
         # drawing past the node of a ConjectureData.for_choices data.
@@ -747,9 +749,9 @@ def _draw(self, choice_type, kwargs, *, observe, forced):
             self.mark_overrun()
 
         if observe and self.prefix is not None and self.index < len(self.prefix):
-            value = self._pop_choice(choice_type, kwargs, forced=forced)
+            value = self._pop_choice(choice_type, constraints, forced=forced)
         elif forced is None:
-            value = getattr(self.provider, f"draw_{choice_type}")(**kwargs)
+            value = getattr(self.provider, f"draw_{choice_type}")(**constraints)
 
         if forced is not None:
             value = forced
@@ -780,7 +782,7 @@ def _draw(self, choice_type, kwargs, *, observe, forced):
         if observe:
             was_forced = forced is not None
             getattr(self.observer, f"draw_{choice_type}")(
-                value, kwargs=kwargs, was_forced=was_forced
+                value, constraints=constraints, was_forced=was_forced
             )
             size = 0 if self.provider.avoid_realization else choices_size([value])
             if self.length + size > self.max_length:
@@ -792,7 +794,7 @@ def _draw(self, choice_type, kwargs, *, observe, forced):
             node = ChoiceNode(
                 type=choice_type,
                 value=value,
-                kwargs=kwargs,
+                constraints=constraints,
                 was_forced=was_forced,
                 index=len(self.nodes),
             )
@@ -830,7 +832,7 @@ def draw_integer(
         if forced is not None and max_value is not None:
             assert forced <= max_value
 
-        kwargs: IntegerKWargs = self._pooled_kwargs(
+        constraints: IntegerConstraints = self._pooled_constraints(
             "integer",
             {
                 "min_value": min_value,
@@ -839,7 +841,7 @@ def draw_integer(
                 "shrink_towards": shrink_towards,
             },
         )
-        return self._draw("integer", kwargs, observe=observe, forced=forced)
+        return self._draw("integer", constraints, observe=observe, forced=forced)
 
     def draw_float(
         self,
@@ -865,7 +867,7 @@ def draw_float(
                 sign_aware_lte(min_value, forced) and sign_aware_lte(forced, max_value)
             )
 
-        kwargs: FloatKWargs = self._pooled_kwargs(
+        constraints: FloatConstraints = self._pooled_constraints(
             "float",
             {
                 "min_value": min_value,
@@ -874,7 +876,7 @@ def draw_float(
                 "smallest_nonzero_magnitude": smallest_nonzero_magnitude,
             },
         )
-        return self._draw("float", kwargs, observe=observe, forced=forced)
+        return self._draw("float", constraints, observe=observe, forced=forced)
 
     def draw_string(
         self,
@@ -890,7 +892,7 @@ def draw_string(
         if len(intervals) == 0:
             assert min_size == 0
 
-        kwargs: StringKWargs = self._pooled_kwargs(
+        constraints: StringConstraints = self._pooled_constraints(
             "string",
             {
                 "intervals": intervals,
@@ -898,7 +900,7 @@ def draw_string(
                 "max_size": max_size,
             },
         )
-        return self._draw("string", kwargs, observe=observe, forced=forced)
+        return self._draw("string", constraints, observe=observe, forced=forced)
 
     def draw_bytes(
         self,
@@ -911,10 +913,10 @@ def draw_bytes(
         assert forced is None or min_size <= len(forced) <= max_size
         assert min_size >= 0
 
-        kwargs: BytesKWargs = self._pooled_kwargs(
+        constraints: BytesConstraints = self._pooled_constraints(
             "bytes", {"min_size": min_size, "max_size": max_size}
         )
-        return self._draw("bytes", kwargs, observe=observe, forced=forced)
+        return self._draw("bytes", constraints, observe=observe, forced=forced)
 
     def draw_boolean(
         self,
@@ -926,26 +928,26 @@ def draw_boolean(
         assert (forced is not True) or p > 0
         assert (forced is not False) or p < 1
 
-        kwargs: BooleanKWargs = self._pooled_kwargs("boolean", {"p": p})
-        return self._draw("boolean", kwargs, observe=observe, forced=forced)
+        constraints: BooleanConstraints = self._pooled_constraints("boolean", {"p": p})
+        return self._draw("boolean", constraints, observe=observe, forced=forced)
 
-    def _pooled_kwargs(self, choice_type, kwargs):
+    def _pooled_constraints(self, choice_type, constraints):
         """Memoize common dictionary objects to reduce memory pressure."""
         # caching runs afoul of nondeterminism checks
         if self.provider.avoid_realization:
-            return kwargs
+            return constraints
 
-        key = (choice_type, *choice_kwargs_key(choice_type, kwargs))
+        key = (choice_type, *choice_constraints_key(choice_type, constraints))
         try:
-            return POOLED_KWARGS_CACHE[key]
+            return POOLED_CONSTRAINTS_CACHE[key]
         except KeyError:
-            POOLED_KWARGS_CACHE[key] = kwargs
-            return kwargs
+            POOLED_CONSTRAINTS_CACHE[key] = constraints
+            return constraints
 
     def _pop_choice(
         self,
         choice_type: ChoiceTypeT,
-        kwargs: ChoiceKwargsT,
+        constraints: ChoiceConstraintsT,
         *,
         forced: Optional[ChoiceT],
     ) -> ChoiceT:
@@ -967,13 +969,15 @@ def _pop_choice(
                     choice = forced
                 elif isinstance(self.provider, HypothesisProvider):
                     try:
-                        choice = choice_from_index(0, choice_type, kwargs)
+                        choice = choice_from_index(0, choice_type, constraints)
                     except ChoiceTooLarge:
                         self.mark_overrun()
                 else:
                     # give alternative backends control over ChoiceTemplate draws
                     # as well
-                    choice = getattr(self.provider, f"draw_{choice_type}")(**kwargs)
+                    choice = getattr(self.provider, f"draw_{choice_type}")(
+                        **constraints
+                    )
             else:
                 raise NotImplementedError
 
@@ -993,7 +997,7 @@ def _pop_choice(
         }[type(choice)]
         # If we're trying to:
         # * draw a different ir type at the same location
-        # * draw the same ir type with a different kwargs, which does not permit
+        # * draw the same ir type with a different constraints, which does not permit
         #   the current value
         #
         # then we call this a misalignment, because the choice sequence has
@@ -1001,16 +1005,16 @@ def _pop_choice(
         #
         #   one_of(integers(0, 100), integers(101, 200))
         #
-        # where the choice sequence [0, 100] has kwargs {min_value: 0, max_value: 100}
-        # at index 1, but [0, 101] has kwargs {min_value: 101, max_value: 200} at
+        # where the choice sequence [0, 100] has constraints {min_value: 0, max_value: 100}
+        # at index 1, but [0, 101] has constraints {min_value: 101, max_value: 200} at
         # index 1 (which does not permit any of the values 0-100).
         #
         # When the choice sequence becomes misaligned, we generate a new value of the
-        # type and kwargs the strategy expects.
-        if node_choice_type != choice_type or not choice_permitted(choice, kwargs):
+        # type and constraints the strategy expects.
+        if node_choice_type != choice_type or not choice_permitted(choice, constraints):
             # only track first misalignment for now.
             if self.misaligned_at is None:
-                self.misaligned_at = (self.index, choice_type, kwargs, forced)
+                self.misaligned_at = (self.index, choice_type, constraints, forced)
             try:
                 # Fill in any misalignments with index 0 choices. An alternative to
                 # this is using the index of the misaligned choice instead
@@ -1028,7 +1032,7 @@ def _pop_choice(
                 # slipping to high-complexity values are common. Though arguably
                 # we may want to expand a bit beyond *just* the simplest choice.
                 # (we could for example consider sampling choices from index 0-10).
-                choice = choice_from_index(0, choice_type, kwargs)
+                choice = choice_from_index(0, choice_type, constraints)
             except ChoiceTooLarge:
                 # should really never happen with a 0-index choice, but let's be safe.
                 self.mark_overrun()
@@ -1266,6 +1270,6 @@ def mark_overrun(self) -> NoReturn:
         self.conclude_test(Status.OVERRUN)
 
 
-def draw_choice(choice_type, kwargs, *, random):
+def draw_choice(choice_type, constraints, *, random):
     cd = ConjectureData(random=random)
-    return getattr(cd.provider, f"draw_{choice_type}")(**kwargs)
+    return getattr(cd.provider, f"draw_{choice_type}")(**constraints)
diff --git a/hypothesis-python/src/hypothesis/internal/conjecture/datatree.py b/hypothesis-python/src/hypothesis/internal/conjecture/datatree.py
index c461f8cb35..1621d7c3ff 100644
--- a/hypothesis-python/src/hypothesis/internal/conjecture/datatree.py
+++ b/hypothesis-python/src/hypothesis/internal/conjecture/datatree.py
@@ -23,14 +23,14 @@
 )
 from hypothesis.internal import floats as flt
 from hypothesis.internal.conjecture.choice import (
-    BooleanKWargs,
-    BytesKWargs,
-    ChoiceKwargsT,
+    BooleanConstraints,
+    BytesConstraints,
+    ChoiceConstraintsT,
     ChoiceT,
     ChoiceTypeT,
-    FloatKWargs,
-    IntegerKWargs,
-    StringKWargs,
+    FloatConstraints,
+    IntegerConstraints,
+    StringConstraints,
     choice_from_index,
 )
 from hypothesis.internal.conjecture.data import ConjectureData, DataObserver, Status
@@ -81,10 +81,14 @@ def _repr_pretty_(self, p: "RepresentationPrinter", cycle: bool) -> None:
 
 
 def _node_pretty(
-    choice_type: ChoiceTypeT, value: ChoiceT, kwargs: ChoiceKwargsT, *, forced: bool
+    choice_type: ChoiceTypeT,
+    value: ChoiceT,
+    constraints: ChoiceConstraintsT,
+    *,
+    forced: bool,
 ) -> str:
     forced_marker = " [forced]" if forced else ""
-    return f"{choice_type} {value!r}{forced_marker} {kwargs}"
+    return f"{choice_type} {value!r}{forced_marker} {constraints}"
 
 
 @attr.s(slots=True)
@@ -92,13 +96,13 @@ class Branch:
     """Represents a transition where multiple choices can be made as to what
     to drawn."""
 
-    kwargs: ChoiceKwargsT = attr.ib()
+    constraints: ChoiceConstraintsT = attr.ib()
     choice_type: ChoiceTypeT = attr.ib()
     children: dict[ChoiceT, "TreeNode"] = attr.ib(repr=False)
 
     @property
     def max_children(self) -> int:
-        max_children = compute_max_children(self.choice_type, self.kwargs)
+        max_children = compute_max_children(self.choice_type, self.constraints)
         assert max_children > 0
         return max_children
 
@@ -107,7 +111,9 @@ def _repr_pretty_(self, p: "RepresentationPrinter", cycle: bool) -> None:
         for i, (value, child) in enumerate(self.children.items()):
             if i > 0:
                 p.break_()
-            p.text(_node_pretty(self.choice_type, value, self.kwargs, forced=False))
+            p.text(
+                _node_pretty(self.choice_type, value, self.constraints, forced=False)
+            )
             with p.indent(2):
                 p.break_()
                 p.pretty(child)
@@ -179,11 +185,13 @@ def _count_distinct_strings(*, alphabet_size: int, min_size: int, max_size: int)
     return sum(alphabet_size**k for k in range(min_size, max_size + 1))
 
 
-def compute_max_children(choice_type: ChoiceTypeT, kwargs: ChoiceKwargsT) -> int:
+def compute_max_children(
+    choice_type: ChoiceTypeT, constraints: ChoiceConstraintsT
+) -> int:
     if choice_type == "integer":
-        kwargs = cast(IntegerKWargs, kwargs)
-        min_value = kwargs["min_value"]
-        max_value = kwargs["max_value"]
+        constraints = cast(IntegerConstraints, constraints)
+        min_value = constraints["min_value"]
+        max_value = constraints["max_value"]
 
         if min_value is None and max_value is None:
             # full 128 bit range.
@@ -199,22 +207,24 @@ def compute_max_children(choice_type: ChoiceTypeT, kwargs: ChoiceKwargsT) -> int
         assert (min_value is None) ^ (max_value is None)
         return 2**127
     elif choice_type == "boolean":
-        kwargs = cast(BooleanKWargs, kwargs)
-        p = kwargs["p"]
+        constraints = cast(BooleanConstraints, constraints)
+        p = constraints["p"]
         # probabilities of 0 or 1 (or effectively 0 or 1) only have one choice.
         if p <= 2 ** (-64) or p >= (1 - 2 ** (-64)):
             return 1
         return 2
     elif choice_type == "bytes":
-        kwargs = cast(BytesKWargs, kwargs)
+        constraints = cast(BytesConstraints, constraints)
         return _count_distinct_strings(
-            alphabet_size=2**8, min_size=kwargs["min_size"], max_size=kwargs["max_size"]
+            alphabet_size=2**8,
+            min_size=constraints["min_size"],
+            max_size=constraints["max_size"],
         )
     elif choice_type == "string":
-        kwargs = cast(StringKWargs, kwargs)
-        min_size = kwargs["min_size"]
-        max_size = kwargs["max_size"]
-        intervals = kwargs["intervals"]
+        constraints = cast(StringConstraints, constraints)
+        min_size = constraints["min_size"]
+        max_size = constraints["max_size"]
+        intervals = constraints["intervals"]
 
         if len(intervals) == 0:
             # Special-case the empty alphabet to avoid an error in math.log(0).
@@ -230,10 +240,10 @@ def compute_max_children(choice_type: ChoiceTypeT, kwargs: ChoiceKwargsT) -> int
             alphabet_size=len(intervals), min_size=min_size, max_size=max_size
         )
     elif choice_type == "float":
-        kwargs = cast(FloatKWargs, kwargs)
-        min_value_f = kwargs["min_value"]
-        max_value_f = kwargs["max_value"]
-        smallest_nonzero_magnitude = kwargs["smallest_nonzero_magnitude"]
+        constraints = cast(FloatConstraints, constraints)
+        min_value_f = constraints["min_value"]
+        max_value_f = constraints["max_value"]
+        smallest_nonzero_magnitude = constraints["smallest_nonzero_magnitude"]
 
         count = count_between_floats(min_value_f, max_value_f)
 
@@ -266,7 +276,7 @@ def compute_max_children(choice_type: ChoiceTypeT, kwargs: ChoiceKwargsT) -> int
 
 # In theory, this is a strict superset of the functionality of compute_max_children;
 #
-#   assert len(all_children(choice_type, kwargs)) == compute_max_children(choice_type, kwargs)
+#   assert len(all_children(choice_type, constraints)) == compute_max_children(choice_type, constraints)
 #
 # In practice, we maintain two distinct implementations for efficiency and space
 # reasons. If you just need the number of children, it is cheaper to use
@@ -278,13 +288,13 @@ def _floats_between(a: float, b: float) -> Generator[float, None, None]:
 
 
 def all_children(
-    choice_type: ChoiceTypeT, kwargs: ChoiceKwargsT
+    choice_type: ChoiceTypeT, constraints: ChoiceConstraintsT
 ) -> Generator[ChoiceT, None, None]:
     if choice_type != "float":
-        for index in range(compute_max_children(choice_type, kwargs)):
-            yield choice_from_index(index, choice_type, kwargs)
+        for index in range(compute_max_children(choice_type, constraints)):
+            yield choice_from_index(index, choice_type, constraints)
     else:
-        kwargs = cast(FloatKWargs, kwargs)
+        constraints = cast(FloatConstraints, constraints)
         # the float ordering is not injective (because of resampling
         # out-of-bounds values), so using choice_from_index would result in
         # duplicates. This violates invariants in datatree about being able
@@ -292,9 +302,9 @@ def all_children(
         #
         # We instead maintain a separate implementation for floats.
         # TODO_IR write a better (bijective) ordering for floats and remove this!
-        min_value = kwargs["min_value"]
-        max_value = kwargs["max_value"]
-        smallest_nonzero_magnitude = kwargs["smallest_nonzero_magnitude"]
+        min_value = constraints["min_value"]
+        max_value = constraints["max_value"]
+        smallest_nonzero_magnitude = constraints["smallest_nonzero_magnitude"]
 
         # handle zeroes separately so smallest_nonzero_magnitude can think of
         # itself as a complete interval (instead of a hole at ±0).
@@ -331,7 +341,7 @@ class TreeNode:
 
     Conceptually, you can unfold a single TreeNode storing n values in its lists
     into a sequence of n nodes, each a child of the last. In other words,
-    (kwargs[i], values[i], choice_types[i]) corresponds to the single node at index
+    (constraints[i], values[i], choice_types[i]) corresponds to the single node at index
     i.
 
     Note that if a TreeNode represents a choice (i.e. the nodes cannot be compacted
@@ -384,9 +394,9 @@ class TreeNode:
                   └───┘      └───┘
     """
 
-    # The kwargs, value, and choice_types of the nodes stored here. These always
+    # The constraints, value, and choice_types of the nodes stored here. These always
     # have the same length. The values at index i belong to node i.
-    kwargs: list[ChoiceKwargsT] = attr.ib(factory=list)
+    constraints: list[ChoiceConstraintsT] = attr.ib(factory=list)
     values: list[ChoiceT] = attr.ib(factory=list)
     choice_types: list[ChoiceTypeT] = attr.ib(factory=list)
 
@@ -446,12 +456,12 @@ def split_at(self, i: int) -> None:
 
         child = TreeNode(
             choice_types=self.choice_types[i + 1 :],
-            kwargs=self.kwargs[i + 1 :],
+            constraints=self.constraints[i + 1 :],
             values=self.values[i + 1 :],
             transition=self.transition,
         )
         self.transition = Branch(
-            kwargs=self.kwargs[i],
+            constraints=self.constraints[i],
             choice_type=self.choice_types[i],
             children={key: child},
         )
@@ -461,8 +471,8 @@ def split_at(self, i: int) -> None:
         child.check_exhausted()
         del self.choice_types[i:]
         del self.values[i:]
-        del self.kwargs[i:]
-        assert len(self.values) == len(self.kwargs) == len(self.choice_types) == i
+        del self.constraints[i:]
+        assert len(self.values) == len(self.constraints) == len(self.choice_types) == i
 
     def check_exhausted(self) -> bool:
         """
@@ -511,14 +521,16 @@ def check_exhausted(self) -> bool:
     def _repr_pretty_(self, p: "RepresentationPrinter", cycle: bool) -> None:
         assert cycle is False
         indent = 0
-        for i, (choice_type, kwargs, value) in enumerate(
-            zip(self.choice_types, self.kwargs, self.values)
+        for i, (choice_type, constraints, value) in enumerate(
+            zip(self.choice_types, self.constraints, self.values)
         ):
             with p.indent(indent):
                 if i > 0:
                     p.break_()
                 p.text(
-                    _node_pretty(choice_type, value, kwargs, forced=i in self.forced)
+                    _node_pretty(
+                        choice_type, value, constraints, forced=i in self.forced
+                    )
                 )
             indent += 2
 
@@ -656,15 +668,15 @@ class DataTree:
     intuition. In practice, there are some implementation details to be aware
     of.
 
-    - In draw nodes, we store the kwargs used in addition to the value drawn.
+    - In draw nodes, we store the constraints used in addition to the value drawn.
       E.g. the node corresponding to data.draw_float(min_value=1.0, max_value=1.5)
       would store {"min_value": 1.0, "max_value": 1.5, ...} (default values for
-      other kwargs omitted).
+      other constraints omitted).
 
-      The kwargs parameters have the potential to change both the range of
+      The constraints parameters have the potential to change both the range of
       possible outputs of a node, and the probability distribution within that
       range, so we need to use these when drawing in DataTree as well. We draw
-      values using these kwargs when (1) generating a novel value for a node
+      values using these constraints when (1) generating a novel value for a node
       and (2) choosing a random child when traversing the tree.
 
     - For space efficiency, rather than tracking the full tree structure, we
@@ -707,8 +719,12 @@ def append_choice(choice_type: ChoiceTypeT, choice: ChoiceT) -> None:
         current_node = self.root
         while True:
             assert not current_node.is_exhausted
-            for i, (choice_type, kwargs, value) in enumerate(
-                zip(current_node.choice_types, current_node.kwargs, current_node.values)
+            for i, (choice_type, constraints, value) in enumerate(
+                zip(
+                    current_node.choice_types,
+                    current_node.constraints,
+                    current_node.values,
+                )
             ):
                 if i in current_node.forced:
                     append_choice(choice_type, value)
@@ -718,7 +734,7 @@ def append_choice(choice_type: ChoiceTypeT, choice: ChoiceT) -> None:
                         if attempts <= 10:
                             try:
                                 node_value = self._draw(
-                                    choice_type, kwargs, random=random
+                                    choice_type, constraints, random=random
                                 )
                             except StopTest:  # pragma: no cover
                                 # it is possible that drawing from a fresh data can
@@ -728,7 +744,10 @@ def append_choice(choice_type: ChoiceTypeT, choice: ChoiceT) -> None:
                                 continue
                         else:
                             node_value = self._draw_from_cache(
-                                choice_type, kwargs, key=id(current_node), random=random
+                                choice_type,
+                                constraints,
+                                key=id(current_node),
+                                random=random,
                             )
 
                         if node_value != value:
@@ -736,7 +755,10 @@ def append_choice(choice_type: ChoiceTypeT, choice: ChoiceT) -> None:
                             break
                         attempts += 1
                         self._reject_child(
-                            choice_type, kwargs, child=node_value, key=id(current_node)
+                            choice_type,
+                            constraints,
+                            child=node_value,
+                            key=id(current_node),
                         )
                     # We've now found a value that is allowed to
                     # vary, so what follows is not fixed.
@@ -753,7 +775,7 @@ def append_choice(choice_type: ChoiceTypeT, choice: ChoiceT) -> None:
                     if attempts <= 10:
                         try:
                             node_value = self._draw(
-                                branch.choice_type, branch.kwargs, random=random
+                                branch.choice_type, branch.constraints, random=random
                             )
                         except StopTest:  # pragma: no cover
                             attempts += 1
@@ -761,7 +783,7 @@ def append_choice(choice_type: ChoiceTypeT, choice: ChoiceT) -> None:
                     else:
                         node_value = self._draw_from_cache(
                             branch.choice_type,
-                            branch.kwargs,
+                            branch.constraints,
                             key=id(branch),
                             random=random,
                         )
@@ -777,7 +799,7 @@ def append_choice(choice_type: ChoiceTypeT, choice: ChoiceT) -> None:
                     attempts += 1
                     self._reject_child(
                         branch.choice_type,
-                        branch.kwargs,
+                        branch.constraints,
                         child=node_value,
                         key=id(branch),
                     )
@@ -810,12 +832,12 @@ def simulate_test_function(self, data: ConjectureData) -> None:
         tree. This will likely change in future."""
         node = self.root
 
-        def draw(choice_type, kwargs, *, forced=None, convert_forced=True):
+        def draw(choice_type, constraints, *, forced=None, convert_forced=True):
             if choice_type == "float" and forced is not None and convert_forced:
                 forced = int_to_float(forced)
 
             draw_func = getattr(data, f"draw_{choice_type}")
-            value = draw_func(**kwargs, forced=forced)
+            value = draw_func(**constraints, forced=forced)
 
             if choice_type == "float":
                 value = float_to_int(value)
@@ -823,12 +845,12 @@ def draw(choice_type, kwargs, *, forced=None, convert_forced=True):
 
         try:
             while True:
-                for i, (choice_type, kwargs, previous) in enumerate(
-                    zip(node.choice_types, node.kwargs, node.values)
+                for i, (choice_type, constraints, previous) in enumerate(
+                    zip(node.choice_types, node.constraints, node.values)
                 ):
                     v = draw(
                         choice_type,
-                        kwargs,
+                        constraints,
                         forced=previous if i in node.forced else None,
                     )
                     if v != previous:
@@ -839,7 +861,7 @@ def draw(choice_type, kwargs, *, forced=None, convert_forced=True):
                 elif node.transition is None:
                     raise PreviouslyUnseenBehaviour
                 elif isinstance(node.transition, Branch):
-                    v = draw(node.transition.choice_type, node.transition.kwargs)
+                    v = draw(node.transition.choice_type, node.transition.constraints)
                     try:
                         node = node.transition.children[v]
                     except KeyError as err:
@@ -855,11 +877,15 @@ def new_observer(self):
         return TreeRecordingObserver(self)
 
     def _draw(
-        self, choice_type: ChoiceTypeT, kwargs: ChoiceKwargsT, *, random: Random
+        self,
+        choice_type: ChoiceTypeT,
+        constraints: ChoiceConstraintsT,
+        *,
+        random: Random,
     ) -> ChoiceT:
         from hypothesis.internal.conjecture.data import draw_choice
 
-        value = draw_choice(choice_type, kwargs, random=random)
+        value = draw_choice(choice_type, constraints, random=random)
         # using floats as keys into branch.children breaks things, because
         # e.g. hash(0.0) == hash(-0.0) would collide as keys when they are
         # in fact distinct child branches.
@@ -873,7 +899,7 @@ def _draw(
         return value
 
     def _get_children_cache(
-        self, choice_type: ChoiceTypeT, kwargs: ChoiceKwargsT, *, key: ChoiceT
+        self, choice_type: ChoiceTypeT, constraints: ChoiceConstraintsT, *, key: ChoiceT
     ) -> ChildrenCacheValueT:
         # cache the state of the children generator per node/branch (passed as
         # `key` here), such that we track which children we've already tried
@@ -885,7 +911,7 @@ def _get_children_cache(
         # with. Whenever we need to top up this list, we will draw a new value
         # from the generator.
         if key not in self._children_cache:
-            generator = all_children(choice_type, kwargs)
+            generator = all_children(choice_type, constraints)
             children: list[ChoiceT] = []
             rejected: set[ChoiceT] = set()
             self._children_cache[key] = (generator, children, rejected)
@@ -895,13 +921,13 @@ def _get_children_cache(
     def _draw_from_cache(
         self,
         choice_type: ChoiceTypeT,
-        kwargs: ChoiceKwargsT,
+        constraints: ChoiceConstraintsT,
         *,
         key: ChoiceT,
         random: Random,
     ) -> ChoiceT:
         (generator, children, rejected) = self._get_children_cache(
-            choice_type, kwargs, key=key
+            choice_type, constraints, key=key
         )
         # Keep a stock of 100 potentially-valid children at all times.
         # This number is chosen to balance memory/speed vs randomness. Ideally
@@ -925,13 +951,13 @@ def _draw_from_cache(
     def _reject_child(
         self,
         choice_type: ChoiceTypeT,
-        kwargs: ChoiceKwargsT,
+        constraints: ChoiceConstraintsT,
         *,
         child: ChoiceT,
         key: ChoiceT,
     ) -> None:
         (_generator, children, rejected) = self._get_children_cache(
-            choice_type, kwargs, key=key
+            choice_type, constraints, key=key
         )
         rejected.add(child)
         # we remove a child from the list of possible children *only* when it is
@@ -966,29 +992,33 @@ def __init__(self, tree: DataTree):
         self.killed: bool = False
 
     def draw_integer(
-        self, value: int, *, was_forced: bool, kwargs: IntegerKWargs
+        self, value: int, *, was_forced: bool, constraints: IntegerConstraints
     ) -> None:
-        self.draw_value("integer", value, was_forced=was_forced, kwargs=kwargs)
+        self.draw_value(
+            "integer", value, was_forced=was_forced, constraints=constraints
+        )
 
     def draw_float(
-        self, value: float, *, was_forced: bool, kwargs: FloatKWargs
+        self, value: float, *, was_forced: bool, constraints: FloatConstraints
     ) -> None:
-        self.draw_value("float", value, was_forced=was_forced, kwargs=kwargs)
+        self.draw_value("float", value, was_forced=was_forced, constraints=constraints)
 
     def draw_string(
-        self, value: str, *, was_forced: bool, kwargs: StringKWargs
+        self, value: str, *, was_forced: bool, constraints: StringConstraints
     ) -> None:
-        self.draw_value("string", value, was_forced=was_forced, kwargs=kwargs)
+        self.draw_value("string", value, was_forced=was_forced, constraints=constraints)
 
     def draw_bytes(
-        self, value: bytes, *, was_forced: bool, kwargs: BytesKWargs
+        self, value: bytes, *, was_forced: bool, constraints: BytesConstraints
     ) -> None:
-        self.draw_value("bytes", value, was_forced=was_forced, kwargs=kwargs)
+        self.draw_value("bytes", value, was_forced=was_forced, constraints=constraints)
 
     def draw_boolean(
-        self, value: bool, *, was_forced: bool, kwargs: BooleanKWargs
+        self, value: bool, *, was_forced: bool, constraints: BooleanConstraints
     ) -> None:
-        self.draw_value("boolean", value, was_forced=was_forced, kwargs=kwargs)
+        self.draw_value(
+            "boolean", value, was_forced=was_forced, constraints=constraints
+        )
 
     def draw_value(
         self,
@@ -996,7 +1026,7 @@ def draw_value(
         value: ChoiceT,
         *,
         was_forced: bool,
-        kwargs: ChoiceKwargsT,
+        constraints: ChoiceConstraintsT,
     ) -> None:
         i = self.__index_in_current_node
         self.__index_in_current_node += 1
@@ -1005,9 +1035,12 @@ def draw_value(
         if isinstance(value, float):
             value = float_to_int(value)
 
-        assert len(node.kwargs) == len(node.values) == len(node.choice_types)
+        assert len(node.constraints) == len(node.values) == len(node.choice_types)
         if i < len(node.values):
-            if choice_type != node.choice_types[i] or kwargs != node.kwargs[i]:
+            if (
+                choice_type != node.choice_types[i]
+                or constraints != node.constraints[i]
+            ):
                 raise FlakyStrategyDefinition(_FLAKY_STRAT_MSG)
             # Note that we don't check whether a previously
             # forced value is now free. That will be caught
@@ -1029,7 +1062,7 @@ def draw_value(
             trans = node.transition
             if trans is None:
                 node.choice_types.append(choice_type)
-                node.kwargs.append(kwargs)
+                node.constraints.append(constraints)
                 node.values.append(value)
                 if was_forced:
                     node.mark_forced(i)
@@ -1049,7 +1082,10 @@ def draw_value(
                 # An alternative is not writing such choices to the tree at
                 # all, and thus guaranteeing that each node has at least 2 max
                 # children.
-                if compute_max_children(choice_type, kwargs) == 1 and not was_forced:
+                if (
+                    compute_max_children(choice_type, constraints) == 1
+                    and not was_forced
+                ):
                     node.split_at(i)
                     assert isinstance(node.transition, Branch)
                     self.__current_node = node.transition.children[value]
@@ -1061,7 +1097,7 @@ def draw_value(
                 raise FlakyStrategyDefinition(_FLAKY_STRAT_MSG)
             else:
                 assert isinstance(trans, Branch), trans
-                if choice_type != trans.choice_type or kwargs != trans.kwargs:
+                if choice_type != trans.choice_type or constraints != trans.constraints:
                     raise FlakyStrategyDefinition(_FLAKY_STRAT_MSG)
                 try:
                     self.__current_node = trans.children[value]
diff --git a/hypothesis-python/src/hypothesis/internal/conjecture/engine.py b/hypothesis-python/src/hypothesis/internal/conjecture/engine.py
index ec6c3e73e5..b0ea8ba62c 100644
--- a/hypothesis-python/src/hypothesis/internal/conjecture/engine.py
+++ b/hypothesis-python/src/hypothesis/internal/conjecture/engine.py
@@ -35,8 +35,8 @@
 from hypothesis.internal.cache import LRUReusedCache
 from hypothesis.internal.compat import NotRequired, TypeAlias, TypedDict, ceil, override
 from hypothesis.internal.conjecture.choice import (
+    ChoiceConstraintsT,
     ChoiceKeyT,
-    ChoiceKwargsT,
     ChoiceNode,
     ChoiceT,
     ChoiceTemplate,
@@ -229,12 +229,12 @@ def realize_choices(data: ConjectureData) -> None:
                 f"{data.provider.realize.__qualname__}, got {type(value)}"
             )
 
-        kwargs = cast(
-            ChoiceKwargsT,
-            {k: data.provider.realize(v) for k, v in node.kwargs.items()},
+        constraints = cast(
+            ChoiceConstraintsT,
+            {k: data.provider.realize(v) for k, v in node.constraints.items()},
         )
         node.value = value
-        node.kwargs = kwargs
+        node.constraints = constraints
 
 
 class ConjectureRunner:
diff --git a/hypothesis-python/src/hypothesis/internal/conjecture/optimiser.py b/hypothesis-python/src/hypothesis/internal/conjecture/optimiser.py
index 33c58ceda9..03807cf4be 100644
--- a/hypothesis-python/src/hypothesis/internal/conjecture/optimiser.py
+++ b/hypothesis-python/src/hypothesis/internal/conjecture/optimiser.py
@@ -157,7 +157,7 @@ def attempt_replace(k: int) -> bool:
                     size = max(len(node.value), bits_to_bytes(v.bit_length()))
                     new_choice = int_to_bytes(v, size)
 
-                if not choice_permitted(new_choice, node.kwargs):
+                if not choice_permitted(new_choice, node.constraints):
                     return False
 
                 for _ in range(3):
diff --git a/hypothesis-python/src/hypothesis/internal/conjecture/providers.py b/hypothesis-python/src/hypothesis/internal/conjecture/providers.py
index 609b2dd687..beccc75c36 100644
--- a/hypothesis-python/src/hypothesis/internal/conjecture/providers.py
+++ b/hypothesis-python/src/hypothesis/internal/conjecture/providers.py
@@ -30,8 +30,8 @@
 from hypothesis.internal.cache import LRUCache
 from hypothesis.internal.compat import WINDOWS, int_from_bytes
 from hypothesis.internal.conjecture.choice import (
-    StringKWargs,
-    choice_kwargs_key,
+    StringConstraints,
+    choice_constraints_key,
     choice_permitted,
 )
 from hypothesis.internal.conjecture.floats import float_to_lex, lex_to_float
@@ -235,7 +235,7 @@ class PrimitiveProvider(abc.ABC):
     #
     # Setting this to True disables some hypothesis features, such as
     # DataTree-based deduplication, and some internal optimizations, such as
-    # caching kwargs. Only enable this if it is necessary for your backend.
+    # caching constraints. Only enable this if it is necessary for your backend.
     avoid_realization = False
 
     def __init__(self, conjecturedata: Optional["ConjectureData"], /) -> None:
@@ -683,16 +683,16 @@ def _draw_string_sampler(
         min_size: int,
         max_size: int,
     ) -> tuple[Optional[Sampler], list[str]]:
-        kwargs: StringKWargs = {
+        constraints: StringConstraints = {
             "intervals": intervals,
             "min_size": min_size,
             "max_size": max_size,
         }
-        key = choice_kwargs_key("string", kwargs)
+        key = choice_constraints_key("string", constraints)
         if key in STRING_SAMPLER_CACHE:
             return STRING_SAMPLER_CACHE[key]
 
-        nasty_strings = [s for s in NASTY_STRINGS if choice_permitted(s, kwargs)]
+        nasty_strings = [s for s in NASTY_STRINGS if choice_permitted(s, constraints)]
         sampler = (
             Sampler([1 / len(nasty_strings)] * len(nasty_strings), observe=False)
             if nasty_strings
diff --git a/hypothesis-python/src/hypothesis/internal/conjecture/shrinker.py b/hypothesis-python/src/hypothesis/internal/conjecture/shrinker.py
index 5a6806c76d..1802034110 100644
--- a/hypothesis-python/src/hypothesis/internal/conjecture/shrinker.py
+++ b/hypothesis-python/src/hypothesis/internal/conjecture/shrinker.py
@@ -81,7 +81,7 @@ def sort_key(nodes: Sequence[ChoiceNode]) -> tuple[int, tuple[int, ...]]:
     """
     return (
         len(nodes),
-        tuple(choice_to_index(node.value, node.kwargs) for node in nodes),
+        tuple(choice_to_index(node.value, node.constraints) for node in nodes),
     )
 
 
@@ -394,7 +394,7 @@ def cached_test_function(
 
         # sometimes our shrinking passes try obviously invalid things. We handle
         # discarding them in one place here.
-        if any(not choice_permitted(node.value, node.kwargs) for node in nodes):
+        if any(not choice_permitted(node.value, node.constraints) for node in nodes):
             return (False, None)
 
         result = self.engine.cached_test_function(
@@ -531,7 +531,9 @@ def explain(self) -> None:
                 for i in range(start, end):
                     node = nodes[i]
                     if not node.was_forced:
-                        value = draw_choice(node.type, node.kwargs, random=self.random)
+                        value = draw_choice(
+                            node.type, node.constraints, random=self.random
+                        )
                         node = node.copy(with_value=value)
                     replacement.append(node.value)
 
@@ -682,7 +684,7 @@ def reduce_each_alternative(self):
                 node.type == "integer"
                 and not node.was_forced
                 and node.value <= 10
-                and node.kwargs["min_value"] == 0
+                and node.constraints["min_value"] == 0
             ):
                 assert isinstance(node.value, int)
 
@@ -713,7 +715,7 @@ def reduce_each_alternative(self):
                             if (
                                 zero_node.type != orig_node.type
                                 or not choice_permitted(
-                                    orig_node.value, zero_node.kwargs
+                                    orig_node.value, zero_node.constraints
                                 )
                             ):
                                 changed_shape = True
@@ -982,7 +984,9 @@ def lower_common_node_offset(self):
         if not changed:
             return
 
-        ints = [abs(node.value - node.kwargs["shrink_towards"]) for node in changed]
+        ints = [
+            abs(node.value - node.constraints["shrink_towards"]) for node in changed
+        ]
         offset = min(ints)
         assert offset > 0
 
@@ -995,7 +999,7 @@ def offset_node(node, n):
             return (
                 node.index,
                 node.index + 1,
-                [node.copy(with_value=node.kwargs["shrink_towards"] + n)],
+                [node.copy(with_value=node.constraints["shrink_towards"] + n)],
             )
 
         def consider(n, sign):
@@ -1036,7 +1040,7 @@ def __changed_nodes(self) -> set[int]:
         if len(prev_nodes) != len(new_nodes) or any(
             n1.type != n2.type for n1, n2 in zip(prev_nodes, new_nodes)
         ):
-            # should we check kwargs are equal as well?
+            # should we check constraints are equal as well?
             self.__all_changed_nodes = set()
         else:
             assert len(prev_nodes) == len(new_nodes)
@@ -1118,7 +1122,7 @@ def try_shrinking_nodes(self, nodes, n):
             # min_size than our attempt had for the draw_string node.
             #
             # We'll now try realigning this tree by:
-            # * replacing the kwargs in our attempt with what test_function tried
+            # * replacing the constraints in our attempt with what test_function tried
             #   to draw in practice
             # * truncating the value of that node to match min_size
             #
@@ -1130,7 +1134,7 @@ def try_shrinking_nodes(self, nodes, n):
             # case of this function of preserving from the right instead of
             # preserving from the left. see test_can_shrink_variable_string_draws.
 
-            (index, attempt_choice_type, attempt_kwargs, _attempt_forced) = (
+            (index, attempt_choice_type, attempt_constraints, _attempt_forced) = (
                 attempt.misaligned_at
             )
             node = self.nodes[index]
@@ -1142,7 +1146,7 @@ def try_shrinking_nodes(self, nodes, n):
             if node.type in {"string", "bytes"}:
                 # if the size *increased*, we would have to guess what to pad with
                 # in order to try fixing up this attempt. Just give up.
-                if node.kwargs["min_size"] <= attempt_kwargs["min_size"]:
+                if node.constraints["min_size"] <= attempt_constraints["min_size"]:
                     # attempts which increase min_size tend to overrun rather than
                     # be misaligned, making a covering case difficult.
                     return False  # pragma: no cover
@@ -1152,9 +1156,9 @@ def try_shrinking_nodes(self, nodes, n):
                     initial_attempt[: node.index]
                     + [
                         initial_attempt[node.index].copy(
-                            with_kwargs=attempt_kwargs,
+                            with_constraints=attempt_constraints,
                             with_value=initial_attempt[node.index].value[
-                                : attempt_kwargs["min_size"]
+                                : attempt_constraints["min_size"]
                             ],
                         )
                     ]
@@ -1372,7 +1376,7 @@ def lower_integers_together(self, chooser):
         # unconditionally. In reality, it's acceptable for us to transition node2
         # from trivial to nontrivial, because the shrink ordering is dominated by
         # the complexity of the earlier node1. What matters is minimizing node1.
-        shrink_towards = node1.kwargs["shrink_towards"]
+        shrink_towards = node1.constraints["shrink_towards"]
 
         def consider(n):
             return self.consider_new_nodes(
@@ -1426,7 +1430,7 @@ def lower_duplicated_characters(self, chooser):
         duplicated_characters = set(node1.value) & set(node2.value)
         # deterministic ordering
         char = chooser.choose(sorted(duplicated_characters))
-        intervals = node1.kwargs["intervals"]
+        intervals = node1.constraints["intervals"]
 
         def copy_node(node, n):
             # replace all duplicate characters in each string. This might miss
@@ -1451,20 +1455,20 @@ def copy_node(node, n):
     def minimize_nodes(self, nodes):
         choice_type = nodes[0].type
         value = nodes[0].value
-        # unlike choice_type and value, kwargs are *not* guaranteed to be equal among all
-        # passed nodes. We arbitrarily use the kwargs of the first node. I think
+        # unlike choice_type and value, constraints are *not* guaranteed to be equal among all
+        # passed nodes. We arbitrarily use the constraints of the first node. I think
         # this is unsound (= leads to us trying shrinks that could not have been
         # generated), but those get discarded at test-time, and this enables useful
-        # slips where kwargs are not equal but are close enough that doing the
+        # slips where constraints are not equal but are close enough that doing the
         # same operation on both basically just works.
-        kwargs = nodes[0].kwargs
+        constraints = nodes[0].constraints
         assert all(
             node.type == choice_type and choice_equal(node.value, value)
             for node in nodes
         )
 
         if choice_type == "integer":
-            shrink_towards = kwargs["shrink_towards"]
+            shrink_towards = constraints["shrink_towards"]
             # try shrinking from both sides towards shrink_towards.
             # we're starting from n = abs(shrink_towards - value). Because the
             # shrinker will not check its starting value, we need to try
@@ -1497,14 +1501,14 @@ def minimize_nodes(self, nodes):
             Bytes.shrink(
                 value,
                 lambda val: self.try_shrinking_nodes(nodes, val),
-                min_size=kwargs["min_size"],
+                min_size=constraints["min_size"],
             )
         elif choice_type == "string":
             String.shrink(
                 value,
                 lambda val: self.try_shrinking_nodes(nodes, val),
-                intervals=kwargs["intervals"],
-                min_size=kwargs["min_size"],
+                intervals=constraints["intervals"],
+                min_size=constraints["min_size"],
             )
         else:
             raise NotImplementedError
@@ -1523,7 +1527,7 @@ def try_trivial_spans(self, chooser):
                     node
                     if node.was_forced
                     else node.copy(
-                        with_value=choice_from_index(0, node.type, node.kwargs)
+                        with_value=choice_from_index(0, node.type, node.constraints)
                     )
                 )
                 for node in nodes[ex.start : ex.end]
diff --git a/hypothesis-python/src/hypothesis/internal/filtering.py b/hypothesis-python/src/hypothesis/internal/filtering.py
index f06002dced..f56e9b84b6 100644
--- a/hypothesis-python/src/hypothesis/internal/filtering.py
+++ b/hypothesis-python/src/hypothesis/internal/filtering.py
@@ -44,7 +44,7 @@
 
 
 class ConstructivePredicate(NamedTuple):
-    """Return kwargs to the appropriate strategy, and the predicate if needed.
+    """Return constraints to the appropriate strategy, and the predicate if needed.
 
     For example::
 
@@ -61,7 +61,7 @@ class ConstructivePredicate(NamedTuple):
     for each numeric type, for strings, for bytes, for collection sizes, etc.
     """
 
-    kwargs: dict[str, Any]
+    constraints: dict[str, Any]
     predicate: Optional[Predicate]
 
     @classmethod
@@ -70,7 +70,7 @@ def unchanged(cls, predicate: Predicate) -> "ConstructivePredicate":
 
     def __repr__(self) -> str:
         fn = get_pretty_function_description(self.predicate)
-        return f"{self.__class__.__name__}(kwargs={self.kwargs!r}, predicate={fn})"
+        return f"{self.__class__.__name__}(constraints={self.constraints!r}, predicate={fn})"
 
 
 ARG = object()
@@ -92,7 +92,7 @@ def convert(node: ast.AST, argname: str) -> object:
     return ast.literal_eval(node)
 
 
-def comp_to_kwargs(x: ast.AST, op: ast.AST, y: ast.AST, *, argname: str) -> dict:
+def comp_to_constraints(x: ast.AST, op: ast.AST, y: ast.AST, *, argname: str) -> dict:
     a = convert(x, argname)
     b = convert(y, argname)
     num = (int, float)
@@ -196,11 +196,11 @@ def numeric_bounds_from_ast(
         bounds = []
         for comp in comparisons:
             try:
-                kwargs = comp_to_kwargs(*comp, argname=argname)
+                constraints = comp_to_constraints(*comp, argname=argname)
                 # Because `len` could be redefined in the enclosing scope, we *always*
                 # have to apply the condition as a filter, in addition to rewriting.
-                pred = fallback.predicate if "len" in kwargs else None
-                bounds.append(ConstructivePredicate(kwargs, pred))
+                pred = fallback.predicate if "len" in constraints else None
+                bounds.append(ConstructivePredicate(constraints, pred))
             except ValueError:
                 bounds.append(fallback)
         return merge_preds(*bounds)
@@ -282,54 +282,58 @@ def get_numeric_predicate_bounds(predicate: Predicate) -> ConstructivePredicate:
 
 
 def get_integer_predicate_bounds(predicate: Predicate) -> ConstructivePredicate:
-    kwargs, predicate = get_numeric_predicate_bounds(predicate)
+    constraints, predicate = get_numeric_predicate_bounds(predicate)
 
-    if "min_value" in kwargs:
-        if kwargs["min_value"] == -math.inf:
-            del kwargs["min_value"]
-        elif math.isinf(kwargs["min_value"]):
+    if "min_value" in constraints:
+        if constraints["min_value"] == -math.inf:
+            del constraints["min_value"]
+        elif math.isinf(constraints["min_value"]):
             return ConstructivePredicate({"min_value": 1, "max_value": -1}, None)
-        elif kwargs["min_value"] != int(kwargs["min_value"]):
-            kwargs["min_value"] = ceil(kwargs["min_value"])
-        elif kwargs.get("exclude_min", False):
-            kwargs["min_value"] = int(kwargs["min_value"]) + 1
-
-    if "max_value" in kwargs:
-        if kwargs["max_value"] == math.inf:
-            del kwargs["max_value"]
-        elif math.isinf(kwargs["max_value"]):
+        elif constraints["min_value"] != int(constraints["min_value"]):
+            constraints["min_value"] = ceil(constraints["min_value"])
+        elif constraints.get("exclude_min", False):
+            constraints["min_value"] = int(constraints["min_value"]) + 1
+
+    if "max_value" in constraints:
+        if constraints["max_value"] == math.inf:
+            del constraints["max_value"]
+        elif math.isinf(constraints["max_value"]):
             return ConstructivePredicate({"min_value": 1, "max_value": -1}, None)
-        elif kwargs["max_value"] != int(kwargs["max_value"]):
-            kwargs["max_value"] = floor(kwargs["max_value"])
-        elif kwargs.get("exclude_max", False):
-            kwargs["max_value"] = int(kwargs["max_value"]) - 1
+        elif constraints["max_value"] != int(constraints["max_value"]):
+            constraints["max_value"] = floor(constraints["max_value"])
+        elif constraints.get("exclude_max", False):
+            constraints["max_value"] = int(constraints["max_value"]) - 1
 
     kw_categories = {"min_value", "max_value", "len"}
-    kwargs = {k: v for k, v in kwargs.items() if k in kw_categories}
-    return ConstructivePredicate(kwargs, predicate)
+    constraints = {k: v for k, v in constraints.items() if k in kw_categories}
+    return ConstructivePredicate(constraints, predicate)
 
 
 def get_float_predicate_bounds(predicate: Predicate) -> ConstructivePredicate:
-    kwargs, predicate = get_numeric_predicate_bounds(predicate)
-
-    if "min_value" in kwargs:
-        min_value = kwargs["min_value"]
-        kwargs["min_value"] = float(kwargs["min_value"])
-        if min_value < kwargs["min_value"] or (
-            min_value == kwargs["min_value"] and kwargs.get("exclude_min", False)
+    constraints, predicate = get_numeric_predicate_bounds(predicate)
+
+    if "min_value" in constraints:
+        min_value = constraints["min_value"]
+        constraints["min_value"] = float(constraints["min_value"])
+        if min_value < constraints["min_value"] or (
+            min_value == constraints["min_value"]
+            and constraints.get("exclude_min", False)
         ):
-            kwargs["min_value"] = next_up(kwargs["min_value"])
-
-    if "max_value" in kwargs:
-        max_value = kwargs["max_value"]
-        kwargs["max_value"] = float(kwargs["max_value"])
-        if max_value > kwargs["max_value"] or (
-            max_value == kwargs["max_value"] and kwargs.get("exclude_max", False)
+            constraints["min_value"] = next_up(constraints["min_value"])
+
+    if "max_value" in constraints:
+        max_value = constraints["max_value"]
+        constraints["max_value"] = float(constraints["max_value"])
+        if max_value > constraints["max_value"] or (
+            max_value == constraints["max_value"]
+            and constraints.get("exclude_max", False)
         ):
-            kwargs["max_value"] = next_down(kwargs["max_value"])
+            constraints["max_value"] = next_down(constraints["max_value"])
 
-    kwargs = {k: v for k, v in kwargs.items() if k in {"min_value", "max_value"}}
-    return ConstructivePredicate(kwargs, predicate)
+    constraints = {
+        k: v for k, v in constraints.items() if k in {"min_value", "max_value"}
+    }
+    return ConstructivePredicate(constraints, predicate)
 
 
 def max_len(size: int, element: Collection[object]) -> bool:
diff --git a/hypothesis-python/src/hypothesis/strategies/_internal/collections.py b/hypothesis-python/src/hypothesis/strategies/_internal/collections.py
index 9e7c48a774..bcdbe7f333 100644
--- a/hypothesis-python/src/hypothesis/strategies/_internal/collections.py
+++ b/hypothesis-python/src/hypothesis/strategies/_internal/collections.py
@@ -221,11 +221,17 @@ def filter(self, condition: Callable[[list[Ex]], Any]) -> SearchStrategy[list[Ex
             new.min_size = 1
             return new
 
-        kwargs, pred = get_integer_predicate_bounds(condition)
-        if kwargs.get("len") and ("min_value" in kwargs or "max_value" in kwargs):
+        constraints, pred = get_integer_predicate_bounds(condition)
+        if constraints.get("len") and (
+            "min_value" in constraints or "max_value" in constraints
+        ):
             new = copy.copy(self)
-            new.min_size = max(self.min_size, kwargs.get("min_value", self.min_size))
-            new.max_size = min(self.max_size, kwargs.get("max_value", self.max_size))
+            new.min_size = max(
+                self.min_size, constraints.get("min_value", self.min_size)
+            )
+            new.max_size = min(
+                self.max_size, constraints.get("max_value", self.max_size)
+            )
             # Unsatisfiable filters are easiest to understand without rewriting.
             if new.min_size > new.max_size:
                 return SearchStrategy.filter(self, condition)
diff --git a/hypothesis-python/src/hypothesis/strategies/_internal/numbers.py b/hypothesis-python/src/hypothesis/strategies/_internal/numbers.py
index 483ccbd847..59bf66ecc9 100644
--- a/hypothesis-python/src/hypothesis/strategies/_internal/numbers.py
+++ b/hypothesis-python/src/hypothesis/strategies/_internal/numbers.py
@@ -89,13 +89,13 @@ def filter(self, condition):
             return self
         if condition in [math.isinf, math.isnan]:
             return nothing()
-        kwargs, pred = get_integer_predicate_bounds(condition)
+        constraints, pred = get_integer_predicate_bounds(condition)
 
         start, end = self.start, self.end
-        if "min_value" in kwargs:
-            start = max(kwargs["min_value"], -math.inf if start is None else start)
-        if "max_value" in kwargs:
-            end = min(kwargs["max_value"], math.inf if end is None else end)
+        if "min_value" in constraints:
+            start = max(constraints["min_value"], -math.inf if start is None else start)
+        if "max_value" in constraints:
+            end = min(constraints["max_value"], math.inf if end is None else end)
 
         if start != self.start or end != self.end:
             if start is not None and end is not None and start > end:
@@ -210,11 +210,11 @@ def filter(self, condition):
                 return nothing()
             return NanStrategy()
 
-        kwargs, pred = get_float_predicate_bounds(condition)
-        if not kwargs:
+        constraints, pred = get_float_predicate_bounds(condition)
+        if not constraints:
             return super().filter(pred)
-        min_bound = max(kwargs.get("min_value", -math.inf), self.min_value)
-        max_bound = min(kwargs.get("max_value", math.inf), self.max_value)
+        min_bound = max(constraints.get("min_value", -math.inf), self.min_value)
+        max_bound = min(constraints.get("max_value", math.inf), self.max_value)
 
         # Adjustments for allow_subnormal=False, if any need to be made
         if -self.smallest_nonzero_magnitude < min_bound < 0:
diff --git a/hypothesis-python/tests/conjecture/common.py b/hypothesis-python/tests/conjecture/common.py
index 95218219c7..2c7f859d91 100644
--- a/hypothesis-python/tests/conjecture/common.py
+++ b/hypothesis-python/tests/conjecture/common.py
@@ -143,12 +143,12 @@ def fresh_data(*, random=None, observer=None) -> ConjectureData:
     return ConjectureData(random=random, observer=observer)
 
 
-def clamped_shrink_towards(kwargs):
-    v = kwargs["shrink_towards"]
-    if kwargs["min_value"] is not None:
-        v = max(kwargs["min_value"], v)
-    if kwargs["max_value"] is not None:
-        v = min(kwargs["max_value"], v)
+def clamped_shrink_towards(constraints):
+    v = constraints["shrink_towards"]
+    if constraints["min_value"] is not None:
+        v = max(constraints["min_value"], v)
+    if constraints["max_value"] is not None:
+        v = min(constraints["max_value"], v)
     return v
 
 
@@ -175,7 +175,7 @@ def integer_weights(draw, min_value=None, max_value=None):
 
 
 @st.composite
-def integer_kwargs(
+def integer_constraints(
     draw,
     *,
     use_min_value=None,
@@ -242,7 +242,7 @@ def integer_kwargs(
 
 
 @st.composite
-def _collection_kwargs(draw, *, forced, use_min_size=None, use_max_size=None):
+def _collection_constraints(draw, *, forced, use_min_size=None, use_max_size=None):
     min_size = 0
     max_size = COLLECTION_DEFAULT_MAX_SIZE
     # collections are quite expensive in entropy. cap to avoid overruns.
@@ -271,38 +271,40 @@ def _collection_kwargs(draw, *, forced, use_min_size=None, use_max_size=None):
 
 
 @st.composite
-def string_kwargs(draw, *, use_min_size=None, use_max_size=None, use_forced=False):
+def string_constraints(draw, *, use_min_size=None, use_max_size=None, use_forced=False):
     interval_set = draw(intervals())
     forced = (
         draw(TextStrategy(OneCharStringStrategy(interval_set))) if use_forced else None
     )
-    kwargs = draw(
-        _collection_kwargs(
+    constraints = draw(
+        _collection_constraints(
             forced=forced, use_min_size=use_min_size, use_max_size=use_max_size
         )
     )
     # if the intervalset is empty, then the min size must be zero, because the
     # only valid value is the empty string.
     if len(interval_set) == 0:
-        kwargs["min_size"] = 0
+        constraints["min_size"] = 0
 
-    return {"intervals": interval_set, "forced": forced, **kwargs}
+    return {"intervals": interval_set, "forced": forced, **constraints}
 
 
 @st.composite
-def bytes_kwargs(draw, *, use_min_size=None, use_max_size=None, use_forced=False):
+def bytes_constraints(draw, *, use_min_size=None, use_max_size=None, use_forced=False):
     forced = draw(st.binary()) if use_forced else None
 
-    kwargs = draw(
-        _collection_kwargs(
+    constraints = draw(
+        _collection_constraints(
             forced=forced, use_min_size=use_min_size, use_max_size=use_max_size
         )
     )
-    return {"forced": forced, **kwargs}
+    return {"forced": forced, **constraints}
 
 
 @st.composite
-def float_kwargs(draw, *, use_min_value=None, use_max_value=None, use_forced=False):
+def float_constraints(
+    draw, *, use_min_value=None, use_max_value=None, use_forced=False
+):
     if use_min_value is None:
         use_min_value = draw(st.booleans())
     if use_max_value is None:
@@ -353,7 +355,7 @@ def float_kwargs(draw, *, use_min_value=None, use_max_value=None, use_forced=Fal
 
 
 @st.composite
-def boolean_kwargs(draw, *, use_forced=False):
+def boolean_constraints(draw, *, use_forced=False):
     forced = draw(st.booleans()) if use_forced else None
     # avoid invalid forced combinations
     p = draw(st.floats(0, 1, exclude_min=forced is True, exclude_max=forced is False))
@@ -361,59 +363,60 @@ def boolean_kwargs(draw, *, use_forced=False):
     return {"p": p, "forced": forced}
 
 
-def kwargs_strategy(choice_type, strategy_kwargs=None, *, use_forced=False):
+def constraints_strategy(choice_type, strategy_constraints=None, *, use_forced=False):
     strategy = {
-        "boolean": boolean_kwargs,
-        "integer": integer_kwargs,
-        "float": float_kwargs,
-        "bytes": bytes_kwargs,
-        "string": string_kwargs,
+        "boolean": boolean_constraints,
+        "integer": integer_constraints,
+        "float": float_constraints,
+        "bytes": bytes_constraints,
+        "string": string_constraints,
     }[choice_type]
-    if strategy_kwargs is None:
-        strategy_kwargs = {}
-    return strategy(**strategy_kwargs.get(choice_type, {}), use_forced=use_forced)
+    if strategy_constraints is None:
+        strategy_constraints = {}
+    return strategy(**strategy_constraints.get(choice_type, {}), use_forced=use_forced)
 
 
-def choice_types_kwargs(strategy_kwargs=None, *, use_forced=False):
+def choice_types_constraints(strategy_constraints=None, *, use_forced=False):
     options = ["boolean", "integer", "float", "bytes", "string"]
     return st.one_of(
         st.tuples(
-            st.just(name), kwargs_strategy(name, strategy_kwargs, use_forced=use_forced)
+            st.just(name),
+            constraints_strategy(name, strategy_constraints, use_forced=use_forced),
         )
         for name in options
     )
 
 
-def draw_value(choice_type, kwargs):
+def draw_value(choice_type, constraints):
     data = fresh_data()
-    return getattr(data, f"draw_{choice_type}")(**kwargs)
+    return getattr(data, f"draw_{choice_type}")(**constraints)
 
 
 @st.composite
 def nodes(draw, *, was_forced=None, choice_types=None):
     if choice_types is None:
-        (choice_type, kwargs) = draw(choice_types_kwargs())
+        (choice_type, constraints) = draw(choice_types_constraints())
     else:
         choice_type = draw(st.sampled_from(choice_types))
-        kwargs = draw(kwargs_strategy(choice_type))
-    # ir nodes don't include forced in their kwargs. see was_forced attribute
-    del kwargs["forced"]
-    value = draw_value(choice_type, kwargs)
+        constraints = draw(constraints_strategy(choice_type))
+    # ir nodes don't include forced in their constraints. see was_forced attribute
+    del constraints["forced"]
+    value = draw_value(choice_type, constraints)
     was_forced = draw(st.booleans()) if was_forced is None else was_forced
 
     return ChoiceNode(
-        type=choice_type, value=value, kwargs=kwargs, was_forced=was_forced
+        type=choice_type, value=value, constraints=constraints, was_forced=was_forced
     )
 
 
 def ir(*values: list[ChoiceT]) -> list[ChoiceNode]:
     """
     For inline-creating an ir node or list of ir nodes, where you don't care about the
-    kwargs. This uses maximally-permissable kwargs and infers the choice_type you meant
+    constraints. This uses maximally-permissable constraints and infers the choice_type you meant
     based on the type of the value.
 
-    You can optionally pass (value, kwargs) to as an element in order to override
-    the default kwargs for that element.
+    You can optionally pass (value, constraints) to as an element in order to override
+    the default constraints for that element.
     """
     mapping = {
         float: (
@@ -447,19 +450,19 @@ def ir(*values: list[ChoiceT]) -> list[ChoiceNode]:
     }
     nodes = []
     for value in values:
-        override_kwargs = {}
+        override_constraints = {}
         if isinstance(value, tuple):
-            (value, override_kwargs) = value
-            if override_kwargs is None:
-                override_kwargs = {}
+            (value, override_constraints) = value
+            if override_constraints is None:
+                override_constraints = {}
 
-        (choice_type, kwargs) = mapping[type(value)]
+        (choice_type, constraints) = mapping[type(value)]
 
         nodes.append(
             ChoiceNode(
                 type=choice_type,
                 value=value,
-                kwargs=kwargs | override_kwargs,
+                constraints=constraints | override_constraints,
                 was_forced=False,
             )
         )
@@ -467,7 +470,7 @@ def ir(*values: list[ChoiceT]) -> list[ChoiceNode]:
     return tuple(nodes)
 
 
-def float_kw(
+def float_constr(
     min_value=-math.inf,
     max_value=math.inf,
     *,
@@ -482,7 +485,7 @@ def float_kw(
     }
 
 
-def integer_kw(min_value=None, max_value=None, *, weights=None, shrink_towards=0):
+def integer_constr(min_value=None, max_value=None, *, weights=None, shrink_towards=0):
     return {
         "min_value": min_value,
         "max_value": max_value,
@@ -491,7 +494,7 @@ def integer_kw(min_value=None, max_value=None, *, weights=None, shrink_towards=0
     }
 
 
-def string_kw(intervals, *, min_size=0, max_size=COLLECTION_DEFAULT_MAX_SIZE):
+def string_constr(intervals, *, min_size=0, max_size=COLLECTION_DEFAULT_MAX_SIZE):
     return {"intervals": intervals, "min_size": min_size, "max_size": max_size}
 
 
diff --git a/hypothesis-python/tests/conjecture/test_alt_backend.py b/hypothesis-python/tests/conjecture/test_alt_backend.py
index e8e8fbe07d..b6f9593e08 100644
--- a/hypothesis-python/tests/conjecture/test_alt_backend.py
+++ b/hypothesis-python/tests/conjecture/test_alt_backend.py
@@ -255,7 +255,7 @@ def test_backend_can_shrink_floats():
 @given(nodes())
 def test_new_conjecture_data_with_backend(node):
     def test(data):
-        getattr(data, f"draw_{node.type}")(**node.kwargs)
+        getattr(data, f"draw_{node.type}")(**node.constraints)
 
     with temp_register_backend("prng", PrngProvider):
         runner = ConjectureRunner(test, settings=settings(backend="prng"))
@@ -264,19 +264,19 @@ def test(data):
 
 # trivial provider for tests which don't care about drawn distributions.
 class TrivialProvider(PrimitiveProvider):
-    def draw_integer(self, *args, **kwargs):
+    def draw_integer(self, *args, **constraints):
         return 1
 
-    def draw_boolean(self, *args, **kwargs):
+    def draw_boolean(self, *args, **constraints):
         return True
 
-    def draw_float(self, *args, **kwargs):
+    def draw_float(self, *args, **constraints):
         return 1.0
 
-    def draw_bytes(self, *args, **kwargs):
+    def draw_bytes(self, *args, **constraints):
         return b""
 
-    def draw_string(self, *args, **kwargs):
+    def draw_string(self, *args, **constraints):
         return ""
 
 
@@ -509,7 +509,7 @@ def __init__(self, conjecturedata: "ConjectureData", /) -> None:
         super().__init__(conjecturedata)
         self._it = itertools.cycle([1, 1, 1, "discard_test_case", "other"])
 
-    def draw_integer(self, *args, **kwargs):
+    def draw_integer(self, *args, **constraints):
         x = next(self._it)
         if isinstance(x, str):
             raise BackendCannotProceed(x)
@@ -554,7 +554,7 @@ def __init__(self, conjecturedata: "ConjectureData", /) -> None:
         super().__init__(conjecturedata)
         self._calls = 0
 
-    def draw_integer(self, *args, **kwargs):
+    def draw_integer(self, *args, **constraints):
         self._calls += 1
         if self._calls > 20:
             # This is complete nonsense of course, so we'll see Hypothesis complain
diff --git a/hypothesis-python/tests/conjecture/test_data_tree.py b/hypothesis-python/tests/conjecture/test_data_tree.py
index c1f15879dd..d19bfbf932 100644
--- a/hypothesis-python/tests/conjecture/test_data_tree.py
+++ b/hypothesis-python/tests/conjecture/test_data_tree.py
@@ -28,11 +28,11 @@
 from hypothesis.vendor import pretty
 
 from tests.conjecture.common import (
-    boolean_kwargs,
+    boolean_constraints,
+    constraints_strategy,
     fresh_data,
-    integer_kwargs,
+    integer_constraints,
     interesting_origin,
-    kwargs_strategy,
     nodes,
     run_to_nodes,
 )
@@ -148,7 +148,7 @@ def runner(data):
         data.mark_interesting()
 
     root = runner.tree.root
-    assert len(root.kwargs) == 10
+    assert len(root.constraints) == 10
     assert len(root.values) == 10
     assert root.transition.status == Status.INTERESTING
 
@@ -162,7 +162,7 @@ def runner(data):
         data.mark_interesting()
 
     root = runner.tree.root
-    assert len(root.kwargs) == len(root.values) == 1
+    assert len(root.constraints) == len(root.values) == 1
     assert list(root.transition.children[0].values) == [2]
     assert list(root.transition.children[1].values) == [3]
 
@@ -406,18 +406,18 @@ def test_low_probabilities_are_still_explored():
 
 
 def _test_observed_draws_are_recorded_in_tree(choice_type):
-    @given(kwargs_strategy(choice_type))
-    def test(kwargs):
+    @given(constraints_strategy(choice_type))
+    def test(constraints):
         # we currently split pseudo-choices with a single child into their
         # own transition, which clashes with our asserts below. If we ever
         # change this (say, by not writing pseudo choices to the ir at all),
         # this restriction can be relaxed.
-        assume(compute_max_children(choice_type, kwargs) > 1)
+        assume(compute_max_children(choice_type, constraints) > 1)
 
         tree = DataTree()
         data = fresh_data(observer=tree.new_observer())
         draw_func = getattr(data, f"draw_{choice_type}")
-        draw_func(**kwargs)
+        draw_func(**constraints)
 
         assert tree.root.transition is None
         assert tree.root.choice_types == [choice_type]
@@ -426,18 +426,18 @@ def test(kwargs):
 
 
 def _test_non_observed_draws_are_not_recorded_in_tree(choice_type):
-    @given(kwargs_strategy(choice_type))
-    def test(kwargs):
-        assume(compute_max_children(choice_type, kwargs) > 1)
+    @given(constraints_strategy(choice_type))
+    def test(constraints):
+        assume(compute_max_children(choice_type, constraints) > 1)
 
         tree = DataTree()
         data = fresh_data(observer=tree.new_observer())
         draw_func = getattr(data, f"draw_{choice_type}")
-        draw_func(**kwargs, observe=False)
+        draw_func(**constraints, observe=False)
 
         root = tree.root
         assert root.transition is None
-        assert root.kwargs == root.values == root.choice_types == []
+        assert root.constraints == root.values == root.choice_types == []
 
     test()
 
@@ -515,44 +515,44 @@ def nodes(data):
         assert data.draw_float(min_value, max_value, allow_nan=False) == expected_value
 
 
-@given(boolean_kwargs(), integer_kwargs())
-def test_datatree_repr(bool_kwargs, int_kwargs):
+@given(boolean_constraints(), integer_constraints())
+def test_datatree_repr(bool_constraints, int_constraints):
     tree = DataTree()
 
     origin = interesting_origin()
 
     observer = tree.new_observer()
-    observer.draw_boolean(True, was_forced=False, kwargs=bool_kwargs)
+    observer.draw_boolean(True, was_forced=False, constraints=bool_constraints)
     observer.conclude_test(Status.INVALID, interesting_origin=None)
 
     observer = tree.new_observer()
-    observer.draw_boolean(False, was_forced=False, kwargs=bool_kwargs)
-    observer.draw_integer(42, was_forced=False, kwargs=int_kwargs)
+    observer.draw_boolean(False, was_forced=False, constraints=bool_constraints)
+    observer.draw_integer(42, was_forced=False, constraints=int_constraints)
     observer.conclude_test(Status.VALID, interesting_origin=None)
 
     observer = tree.new_observer()
-    observer.draw_boolean(False, was_forced=False, kwargs=bool_kwargs)
-    observer.draw_integer(0, was_forced=False, kwargs=int_kwargs)
-    observer.draw_boolean(False, was_forced=True, kwargs=bool_kwargs)
+    observer.draw_boolean(False, was_forced=False, constraints=bool_constraints)
+    observer.draw_integer(0, was_forced=False, constraints=int_constraints)
+    observer.draw_boolean(False, was_forced=True, constraints=bool_constraints)
     observer.conclude_test(Status.INTERESTING, interesting_origin=origin)
 
     observer = tree.new_observer()
-    observer.draw_boolean(False, was_forced=False, kwargs=bool_kwargs)
-    observer.draw_integer(5, was_forced=False, kwargs=int_kwargs)
+    observer.draw_boolean(False, was_forced=False, constraints=bool_constraints)
+    observer.draw_integer(5, was_forced=False, constraints=int_constraints)
 
     assert (
         pretty.pretty(tree)
         == textwrap.dedent(
             f"""
-        boolean True {bool_kwargs}
+        boolean True {bool_constraints}
           Conclusion (Status.INVALID)
-        boolean False {bool_kwargs}
-          integer 42 {int_kwargs}
+        boolean False {bool_constraints}
+          integer 42 {int_constraints}
             Conclusion (Status.VALID)
-          integer 0 {int_kwargs}
-            boolean False [forced] {bool_kwargs}
+          integer 0 {int_constraints}
+            boolean False [forced] {bool_constraints}
               Conclusion (Status.INTERESTING, {origin})
-          integer 5 {int_kwargs}
+          integer 5 {int_constraints}
             unknown
         """
         ).strip()
@@ -560,7 +560,7 @@ def test_datatree_repr(bool_kwargs, int_kwargs):
 
 
 def _draw(data, node, *, forced=None):
-    return getattr(data, f"draw_{node.type}")(**node.kwargs, forced=forced)
+    return getattr(data, f"draw_{node.type}")(**node.constraints, forced=forced)
 
 
 @given(nodes(was_forced=True, choice_types=["float"]))
@@ -568,7 +568,7 @@ def test_simulate_forced_floats(node):
     tree = DataTree()
 
     data = ConjectureData.for_choices([node.value], observer=tree.new_observer())
-    data.draw_float(**node.kwargs, forced=node.value)
+    data.draw_float(**node.constraints, forced=node.value)
     with pytest.raises(StopTest):
         data.conclude_test(Status.VALID)
 
diff --git a/hypothesis-python/tests/conjecture/test_engine.py b/hypothesis-python/tests/conjecture/test_engine.py
index 258f216775..54ab46cbec 100644
--- a/hypothesis-python/tests/conjecture/test_engine.py
+++ b/hypothesis-python/tests/conjecture/test_engine.py
@@ -1539,12 +1539,12 @@ def test_too_slow_report():
 
 
 def _draw(cd, node):
-    return getattr(cd, f"draw_{node.type}")(**node.kwargs)
+    return getattr(cd, f"draw_{node.type}")(**node.constraints)
 
 
 @given(nodes(was_forced=False))
 def test_overruns_with_extend_are_not_cached(node):
-    assume(compute_max_children(node.type, node.kwargs) > 100)
+    assume(compute_max_children(node.type, node.constraints) > 100)
 
     def test(cd):
         _draw(cd, node)
diff --git a/hypothesis-python/tests/conjecture/test_float_encoding.py b/hypothesis-python/tests/conjecture/test_float_encoding.py
index a4ed92639d..e18ac04aef 100644
--- a/hypothesis-python/tests/conjecture/test_float_encoding.py
+++ b/hypothesis-python/tests/conjecture/test_float_encoding.py
@@ -121,11 +121,11 @@ def test_reverse_bits_table_has_right_elements():
     assert sorted(flt.REVERSE_BITS_TABLE) == list(range(256))
 
 
-def float_runner(start, condition, *, kwargs=None):
-    kwargs = {} if kwargs is None else kwargs
+def float_runner(start, condition, *, constraints=None):
+    constraints = {} if constraints is None else constraints
 
     def test_function(data):
-        f = data.draw_float(**kwargs)
+        f = data.draw_float(**constraints)
         if condition(f):
             data.mark_interesting()
 
@@ -135,8 +135,8 @@ def test_function(data):
     return runner
 
 
-def minimal_from(start, condition, *, kwargs=None):
-    runner = float_runner(start, condition, kwargs=kwargs)
+def minimal_from(start, condition, *, constraints=None):
+    runner = float_runner(start, condition, constraints=constraints)
     runner.shrink_interesting_examples()
     (v,) = runner.interesting_examples.values()
     f = v.choices[0]
@@ -198,8 +198,8 @@ def test_does_not_shrink_across_one():
 
 def test_reject_out_of_bounds_floats_while_shrinking():
     # coverage test for rejecting out of bounds floats while shrinking
-    kwargs = {"min_value": 103.0}
-    g = minimal_from(103.1, lambda x: x >= 100, kwargs=kwargs)
+    constraints = {"min_value": 103.0}
+    g = minimal_from(103.1, lambda x: x >= 100, constraints=constraints)
     assert g == 103.0
 
 
diff --git a/hypothesis-python/tests/conjecture/test_forced.py b/hypothesis-python/tests/conjecture/test_forced.py
index f70bedf46c..3a467606c5 100644
--- a/hypothesis-python/tests/conjecture/test_forced.py
+++ b/hypothesis-python/tests/conjecture/test_forced.py
@@ -19,7 +19,7 @@
 from hypothesis.internal.conjecture.data import ConjectureData
 from hypothesis.internal.floats import SIGNALING_NAN, SMALLEST_SUBNORMAL
 
-from tests.conjecture.common import choice_types_kwargs, fresh_data
+from tests.conjecture.common import choice_types_constraints, fresh_data
 
 
 @given(st.data())
@@ -130,26 +130,26 @@ def test_forced_many(data):
         {"min_value": -1 * math.inf, "max_value": -1 * math.inf, "forced": math.nan},
     )
 )
-@given(choice_types_kwargs(use_forced=True))
-def test_forced_values(choice_type_and_kwargs):
-    (choice_type, kwargs) = choice_type_and_kwargs
+@given(choice_types_constraints(use_forced=True))
+def test_forced_values(choice_type_and_constraints):
+    (choice_type, constraints) = choice_type_and_constraints
 
     if choice_type == "float":
         # TODO intentionally avoid triggering a bug with forcing nan values
         # while both min and max value have the opposite sign.
         # Once we fix the aforementioned bug we can remove this intentional
         # weakening of the test.
-        assume(not math.isnan(kwargs["forced"]))
+        assume(not math.isnan(constraints["forced"]))
 
-    forced = kwargs["forced"]
+    forced = constraints["forced"]
     data = fresh_data()
-    assert choice_equal(getattr(data, f"draw_{choice_type}")(**kwargs), forced)
+    assert choice_equal(getattr(data, f"draw_{choice_type}")(**constraints), forced)
 
     # now make sure the written buffer reproduces the forced value, even without
     # specifying forced=.
-    del kwargs["forced"]
+    del constraints["forced"]
     data = ConjectureData.for_choices(data.choices)
-    assert choice_equal(getattr(data, f"draw_{choice_type}")(**kwargs), forced)
+    assert choice_equal(getattr(data, f"draw_{choice_type}")(**constraints), forced)
 
 
 @pytest.mark.parametrize("sign", [1, -1])
diff --git a/hypothesis-python/tests/conjecture/test_ir.py b/hypothesis-python/tests/conjecture/test_ir.py
index f1f0cf7937..7628d3530b 100644
--- a/hypothesis-python/tests/conjecture/test_ir.py
+++ b/hypothesis-python/tests/conjecture/test_ir.py
@@ -50,13 +50,13 @@
 
 from tests.common.debug import minimal
 from tests.conjecture.common import (
-    choice_types_kwargs,
+    choice_types_constraints,
     clamped_shrink_towards,
     draw_value,
-    float_kw,
+    float_constr,
     fresh_data,
-    integer_kw,
-    integer_kwargs,
+    integer_constr,
+    integer_constraints,
     nodes,
 )
 
@@ -64,17 +64,17 @@
 # we max out at 128 bit integers in the *unbounded* case, but someone may
 # specify a bound with a larger magnitude. Ensure we calculate max children for
 # those cases correctly.
-@example(("integer", integer_kw(max_value=-(2**200))))
-@example(("integer", integer_kw(min_value=2**200)))
-@example(("integer", integer_kw(-(2**200), 2**200)))
-@given(choice_types_kwargs())
-def test_compute_max_children_is_positive(choice_type_and_kwargs):
-    (choice_type, kwargs) = choice_type_and_kwargs
-    assert compute_max_children(choice_type, kwargs) >= 0
+@example(("integer", integer_constr(max_value=-(2**200))))
+@example(("integer", integer_constr(min_value=2**200)))
+@example(("integer", integer_constr(-(2**200), 2**200)))
+@given(choice_types_constraints())
+def test_compute_max_children_is_positive(choice_type_and_constraints):
+    (choice_type, constraints) = choice_type_and_constraints
+    assert compute_max_children(choice_type, constraints) >= 0
 
 
 @pytest.mark.parametrize(
-    "choice_type, kwargs, count_children",
+    "choice_type, constraints, count_children",
     [
         ("integer", {"min_value": 1, "max_value": 2, "weights": {1: 0.1, 2: 0.1}}, 2),
         # only possibility is the empty string
@@ -150,26 +150,26 @@ def test_compute_max_children_is_positive(choice_type_and_kwargs):
         ("boolean", {"p": 0.5}, 2),
         ("boolean", {"p": 0.001}, 2),
         ("boolean", {"p": 0.999}, 2),
-        ("float", float_kw(0.0, 0.0), 1),
-        ("float", float_kw(-0.0, -0.0), 1),
-        ("float", float_kw(-0.0, 0.0), 2),
-        ("float", float_kw(next_down(-0.0), next_up(0.0)), 4),
+        ("float", float_constr(0.0, 0.0), 1),
+        ("float", float_constr(-0.0, -0.0), 1),
+        ("float", float_constr(-0.0, 0.0), 2),
+        ("float", float_constr(next_down(-0.0), next_up(0.0)), 4),
         (
             "float",
-            float_kw(
+            float_constr(
                 next_down(next_down(-0.0)),
                 next_up(next_up(0.0)),
                 smallest_nonzero_magnitude=next_up(SMALLEST_SUBNORMAL),
             ),
             4,
         ),
-        ("float", float_kw(smallest_nonzero_magnitude=next_down(math.inf)), 6),
-        ("float", float_kw(1, 10, smallest_nonzero_magnitude=11.0), 0),
-        ("float", float_kw(-3, -2, smallest_nonzero_magnitude=4.0), 0),
+        ("float", float_constr(smallest_nonzero_magnitude=next_down(math.inf)), 6),
+        ("float", float_constr(1, 10, smallest_nonzero_magnitude=11.0), 0),
+        ("float", float_constr(-3, -2, smallest_nonzero_magnitude=4.0), 0),
     ],
 )
-def test_compute_max_children(choice_type, kwargs, count_children):
-    assert compute_max_children(choice_type, kwargs) == count_children
+def test_compute_max_children(choice_type, constraints, count_children):
+    assert compute_max_children(choice_type, constraints) == count_children
 
 
 @given(st.text(min_size=1, max_size=1), st.integers(0, 100))
@@ -197,17 +197,17 @@ def test_draw_string_single_interval_with_equal_bounds(s, n):
     )
 )
 # all combinations of float signs
-@example(("float", float_kw(next_down(-0.0), -0.0)))
-@example(("float", float_kw(next_down(-0.0), next_up(0.0))))
-@example(("float", float_kw(0.0, next_up(0.0))))
+@example(("float", float_constr(next_down(-0.0), -0.0)))
+@example(("float", float_constr(next_down(-0.0), next_up(0.0))))
+@example(("float", float_constr(0.0, next_up(0.0))))
 # using a smallest_nonzero_magnitude which happens to filter out everything
-@example(("float", float_kw(1.0, 2.0, smallest_nonzero_magnitude=3.0)))
-@example(("integer", integer_kw(1, 2, weights={1: 0.2, 2: 0.4})))
-@given(choice_types_kwargs())
+@example(("float", float_constr(1.0, 2.0, smallest_nonzero_magnitude=3.0)))
+@example(("integer", integer_constr(1, 2, weights={1: 0.2, 2: 0.4})))
+@given(choice_types_constraints())
 @settings(suppress_health_check=[HealthCheck.filter_too_much])
-def test_compute_max_children_and_all_children_agree(choice_type_and_kwargs):
-    (choice_type, kwargs) = choice_type_and_kwargs
-    max_children = compute_max_children(choice_type, kwargs)
+def test_compute_max_children_and_all_children_agree(choice_type_and_constraints):
+    (choice_type, constraints) = choice_type_and_constraints
+    max_children = compute_max_children(choice_type, constraints)
 
     # avoid slowdowns / OOM when reifying extremely large all_children generators.
     # We also hard cap at MAX_CHILDREN_EFFECTIVELY_INFINITE, because max_children
@@ -215,7 +215,7 @@ def test_compute_max_children_and_all_children_agree(choice_type_and_kwargs):
     # all_children.
     cap = min(100_000, MAX_CHILDREN_EFFECTIVELY_INFINITE)
     assume(max_children < cap)
-    assert len(list(all_children(choice_type, kwargs))) == max_children
+    assert len(list(all_children(choice_type, constraints))) == max_children
 
 
 # it's very hard to test that unbounded integer ranges agree with
@@ -224,10 +224,10 @@ def test_compute_max_children_and_all_children_agree(choice_type_and_kwargs):
 # element is what we expect.
 
 
-@given(integer_kwargs())
-def test_compute_max_children_unbounded_integer_ranges(kwargs):
-    expected = clamped_shrink_towards(kwargs)
-    first = next(all_children("integer", kwargs))
+@given(integer_constraints())
+def test_compute_max_children_unbounded_integer_ranges(constraints):
+    expected = clamped_shrink_towards(constraints)
+    first = next(all_children("integer", constraints))
     assert expected == first, (expected, first)
 
 
@@ -247,18 +247,21 @@ def test_nodes(random):
     data.freeze()
     expected_tree_nodes = (
         ChoiceNode(
-            type="float", value=5.0, kwargs=float_kw(-10.0, 10.0), was_forced=True
+            type="float",
+            value=5.0,
+            constraints=float_constr(-10.0, 10.0),
+            was_forced=True,
         ),
         ChoiceNode(
             type="boolean",
             value=True,
-            kwargs={"p": 0.5},
+            constraints={"p": 0.5},
             was_forced=True,
         ),
         ChoiceNode(
             type="string",
             value="abbcccdddd",
-            kwargs={
+            constraints={
                 "intervals": IntervalSet.from_string("abcd"),
                 "min_size": 0,
                 "max_size": COLLECTION_DEFAULT_MAX_SIZE,
@@ -268,11 +271,14 @@ def test_nodes(random):
         ChoiceNode(
             type="bytes",
             value=bytes(8),
-            kwargs={"min_size": 8, "max_size": 8},
+            constraints={"min_size": 8, "max_size": 8},
             was_forced=True,
         ),
         ChoiceNode(
-            type="integer", value=50, kwargs=integer_kw(0, 100), was_forced=True
+            type="integer",
+            value=50,
+            constraints=integer_constr(0, 100),
+            was_forced=True,
         ),
     )
     assert data.nodes == expected_tree_nodes
@@ -283,7 +289,7 @@ def test_copy_choice_node(node):
     assert node == node
 
     assume(not node.was_forced)
-    new_value = draw_value(node.type, node.kwargs)
+    new_value = draw_value(node.type, node.constraints)
     # if we drew the same value as before, the node should still be equal
     assert (node.copy(with_value=new_value) == node) is (
         choice_equal(new_value, node.value)
@@ -323,31 +329,35 @@ def test_data_with_changed_forced_value(node):
     data = ConjectureData.for_choices([node.value])
 
     draw_func = getattr(data, f"draw_{node.type}")
-    kwargs = deepcopy(node.kwargs)
-    kwargs["forced"] = draw_value(node.type, node.kwargs)
-    assume(not choice_equal(kwargs["forced"], node.value))
+    constraints = deepcopy(node.constraints)
+    constraints["forced"] = draw_value(node.type, node.constraints)
+    assume(not choice_equal(constraints["forced"], node.value))
 
-    assert choice_equal(draw_func(**kwargs), kwargs["forced"])
+    assert choice_equal(draw_func(**constraints), constraints["forced"])
 
 
 # ensure we hit bare-minimum coverage for all ir types.
-@example(ChoiceNode(type="float", value=0.0, kwargs=float_kw(), was_forced=True))
+@example(
+    ChoiceNode(type="float", value=0.0, constraints=float_constr(), was_forced=True)
+)
 @example(
     ChoiceNode(
         type="boolean",
         value=False,
-        kwargs={"p": 0.5},
+        constraints={"p": 0.5},
         was_forced=True,
     )
 )
 @example(
-    ChoiceNode(type="integer", value=50, kwargs=integer_kw(50, 100), was_forced=True)
+    ChoiceNode(
+        type="integer", value=50, constraints=integer_constr(50, 100), was_forced=True
+    )
 )
 @example(
     ChoiceNode(
         type="string",
         value="aaaa",
-        kwargs={
+        constraints={
             "intervals": IntervalSet.from_string("bcda"),
             "min_size": 4,
             "max_size": COLLECTION_DEFAULT_MAX_SIZE,
@@ -359,7 +369,7 @@ def test_data_with_changed_forced_value(node):
     ChoiceNode(
         type="bytes",
         value=bytes(8),
-        kwargs={"min_size": 8, "max_size": 8},
+        constraints={"min_size": 8, "max_size": 8},
         was_forced=True,
     )
 )
@@ -372,42 +382,42 @@ def test_data_with_same_forced_value_is_valid(node):
     data = ConjectureData.for_choices([node.value])
     draw_func = getattr(data, f"draw_{node.type}")
 
-    kwargs = deepcopy(node.kwargs)
-    kwargs["forced"] = node.value
-    assert choice_equal(draw_func(**kwargs), kwargs["forced"])
+    constraints = deepcopy(node.constraints)
+    constraints["forced"] = node.value
+    assert choice_equal(draw_func(**constraints), constraints["forced"])
 
 
-@given(choice_types_kwargs())
+@given(choice_types_constraints())
 @settings(suppress_health_check=[HealthCheck.filter_too_much])
-def test_all_children_are_permitted_values(choice_type_and_kwargs):
-    (choice_type, kwargs) = choice_type_and_kwargs
-    max_children = compute_max_children(choice_type, kwargs)
+def test_all_children_are_permitted_values(choice_type_and_constraints):
+    (choice_type, constraints) = choice_type_and_constraints
+    max_children = compute_max_children(choice_type, constraints)
 
     cap = min(100_000, MAX_CHILDREN_EFFECTIVELY_INFINITE)
     assume(max_children < cap)
 
     # test that all_children -> choice_permitted (but not necessarily the converse.)
-    for value in all_children(choice_type, kwargs):
-        assert choice_permitted(value, kwargs), value
+    for value in all_children(choice_type, constraints):
+        assert choice_permitted(value, constraints), value
 
 
 @pytest.mark.parametrize(
-    "value, kwargs, permitted",
+    "value, constraints, permitted",
     [
-        (0, integer_kw(1, 2), False),
-        (2, integer_kw(0, 1), False),
-        (10, integer_kw(0, 20), True),
-        (int(2**128 / 2) - 1, integer_kw(), True),
-        (int(2**128 / 2), integer_kw(), True),
-        (math.nan, float_kw(0.0, 0.0), True),
-        (math.nan, float_kw(0.0, 0.0, allow_nan=False), False),
-        (2.0, float_kw(1.0, 3.0, smallest_nonzero_magnitude=2.5), False),
+        (0, integer_constr(1, 2), False),
+        (2, integer_constr(0, 1), False),
+        (10, integer_constr(0, 20), True),
+        (int(2**128 / 2) - 1, integer_constr(), True),
+        (int(2**128 / 2), integer_constr(), True),
+        (math.nan, float_constr(0.0, 0.0), True),
+        (math.nan, float_constr(0.0, 0.0, allow_nan=False), False),
+        (2.0, float_constr(1.0, 3.0, smallest_nonzero_magnitude=2.5), False),
         (
             -2.0,
-            float_kw(-3.0, -1.0, smallest_nonzero_magnitude=2.5),
+            float_constr(-3.0, -1.0, smallest_nonzero_magnitude=2.5),
             False,
         ),
-        (1.0, float_kw(1.0, 1.0), True),
+        (1.0, float_constr(1.0, 1.0), True),
         (
             "abcd",
             {
@@ -448,8 +458,8 @@ def test_all_children_are_permitted_values(choice_type_and_kwargs):
         (False, {"p": 0.5}, True),
     ],
 )
-def test_choice_permitted(value, kwargs, permitted):
-    assert choice_permitted(value, kwargs) == permitted
+def test_choice_permitted(value, constraints, permitted):
+    assert choice_permitted(value, constraints) == permitted
 
 
 @given(nodes(was_forced=True))
@@ -461,19 +471,33 @@ def test_forced_nodes_are_trivial(node):
     "node",
     [
         ChoiceNode(
-            type="float", value=5.0, kwargs=float_kw(5.0, 10.0), was_forced=False
+            type="float",
+            value=5.0,
+            constraints=float_constr(5.0, 10.0),
+            was_forced=False,
         ),
         ChoiceNode(
-            type="float", value=0.0, kwargs=float_kw(-5.0, 5.0), was_forced=False
+            type="float",
+            value=0.0,
+            constraints=float_constr(-5.0, 5.0),
+            was_forced=False,
+        ),
+        ChoiceNode(
+            type="float", value=0.0, constraints=float_constr(), was_forced=False
+        ),
+        ChoiceNode(
+            type="boolean", value=False, constraints={"p": 0.5}, was_forced=False
+        ),
+        ChoiceNode(
+            type="boolean", value=True, constraints={"p": 1.0}, was_forced=False
+        ),
+        ChoiceNode(
+            type="boolean", value=False, constraints={"p": 0.0}, was_forced=False
         ),
-        ChoiceNode(type="float", value=0.0, kwargs=float_kw(), was_forced=False),
-        ChoiceNode(type="boolean", value=False, kwargs={"p": 0.5}, was_forced=False),
-        ChoiceNode(type="boolean", value=True, kwargs={"p": 1.0}, was_forced=False),
-        ChoiceNode(type="boolean", value=False, kwargs={"p": 0.0}, was_forced=False),
         ChoiceNode(
             type="string",
             value="",
-            kwargs={
+            constraints={
                 "intervals": IntervalSet.from_string("abcd"),
                 "min_size": 0,
                 "max_size": COLLECTION_DEFAULT_MAX_SIZE,
@@ -483,7 +507,7 @@ def test_forced_nodes_are_trivial(node):
         ChoiceNode(
             type="string",
             value="aaaa",
-            kwargs={
+            constraints={
                 "intervals": IntervalSet.from_string("bcda"),
                 "min_size": 4,
                 "max_size": COLLECTION_DEFAULT_MAX_SIZE,
@@ -493,56 +517,64 @@ def test_forced_nodes_are_trivial(node):
         ChoiceNode(
             type="bytes",
             value=bytes(8),
-            kwargs={"min_size": 8, "max_size": 8},
+            constraints={"min_size": 8, "max_size": 8},
             was_forced=False,
         ),
         ChoiceNode(
             type="bytes",
             value=bytes(2),
-            kwargs={"min_size": 2, "max_size": COLLECTION_DEFAULT_MAX_SIZE},
+            constraints={"min_size": 2, "max_size": COLLECTION_DEFAULT_MAX_SIZE},
             was_forced=False,
         ),
         ChoiceNode(
-            type="integer", value=50, kwargs=integer_kw(50, 100), was_forced=False
+            type="integer",
+            value=50,
+            constraints=integer_constr(50, 100),
+            was_forced=False,
         ),
         ChoiceNode(
-            type="integer", value=0, kwargs=integer_kw(-10, 10), was_forced=False
+            type="integer",
+            value=0,
+            constraints=integer_constr(-10, 10),
+            was_forced=False,
         ),
         ChoiceNode(
             type="integer",
             value=2,
-            kwargs=integer_kw(-10, 10, shrink_towards=2),
+            constraints=integer_constr(-10, 10, shrink_towards=2),
             was_forced=False,
         ),
         ChoiceNode(
             type="integer",
             value=-10,
-            kwargs=integer_kw(-10, 10, shrink_towards=-12),
+            constraints=integer_constr(-10, 10, shrink_towards=-12),
             was_forced=False,
         ),
         ChoiceNode(
             type="integer",
             value=10,
-            kwargs=integer_kw(-10, 10, shrink_towards=12),
+            constraints=integer_constr(-10, 10, shrink_towards=12),
             was_forced=False,
         ),
-        ChoiceNode(type="integer", value=0, kwargs=integer_kw(), was_forced=False),
+        ChoiceNode(
+            type="integer", value=0, constraints=integer_constr(), was_forced=False
+        ),
         ChoiceNode(
             type="integer",
             value=1,
-            kwargs=integer_kw(min_value=-10, shrink_towards=1),
+            constraints=integer_constr(min_value=-10, shrink_towards=1),
             was_forced=False,
         ),
         ChoiceNode(
             type="integer",
             value=1,
-            kwargs=integer_kw(max_value=10, shrink_towards=1),
+            constraints=integer_constr(max_value=10, shrink_towards=1),
             was_forced=False,
         ),
         ChoiceNode(
             type="integer",
             value=1,
-            kwargs={
+            constraints={
                 "min_value": None,
                 "max_value": None,
                 "weights": None,
@@ -558,7 +590,7 @@ def test_trivial_nodes(node):
     @st.composite
     def values(draw):
         data = draw(st.data()).conjecture_data
-        return getattr(data, f"draw_{node.type}")(**node.kwargs)
+        return getattr(data, f"draw_{node.type}")(**node.constraints)
 
     # if we're trivial, then shrinking should produce the same value.
     assert choice_equal(minimal(values()), node.value)
@@ -568,18 +600,30 @@ def values(draw):
     "node",
     [
         ChoiceNode(
-            type="float", value=6.0, kwargs=float_kw(5.0, 10.0), was_forced=False
+            type="float",
+            value=6.0,
+            constraints=float_constr(5.0, 10.0),
+            was_forced=False,
+        ),
+        ChoiceNode(
+            type="float",
+            value=-5.0,
+            constraints=float_constr(-5.0, 5.0),
+            was_forced=False,
+        ),
+        ChoiceNode(
+            type="float", value=1.0, constraints=float_constr(), was_forced=False
+        ),
+        ChoiceNode(
+            type="boolean", value=True, constraints={"p": 0.5}, was_forced=False
         ),
         ChoiceNode(
-            type="float", value=-5.0, kwargs=float_kw(-5.0, 5.0), was_forced=False
+            type="boolean", value=True, constraints={"p": 0.99}, was_forced=False
         ),
-        ChoiceNode(type="float", value=1.0, kwargs=float_kw(), was_forced=False),
-        ChoiceNode(type="boolean", value=True, kwargs={"p": 0.5}, was_forced=False),
-        ChoiceNode(type="boolean", value=True, kwargs={"p": 0.99}, was_forced=False),
         ChoiceNode(
             type="string",
             value="d",
-            kwargs={
+            constraints={
                 "intervals": IntervalSet.from_string("abcd"),
                 "min_size": 1,
                 "max_size": COLLECTION_DEFAULT_MAX_SIZE,
@@ -589,25 +633,30 @@ def values(draw):
         ChoiceNode(
             type="bytes",
             value=b"\x01",
-            kwargs={"min_size": 1, "max_size": 1},
+            constraints={"min_size": 1, "max_size": 1},
             was_forced=False,
         ),
         ChoiceNode(
             type="bytes",
             value=bytes(1),
-            kwargs={"min_size": 0, "max_size": COLLECTION_DEFAULT_MAX_SIZE},
+            constraints={"min_size": 0, "max_size": COLLECTION_DEFAULT_MAX_SIZE},
             was_forced=False,
         ),
         ChoiceNode(
             type="bytes",
             value=bytes(2),
-            kwargs={"min_size": 1, "max_size": 10},
+            constraints={"min_size": 1, "max_size": 10},
+            was_forced=False,
+        ),
+        ChoiceNode(
+            type="integer",
+            value=-10,
+            constraints=integer_constr(-10, 10),
             was_forced=False,
         ),
         ChoiceNode(
-            type="integer", value=-10, kwargs=integer_kw(-10, 10), was_forced=False
+            type="integer", value=42, constraints=integer_constr(), was_forced=False
         ),
-        ChoiceNode(type="integer", value=42, kwargs=integer_kw(), was_forced=False),
     ],
 )
 def test_nontrivial_nodes(node):
@@ -616,7 +665,7 @@ def test_nontrivial_nodes(node):
     @st.composite
     def values(draw):
         data = draw(st.data()).conjecture_data
-        return getattr(data, f"draw_{node.type}")(**node.kwargs)
+        return getattr(data, f"draw_{node.type}")(**node.constraints)
 
     # if we're nontrivial, then shrinking should produce something different.
     assert not choice_equal(minimal(values()), node.value)
@@ -628,31 +677,31 @@ def values(draw):
         ChoiceNode(
             type="float",
             value=1.5,
-            kwargs=float_kw(1.1, 1.6),
+            constraints=float_constr(1.1, 1.6),
             was_forced=False,
         ),
         ChoiceNode(
             type="float",
             value=float(math.floor(sys.float_info.max)),
-            kwargs=float_kw(sys.float_info.max - 1, math.inf),
+            constraints=float_constr(sys.float_info.max - 1, math.inf),
             was_forced=False,
         ),
         ChoiceNode(
             type="float",
             value=float(math.ceil(-sys.float_info.max)),
-            kwargs=float_kw(-math.inf, -sys.float_info.max + 1),
+            constraints=float_constr(-math.inf, -sys.float_info.max + 1),
             was_forced=False,
         ),
         ChoiceNode(
             type="float",
             value=math.inf,
-            kwargs=float_kw(math.inf, math.inf),
+            constraints=float_constr(math.inf, math.inf),
             was_forced=False,
         ),
         ChoiceNode(
             type="float",
             value=-math.inf,
-            kwargs=float_kw(-math.inf, -math.inf),
+            constraints=float_constr(-math.inf, -math.inf),
             was_forced=False,
         ),
     ],
@@ -665,7 +714,7 @@ def test_conservative_nontrivial_nodes(node):
     @st.composite
     def values(draw):
         data = draw(st.data()).conjecture_data
-        return getattr(data, f"draw_{node.type}")(**node.kwargs)
+        return getattr(data, f"draw_{node.type}")(**node.constraints)
 
     assert choice_equal(minimal(values()), node.value)
 
@@ -711,138 +760,140 @@ def test_node_template_simplest_is_actually_trivial(node):
     # TODO_IR node.trivial is sound but not complete for floats.
     assume(node.type != "float")
     data = ConjectureData.for_choices((ChoiceTemplate("simplest", count=1),))
-    getattr(data, f"draw_{node.type}")(**node.kwargs)
+    getattr(data, f"draw_{node.type}")(**node.constraints)
     assert len(data.nodes) == 1
     assert data.nodes[0].trivial
 
 
-@given(choice_types_kwargs())
+@given(choice_types_constraints())
 @example(("boolean", {"p": 0}))
 @example(("boolean", {"p": 1}))
-def test_choice_indices_are_positive(choice_type_and_kwargs):
-    (choice_type, kwargs) = choice_type_and_kwargs
-    v = draw_value(choice_type, kwargs)
-    assert choice_to_index(v, kwargs) >= 0
+def test_choice_indices_are_positive(choice_type_and_constraints):
+    (choice_type, constraints) = choice_type_and_constraints
+    v = draw_value(choice_type, constraints)
+    assert choice_to_index(v, constraints) >= 0
 
 
-@given(integer_kwargs())
-def test_shrink_towards_has_index_0(kwargs):
-    shrink_towards = clamped_shrink_towards(kwargs)
+@given(integer_constraints())
+def test_shrink_towards_has_index_0(constraints):
+    shrink_towards = clamped_shrink_towards(constraints)
     note({"clamped_shrink_towards": shrink_towards})
-    assert choice_to_index(shrink_towards, kwargs) == 0
-    assert choice_from_index(0, "integer", kwargs) == shrink_towards
+    assert choice_to_index(shrink_towards, constraints) == 0
+    assert choice_from_index(0, "integer", constraints) == shrink_towards
 
 
-@given(choice_types_kwargs())
-def test_choice_to_index_injective(choice_type_and_kwargs):
+@given(choice_types_constraints())
+def test_choice_to_index_injective(choice_type_and_constraints):
     # ir ordering should be injective both ways.
-    (choice_type, kwargs) = choice_type_and_kwargs
+    (choice_type, constraints) = choice_type_and_constraints
     # ...except for floats, which are hard to order bijectively.
     assume(choice_type != "float")
     # cap to 10k so this test finishes in a reasonable amount of time
-    cap = min(compute_max_children(choice_type, kwargs), 10_000)
+    cap = min(compute_max_children(choice_type, constraints), 10_000)
 
     indices = set()
-    for i, choice in enumerate(all_children(choice_type, kwargs)):
+    for i, choice in enumerate(all_children(choice_type, constraints)):
         if i >= cap:
             break
-        index = choice_to_index(choice, kwargs)
+        index = choice_to_index(choice, constraints)
         assert index not in indices
         indices.add(index)
 
 
-@given(choice_types_kwargs())
+@given(choice_types_constraints())
 @example(
     (
         "string",
         {"min_size": 0, "max_size": 10, "intervals": IntervalSet.from_string("a")},
     )
 )
-def test_choice_from_value_injective(choice_type_and_kwargs):
-    (choice_type, kwargs) = choice_type_and_kwargs
+def test_choice_from_value_injective(choice_type_and_constraints):
+    (choice_type, constraints) = choice_type_and_constraints
     assume(choice_type != "float")
-    cap = min(compute_max_children(choice_type, kwargs), 10_000)
+    cap = min(compute_max_children(choice_type, constraints), 10_000)
 
     choices = set()
     for index in range(cap):
-        choice = choice_from_index(index, choice_type, kwargs)
+        choice = choice_from_index(index, choice_type, constraints)
         assert choice not in choices
         choices.add(choice)
 
 
-@given(choice_types_kwargs())
-def test_choice_index_and_value_are_inverses(choice_type_and_kwargs):
-    (choice_type, kwargs) = choice_type_and_kwargs
-    v = draw_value(choice_type, kwargs)
-    index = choice_to_index(v, kwargs)
+@given(choice_types_constraints())
+def test_choice_index_and_value_are_inverses(choice_type_and_constraints):
+    (choice_type, constraints) = choice_type_and_constraints
+    v = draw_value(choice_type, constraints)
+    index = choice_to_index(v, constraints)
     note({"v": v, "index": index})
-    choice_equal(choice_from_index(index, choice_type, kwargs), v)
+    choice_equal(choice_from_index(index, choice_type, constraints), v)
 
 
 @pytest.mark.parametrize(
-    "choice_type, kwargs, choices",
+    "choice_type, constraints, choices",
     [
         ("boolean", {"p": 1}, [True]),
         ("boolean", {"p": 0}, [False]),
-        ("integer", integer_kw(min_value=1, shrink_towards=4), range(1, 10)),
-        ("integer", integer_kw(max_value=5, shrink_towards=2), range(-10, 5 + 1)),
-        ("integer", integer_kw(max_value=5), range(-10, 5 + 1)),
-        ("integer", integer_kw(min_value=0, shrink_towards=1), range(10)),
-        ("integer", integer_kw(-5, 5, shrink_towards=3), range(-5, 5 + 1)),
-        ("integer", integer_kw(-5, 5, shrink_towards=-3), range(-5, 5 + 1)),
+        ("integer", integer_constr(min_value=1, shrink_towards=4), range(1, 10)),
+        ("integer", integer_constr(max_value=5, shrink_towards=2), range(-10, 5 + 1)),
+        ("integer", integer_constr(max_value=5), range(-10, 5 + 1)),
+        ("integer", integer_constr(min_value=0, shrink_towards=1), range(10)),
+        ("integer", integer_constr(-5, 5, shrink_towards=3), range(-5, 5 + 1)),
+        ("integer", integer_constr(-5, 5, shrink_towards=-3), range(-5, 5 + 1)),
         (
             "float",
-            float_kw(1.0, next_up(next_up(1.0))),
+            float_constr(1.0, next_up(next_up(1.0))),
             [1.0, next_up(1.0), next_up(next_up(1.0))],
         ),
         (
             "float",
-            float_kw(next_down(-0.0), next_up(0.0)),
+            float_constr(next_down(-0.0), next_up(0.0)),
             [next_down(-0.0), -0.0, 0.0, next_up(0.0)],
         ),
     ],
 )
-def test_choice_index_and_value_are_inverses_explicit(choice_type, kwargs, choices):
+def test_choice_index_and_value_are_inverses_explicit(
+    choice_type, constraints, choices
+):
     for choice in choices:
-        index = choice_to_index(choice, kwargs)
-        assert choice_equal(choice_from_index(index, choice_type, kwargs), choice)
+        index = choice_to_index(choice, constraints)
+        assert choice_equal(choice_from_index(index, choice_type, constraints), choice)
 
 
 @pytest.mark.parametrize(
-    "kwargs, choices",
+    "constraints, choices",
     [
         # unbounded
-        (integer_kw(), (0, 1, -1, 2, -2, 3, -3)),
-        (integer_kw(shrink_towards=2), (2, 3, 1, 4, 0, 5, -1, 6, -2)),
+        (integer_constr(), (0, 1, -1, 2, -2, 3, -3)),
+        (integer_constr(shrink_towards=2), (2, 3, 1, 4, 0, 5, -1, 6, -2)),
         # semibounded (below)
-        (integer_kw(min_value=3), (3, 4, 5, 6, 7)),
-        (integer_kw(min_value=3, shrink_towards=5), (5, 6, 4, 7, 3, 8, 9)),
-        (integer_kw(min_value=-3), (0, 1, -1, 2, -2, 3, -3, 4, 5, 6)),
-        (integer_kw(min_value=-3, shrink_towards=-1), (-1, 0, -2, 1, -3, 2, 3, 4)),
+        (integer_constr(min_value=3), (3, 4, 5, 6, 7)),
+        (integer_constr(min_value=3, shrink_towards=5), (5, 6, 4, 7, 3, 8, 9)),
+        (integer_constr(min_value=-3), (0, 1, -1, 2, -2, 3, -3, 4, 5, 6)),
+        (integer_constr(min_value=-3, shrink_towards=-1), (-1, 0, -2, 1, -3, 2, 3, 4)),
         # semibounded (above)
-        (integer_kw(max_value=3), (0, 1, -1, 2, -2, 3, -3, -4, -5, -6)),
-        (integer_kw(max_value=3, shrink_towards=1), (1, 2, 0, 3, -1, -2, -3, -4)),
-        (integer_kw(max_value=-3), (-3, -4, -5, -6, -7)),
-        (integer_kw(max_value=-3, shrink_towards=-5), (-5, -4, -6, -3, -7, -8, -9)),
+        (integer_constr(max_value=3), (0, 1, -1, 2, -2, 3, -3, -4, -5, -6)),
+        (integer_constr(max_value=3, shrink_towards=1), (1, 2, 0, 3, -1, -2, -3, -4)),
+        (integer_constr(max_value=-3), (-3, -4, -5, -6, -7)),
+        (integer_constr(max_value=-3, shrink_towards=-5), (-5, -4, -6, -3, -7, -8, -9)),
         # bounded
-        (integer_kw(-3, 3), (0, 1, -1, 2, -2, 3, -3)),
-        (integer_kw(-3, 3, shrink_towards=1), (1, 2, 0, 3, -1, -2, -3)),
-        (integer_kw(-3, 3, shrink_towards=-1), (-1, 0, -2, 1, -3, 2, 3)),
+        (integer_constr(-3, 3), (0, 1, -1, 2, -2, 3, -3)),
+        (integer_constr(-3, 3, shrink_towards=1), (1, 2, 0, 3, -1, -2, -3)),
+        (integer_constr(-3, 3, shrink_towards=-1), (-1, 0, -2, 1, -3, 2, 3)),
     ],
     ids=repr,
 )
-def test_integer_choice_index(kwargs, choices):
+def test_integer_choice_index(constraints, choices):
     # explicit test which checks that the order of `choices` matches the index
     # order.
     for i, choice in enumerate(choices):
-        assert choice_to_index(choice, kwargs) == i
+        assert choice_to_index(choice, constraints) == i
 
 
 @given(st.lists(nodes()))
 def test_drawing_directly_matches_for_choices(nodes):
     data = ConjectureData.for_choices([n.value for n in nodes])
     for node in nodes:
-        value = getattr(data, f"draw_{node.type}")(**node.kwargs)
+        value = getattr(data, f"draw_{node.type}")(**node.constraints)
         assert choice_equal(node.value, value)
 
 
diff --git a/hypothesis-python/tests/conjecture/test_optimiser.py b/hypothesis-python/tests/conjecture/test_optimiser.py
index 06a67c70f4..f7689a7db2 100644
--- a/hypothesis-python/tests/conjecture/test_optimiser.py
+++ b/hypothesis-python/tests/conjecture/test_optimiser.py
@@ -20,7 +20,12 @@
 from hypothesis.internal.entropy import deterministic_PRNG
 from hypothesis.internal.intervalsets import IntervalSet
 
-from tests.conjecture.common import TEST_SETTINGS, buffer_size_limit, integer_kw, nodes
+from tests.conjecture.common import (
+    TEST_SETTINGS,
+    buffer_size_limit,
+    integer_constr,
+    nodes,
+)
 
 
 def test_optimises_to_maximum():
@@ -230,7 +235,7 @@ def test(data):
     ChoiceNode(
         type="bytes",
         value=b"\xb1",
-        kwargs={"min_size": 1, "max_size": 1},
+        constraints={"min_size": 1, "max_size": 1},
         was_forced=False,
     )
 )
@@ -238,7 +243,7 @@ def test(data):
     ChoiceNode(
         type="string",
         value="aaaa",
-        kwargs={
+        constraints={
             "min_size": 0,
             "max_size": 10,
             "intervals": IntervalSet.from_string("abcd"),
@@ -247,10 +252,12 @@ def test(data):
     )
 )
 @example(
-    ChoiceNode(type="integer", value=1, kwargs=integer_kw(0, 200), was_forced=False)
+    ChoiceNode(
+        type="integer", value=1, constraints=integer_constr(0, 200), was_forced=False
+    )
 )
 def test_optimising_all_nodes(node):
-    assume(compute_max_children(node.type, node.kwargs) > 50)
+    assume(compute_max_children(node.type, node.constraints) > 50)
     size_function = {
         "integer": lambda n: n,
         "float": lambda f: f if math.isfinite(f) else 0,
@@ -261,7 +268,7 @@ def test_optimising_all_nodes(node):
     with deterministic_PRNG():
 
         def test(data):
-            v = getattr(data, f"draw_{node.type}")(**node.kwargs)
+            v = getattr(data, f"draw_{node.type}")(**node.constraints)
             data.target_observations["v"] = size_function[node.type](v)
 
         runner = ConjectureRunner(
diff --git a/hypothesis-python/tests/conjecture/test_provider_contract.py b/hypothesis-python/tests/conjecture/test_provider_contract.py
index 2aba2019e9..632ff6f9ab 100644
--- a/hypothesis-python/tests/conjecture/test_provider_contract.py
+++ b/hypothesis-python/tests/conjecture/test_provider_contract.py
@@ -27,29 +27,29 @@
 from hypothesis.internal.intervalsets import IntervalSet
 
 from tests.conjecture.common import (
-    choice_types_kwargs,
-    float_kw,
-    integer_kw,
+    choice_types_constraints,
+    float_constr,
+    integer_constr,
     nodes,
-    string_kw,
+    string_constr,
 )
 
 
-@example(b"\x00" * 100, [("integer", integer_kw())])
-@example(b"\x00" * 100, [("integer", integer_kw(0, 2))])
-@example(b"\x00" * 100, [("integer", integer_kw(0, 0))])
-@example(b"\x00" * 100, [("integer", integer_kw(min_value=0))])
-@example(b"\x00" * 100, [("integer", integer_kw(max_value=2))])
-@example(b"\x00" * 100, [("integer", integer_kw(0, 2, weights={0: 0.1}))])
+@example(b"\x00" * 100, [("integer", integer_constr())])
+@example(b"\x00" * 100, [("integer", integer_constr(0, 2))])
+@example(b"\x00" * 100, [("integer", integer_constr(0, 0))])
+@example(b"\x00" * 100, [("integer", integer_constr(min_value=0))])
+@example(b"\x00" * 100, [("integer", integer_constr(max_value=2))])
+@example(b"\x00" * 100, [("integer", integer_constr(0, 2, weights={0: 0.1}))])
 @example(b"\x00" * 100, [("boolean", {"p": 1.0})])
 @example(b"\x00" * 100, [("boolean", {"p": 0.0})])
 @example(b"\x00" * 100, [("boolean", {"p": 1e-99})])
-@example(b"\x00" * 100, [("string", string_kw(IntervalSet.from_string("a")))])
-@example(b"\x00" * 100, [("float", float_kw())])
+@example(b"\x00" * 100, [("string", string_constr(IntervalSet.from_string("a")))])
+@example(b"\x00" * 100, [("float", float_constr())])
 @example(b"\x00" * 100, [("bytes", {"min_size": 0, "max_size": 10})])
-@example(b"\x00", [("integer", integer_kw())])
-@given(st.binary(min_size=200), st.lists(choice_types_kwargs()))
-def test_provider_contract_bytestring(bytestring, choice_type_and_kwargs):
+@example(b"\x00", [("integer", integer_constr())])
+@given(st.binary(min_size=200), st.lists(choice_types_constraints()))
+def test_provider_contract_bytestring(bytestring, choice_type_and_constraints):
     data = ConjectureData(
         random=None,
         observer=None,
@@ -57,16 +57,16 @@ def test_provider_contract_bytestring(bytestring, choice_type_and_kwargs):
         provider_kw={"bytestring": bytestring},
     )
 
-    for choice_type, kwargs in choice_type_and_kwargs:
+    for choice_type, constraints in choice_type_and_constraints:
         try:
-            value = getattr(data, f"draw_{choice_type}")(**kwargs)
+            value = getattr(data, f"draw_{choice_type}")(**constraints)
         except StopTest:
             return
 
-        assert choice_permitted(value, kwargs)
-        kwargs["forced"] = choice_from_index(0, choice_type, kwargs)
+        assert choice_permitted(value, constraints)
+        constraints["forced"] = choice_from_index(0, choice_type, constraints)
         assert choice_equal(
-            kwargs["forced"], getattr(data, f"draw_{choice_type}")(**kwargs)
+            constraints["forced"], getattr(data, f"draw_{choice_type}")(**constraints)
         )
 
 
@@ -86,5 +86,5 @@ def test_provider_contract_bytestring(bytestring, choice_type_and_kwargs):
 def test_provider_contract(provider, nodes, random):
     data = ConjectureData(random=random, provider=provider)
     for node in nodes:
-        value = getattr(data, f"draw_{node.type}")(**node.kwargs)
-        assert choice_permitted(value, node.kwargs)
+        value = getattr(data, f"draw_{node.type}")(**node.constraints)
+        assert choice_permitted(value, node.constraints)
diff --git a/hypothesis-python/tests/conjecture/test_shrinker.py b/hypothesis-python/tests/conjecture/test_shrinker.py
index 90f92f9150..814ff60141 100644
--- a/hypothesis-python/tests/conjecture/test_shrinker.py
+++ b/hypothesis-python/tests/conjecture/test_shrinker.py
@@ -28,7 +28,7 @@
 
 from tests.conjecture.common import (
     SOME_LABEL,
-    float_kw,
+    float_constr,
     interesting_origin,
     ir,
     nodes,
@@ -593,13 +593,13 @@ def test_silly_shrinker_subclass():
     ChoiceNode(
         type="float",
         value=float(MAX_PRECISE_INTEGER - 1),
-        kwargs=float_kw(),
+        constraints=float_constr(),
         was_forced=False,
     ),
     ChoiceNode(
         type="float",
         value=float(MAX_PRECISE_INTEGER - 1),
-        kwargs=float_kw(),
+        constraints=float_constr(),
         was_forced=False,
     ),
     0,
@@ -610,15 +610,15 @@ def test_redistribute_numeric_pairs(node1, node2, stop):
     # avoid exhausting the tree while generating, which causes @shrinking_from's
     # runner to raise
     assume(
-        compute_max_children(node1.type, node1.kwargs)
-        + compute_max_children(node2.type, node2.kwargs)
+        compute_max_children(node1.type, node1.constraints)
+        + compute_max_children(node2.type, node2.constraints)
         > 2
     )
 
     @shrinking_from([node1.value, node2.value])
     def shrinker(data: ConjectureData):
-        v1 = getattr(data, f"draw_{node1.type}")(**node1.kwargs)
-        v2 = getattr(data, f"draw_{node2.type}")(**node2.kwargs)
+        v1 = getattr(data, f"draw_{node1.type}")(**node1.constraints)
+        v2 = getattr(data, f"draw_{node2.type}")(**node2.constraints)
         if v1 + v2 > stop:
             data.mark_interesting()
 
diff --git a/hypothesis-python/tests/conjecture/test_test_data.py b/hypothesis-python/tests/conjecture/test_test_data.py
index 7228a9ba38..e838301fa9 100644
--- a/hypothesis-python/tests/conjecture/test_test_data.py
+++ b/hypothesis-python/tests/conjecture/test_test_data.py
@@ -191,10 +191,10 @@ class LoggingObserver(DataObserver):
         def __init__(self):
             self.log = []
 
-        def draw_boolean(self, value: bool, *, was_forced: bool, kwargs: dict):
+        def draw_boolean(self, value: bool, *, was_forced: bool, constraints: dict):
             self.log.append(("draw_boolean", value, was_forced))
 
-        def draw_integer(self, value: int, *, was_forced: bool, kwargs: dict):
+        def draw_integer(self, value: int, *, was_forced: bool, constraints: dict):
             self.log.append(("draw_integer", value, was_forced))
 
         def conclude_test(self, *args):
diff --git a/hypothesis-python/tests/cover/test_float_utils.py b/hypothesis-python/tests/cover/test_float_utils.py
index be9837b23b..d6e6f472e9 100644
--- a/hypothesis-python/tests/cover/test_float_utils.py
+++ b/hypothesis-python/tests/cover/test_float_utils.py
@@ -24,7 +24,7 @@
     sign_aware_lte,
 )
 
-from tests.conjecture.common import float_kw, float_kwargs
+from tests.conjecture.common import float_constr, float_constraints
 
 
 def test_can_handle_straddling_zero():
@@ -50,35 +50,35 @@ def test_next_float_equal(func, val):
 
 
 # exponent comparisons:
-@example(float_kw(1, float_info.max), 0)
-@example(float_kw(1, float_info.max), 1)
-@example(float_kw(1, float_info.max), 10)
-@example(float_kw(1, float_info.max), float_info.max)
-@example(float_kw(1, float_info.max), math.inf)
+@example(float_constr(1, float_info.max), 0)
+@example(float_constr(1, float_info.max), 1)
+@example(float_constr(1, float_info.max), 10)
+@example(float_constr(1, float_info.max), float_info.max)
+@example(float_constr(1, float_info.max), math.inf)
 # mantissa comparisons:
-@example(float_kw(100.0001, 100.0003), 100.0001)
-@example(float_kw(100.0001, 100.0003), 100.0002)
-@example(float_kw(100.0001, 100.0003), 100.0003)
-@example(float_kw(100.0001, 100.0003, allow_nan=False), math.nan)
-@example(float_kw(0, 10, allow_nan=False), math.nan)
-@example(float_kw(0, 10, allow_nan=True), math.nan)
+@example(float_constr(100.0001, 100.0003), 100.0001)
+@example(float_constr(100.0001, 100.0003), 100.0002)
+@example(float_constr(100.0001, 100.0003), 100.0003)
+@example(float_constr(100.0001, 100.0003, allow_nan=False), math.nan)
+@example(float_constr(0, 10, allow_nan=False), math.nan)
+@example(float_constr(0, 10, allow_nan=True), math.nan)
 # the branch coverage of resampling in the "out of range of smallest magnitude" case
 # relies on randomness from the mantissa. try a few different values.
-@example(float_kw(-4, -1, smallest_nonzero_magnitude=4), 4)
-@example(float_kw(-4, -1, smallest_nonzero_magnitude=4), 5)
-@example(float_kw(-4, -1, smallest_nonzero_magnitude=4), 6)
-@example(float_kw(1, 4, smallest_nonzero_magnitude=4), -4)
-@example(float_kw(1, 4, smallest_nonzero_magnitude=4), -5)
-@example(float_kw(1, 4, smallest_nonzero_magnitude=4), -6)
-@example(float_kw(-5e-324, -0.0), 3.0)
-@example(float_kw(0.0, 0.0), -0.0)
-@example(float_kw(-0.0, -0.0), 0.0)
-@given(float_kwargs(), st.floats())
-def test_float_clamper(kwargs, input_value):
-    min_value = kwargs["min_value"]
-    max_value = kwargs["max_value"]
-    allow_nan = kwargs["allow_nan"]
-    smallest_nonzero_magnitude = kwargs["smallest_nonzero_magnitude"]
+@example(float_constr(-4, -1, smallest_nonzero_magnitude=4), 4)
+@example(float_constr(-4, -1, smallest_nonzero_magnitude=4), 5)
+@example(float_constr(-4, -1, smallest_nonzero_magnitude=4), 6)
+@example(float_constr(1, 4, smallest_nonzero_magnitude=4), -4)
+@example(float_constr(1, 4, smallest_nonzero_magnitude=4), -5)
+@example(float_constr(1, 4, smallest_nonzero_magnitude=4), -6)
+@example(float_constr(-5e-324, -0.0), 3.0)
+@example(float_constr(0.0, 0.0), -0.0)
+@example(float_constr(-0.0, -0.0), 0.0)
+@given(float_constraints(), st.floats())
+def test_float_clamper(constraints, input_value):
+    min_value = constraints["min_value"]
+    max_value = constraints["max_value"]
+    allow_nan = constraints["allow_nan"]
+    smallest_nonzero_magnitude = constraints["smallest_nonzero_magnitude"]
     clamper = make_float_clamper(
         min_value,
         max_value,
diff --git a/hypothesis-python/tests/crosshair/test_crosshair.py b/hypothesis-python/tests/crosshair/test_crosshair.py
index 5ee6521248..150721b694 100644
--- a/hypothesis-python/tests/crosshair/test_crosshair.py
+++ b/hypothesis-python/tests/crosshair/test_crosshair.py
@@ -19,7 +19,7 @@
 from hypothesis.vendor.pretty import pretty
 
 from tests.common.utils import capture_observations
-from tests.conjecture.common import float_kw, integer_kw, string_kw
+from tests.conjecture.common import float_constr, integer_constr, string_constr
 
 
 @pytest.mark.parametrize("verbosity", list(Verbosity))
@@ -64,21 +64,21 @@ def f(n):
         f()
 
 
-def count_choices_for(choice_type, kwargs):
+def count_choices_for(choice_type, constraints):
     # returns the number of choices that crosshair makes for this draw, before
     # hypothesis ever has a chance to interact with it.
     provider = CrossHairPrimitiveProvider()
     with provider.per_test_case_context_manager():
         assert len(crosshair.statespace.context_statespace().choices_made) == 0
-        getattr(provider, f"draw_{choice_type}")(**kwargs)
+        getattr(provider, f"draw_{choice_type}")(**constraints)
         return len(crosshair.statespace.context_statespace().choices_made)
 
 
 @pytest.mark.parametrize(
     "strategy, expected_choices",
     [
-        (st.integers(), lambda: count_choices_for("integer", integer_kw())),
-        (st.floats(), lambda: count_choices_for("float", float_kw())),
+        (st.integers(), lambda: count_choices_for("integer", integer_constr())),
+        (st.floats(), lambda: count_choices_for("float", float_constr())),
         (
             st.binary(),
             lambda: count_choices_for(
@@ -89,7 +89,7 @@ def count_choices_for(choice_type, kwargs):
         (
             st.text(),
             lambda: count_choices_for(
-                "string", string_kw(IntervalSet.from_string("a"))
+                "string", string_constr(IntervalSet.from_string("a"))
             ),
         ),
     ],
diff --git a/hypothesis-python/tests/quality/test_poisoned_trees.py b/hypothesis-python/tests/quality/test_poisoned_trees.py
index 969df21a20..d5f19b4d11 100644
--- a/hypothesis-python/tests/quality/test_poisoned_trees.py
+++ b/hypothesis-python/tests/quality/test_poisoned_trees.py
@@ -87,7 +87,7 @@ def test_function(data):
     nodes = [
         node
         for node in data.nodes
-        if node.type == "integer" and node.kwargs["max_value"] == 2**16 - 1
+        if node.type == "integer" and node.constraints["max_value"] == 2**16 - 1
     ]
     assert len(nodes) % 2 == 0
 


Reviews:
user: login: Liam-DeVoe
body: looks great!
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-01T23:17:04Z
user: login: Zac-HD
body: LGTM to merge either as-is, or after comments below.  Thanks, @jobh!
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-02T04:23:36Z
user: login: Liam-DeVoe
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-02T04:27:10Z

Review comments:
diff_hunk: @@ -353,67 +355,68 @@ def float_kwargs(draw, *, use_min_value=None, use_max_value=None, use_forced=Fal
 
 
 @st.composite
-def boolean_kwargs(draw, *, use_forced=False):
+def boolean_constraints(draw, *, use_forced=False):
     forced = draw(st.booleans()) if use_forced else None
     # avoid invalid forced combinations
     p = draw(st.floats(0, 1, exclude_min=forced is True, exclude_max=forced is False))
 
     return {"p": p, "forced": forced}
 
 
-def kwargs_strategy(choice_type, strategy_kwargs=None, *, use_forced=False):
+def constraints_strategy(choice_type, strategy_constraints=None, *, use_forced=False):
     strategy = {
-        "boolean": boolean_kwargs,
-        "integer": integer_kwargs,
-        "float": float_kwargs,
-        "bytes": bytes_kwargs,
-        "string": string_kwargs,
+        "boolean": boolean_constraints,
+        "integer": integer_constraints,
+        "float": float_constraints,
+        "bytes": bytes_constraints,
+        "string": string_constraints,
     }[choice_type]
-    if strategy_kwargs is None:
-        strategy_kwargs = {}
-    return strategy(**strategy_kwargs.get(choice_type, {}), use_forced=use_forced)
+    if strategy_constraints is None:
+        strategy_constraints = {}
+    return strategy(**strategy_constraints.get(choice_type, {}), use_forced=use_forced)
 
 
-def choice_types_kwargs(strategy_kwargs=None, *, use_forced=False):
+def choice_types_constraints(strategy_constraints=None, *, use_forced=False):
     options = ["boolean", "integer", "float", "bytes", "string"]
     return st.one_of(
         st.tuples(
-            st.just(name), kwargs_strategy(name, strategy_kwargs, use_forced=use_forced)
+            st.just(name),
+            constraints_strategy(name, strategy_constraints, use_forced=use_forced),
         )
         for name in options
     )
 
 
-def draw_value(choice_type, kwargs):
+def draw_value(choice_type, constraints):
     data = fresh_data()
-    return getattr(data, f"draw_{choice_type}")(**kwargs)
+    return getattr(data, f"draw_{choice_type}")(**constraints)
 
 
 @st.composite
 def nodes(draw, *, was_forced=None, choice_types=None):
     if choice_types is None:
-        (choice_type, kwargs) = draw(choice_types_kwargs())
+        (choice_type, constraints) = draw(choice_types_constraints())
     else:
         choice_type = draw(st.sampled_from(choice_types))
-        kwargs = draw(kwargs_strategy(choice_type))
-    # ir nodes don't include forced in their kwargs. see was_forced attribute
-    del kwargs["forced"]
-    value = draw_value(choice_type, kwargs)
+        constraints = draw(constraints_strategy(choice_type))
+    # ir nodes don't include forced in their constraints. see was_forced attribute
+    del constraints["forced"]
+    value = draw_value(choice_type, constraints)
     was_forced = draw(st.booleans()) if was_forced is None else was_forced
 
     return ChoiceNode(
-        type=choice_type, value=value, kwargs=kwargs, was_forced=was_forced
+        type=choice_type, value=value, constraints=constraints, was_forced=was_forced
     )
 
 
 def ir(*values: list[ChoiceT]) -> list[ChoiceNode]:
     """
     For inline-creating an ir node or list of ir nodes, where you don't care about the
path: hypothesis-python/tests/conjecture/common.py
user: login: Zac-HD
body: Noting for later: old names, we might want to update.  @tybug fyi.
created_at: 2025-04-02T04:18:10Z
author_association: MEMBER
diff_hunk: @@ -447,27 +450,27 @@ def ir(*values: list[ChoiceT]) -> list[ChoiceNode]:
     }
     nodes = []
     for value in values:
-        override_kwargs = {}
+        override_constraints = {}
         if isinstance(value, tuple):
-            (value, override_kwargs) = value
-            if override_kwargs is None:
-                override_kwargs = {}
+            (value, override_constraints) = value
+            if override_constraints is None:
+                override_constraints = {}
 
-        (choice_type, kwargs) = mapping[type(value)]
+        (choice_type, constraints) = mapping[type(value)]
 
         nodes.append(
             ChoiceNode(
                 type=choice_type,
                 value=value,
-                kwargs=kwargs | override_kwargs,
+                constraints=constraints | override_constraints,
                 was_forced=False,
             )
         )
 
     return tuple(nodes)
 
 
-def float_kw(
+def float_constr(
path: hypothesis-python/tests/conjecture/common.py
user: login: Zac-HD
body: I don't love `_constr` as a suffix, but don't have a better suggestion - `make_x_constraint()` seems too verbose, but using `_constraint` is too similar to the strategy names, so just noting in case anyone else has suggestions I guess.  Maybe even keeping the old name?  (`constraints=float_kw()` seems basically fine to me...)
created_at: 2025-04-02T04:19:02Z
author_association: MEMBER
diff_hunk: @@ -447,27 +450,27 @@ def ir(*values: list[ChoiceT]) -> list[ChoiceNode]:
     }
     nodes = []
     for value in values:
-        override_kwargs = {}
+        override_constraints = {}
         if isinstance(value, tuple):
-            (value, override_kwargs) = value
-            if override_kwargs is None:
-                override_kwargs = {}
+            (value, override_constraints) = value
+            if override_constraints is None:
+                override_constraints = {}
 
-        (choice_type, kwargs) = mapping[type(value)]
+        (choice_type, constraints) = mapping[type(value)]
 
         nodes.append(
             ChoiceNode(
                 type=choice_type,
                 value=value,
-                kwargs=kwargs | override_kwargs,
+                constraints=constraints | override_constraints,
                 was_forced=False,
             )
         )
 
     return tuple(nodes)
 
 
-def float_kw(
+def float_constr(
path: hypothesis-python/tests/conjecture/common.py
user: login: Liam-DeVoe
body: I think `float_kw` and `float_constr` are about equally bad in different ways ("the term `kw` does not match `constraint`", and "constr is a weird suffix"), so I don't have a strong opinion here
created_at: 2025-04-02T04:27:10Z
author_association: MEMBER

PR comments:
user: login: jobh
created_at: 2025-04-01T14:24:28Z
body: I'll postpone formatting changes as they complicate review in this case
author_association: CONTRIBUTOR
user: login: Liam-DeVoe
created_at: 2025-04-01T14:39:55Z
body: I do think `constraints` is a more specific and useful term than `parameters` - my only qualm is that some choice parameters are not actually *constraints* on the distribution, like `draw_boolean(p: float = 0.5)` (sans the p=0 and p=1 case). They overwhelmingly are constraints, though..
author_association: MEMBER
user: login: Zac-HD
created_at: 2025-04-01T15:24:33Z
body: `p=...` totally _is_ a constraint; we just do some additional stuff with the second option in `0`, `(0 .. 1)`, or `1` 😉 
author_association: MEMBER
user: login: jobh
created_at: 2025-04-01T15:40:45Z
body: ... `pseudo_constraints`? More seriously, the term `constraint` is in use in the codebase, pre-PR, in typing mostly I think. Which is a point against it. `distribution_modifiers` is too long, and `distribution` too vague... `modifiers`?

(I'm happy with `constraints`, obviously given this PR, but I don't mind a change either)
author_association: CONTRIBUTOR
user: login: Zac-HD
created_at: 2025-04-01T15:52:54Z
body: (I suggest we just stick with `constraints`)
author_association: MEMBER