Skip to content

Commit 7162a0d

Browse files
sethfitzvcschapp
authored andcommitted
Convert StrippedConstraint to PatternConstraint subclass
StrippedConstraint now extends PatternConstraint, using the same constructor as every other concrete constraint. Custom validate() and __get_pydantic_json_schema__() eliminated. Uses \Z (absolute end-of-string) instead of $ because Python's $ matches before a trailing \n. JSON schema emission swaps \Z back to $ since ECMA regex treats $ as absolute end-of-string. Signed-off-by: Seth Fitzsimmons <sethfitz@amazon.com>
1 parent c19b6b1 commit 7162a0d

2 files changed

Lines changed: 26 additions & 28 deletions

File tree

packages/overture-schema-system/src/overture/schema/system/field_constraint/string.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -208,23 +208,27 @@ def __init__(self) -> None:
208208
)
209209

210210

211-
class StrippedConstraint(StringConstraint):
212-
"""Allows only strings that have no leading/trailing whitespace."""
211+
class StrippedConstraint(PatternConstraint):
212+
r"""Allows only strings that have no leading/trailing whitespace.
213213
214-
def validate(self, value: str, info: ValidationInfo) -> None:
215-
if value != value.strip():
216-
self._raise_validation_error(
217-
value,
218-
info,
219-
f"String cannot have leading or trailing whitespace: {repr(value)}",
220-
)
214+
Uses ``\Z`` (absolute end-of-string) instead of ``$`` because
215+
Python's ``$`` matches before a trailing ``\n``. ECMA regex (used by
216+
JSON Schema) treats ``$`` as absolute end-of-string, so the JSON
217+
schema output swaps ``\Z`` back to ``$``.
218+
"""
219+
220+
def __init__(self) -> None:
221+
super().__init__(
222+
pattern=r"^(\S(.*\S)?)?\Z",
223+
error_message="String cannot have leading or trailing whitespace: {value}",
224+
description="String with no leading/trailing whitespace",
225+
)
221226

222227
def __get_pydantic_json_schema__(
223228
self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
224229
) -> dict[str, Any]:
225-
json_schema = handler(core_schema)
226-
json_schema["pattern"] = r"^(\S(.*\S)?)?$"
227-
json_schema["description"] = "String with no leading/trailing whitespace"
230+
json_schema = super().__get_pydantic_json_schema__(core_schema, handler)
231+
json_schema["pattern"] = self.pattern.pattern.replace(r"\Z", "$")
228232
return json_schema
229233

230234

packages/overture-schema-system/tests/field_constraint/test_string_constraints.py

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@
7272
["Restaurant", "gas-station", "shopping mall", "category!"],
7373
"Invalid category format",
7474
),
75+
(
76+
StrippedConstraint,
77+
["hello", "hello world", "text with internal spaces", ""],
78+
[" hello", "hello ", "\thello", "hello\n", " hello world "],
79+
"leading or trailing whitespace",
80+
),
7581
]
7682

7783

@@ -174,22 +180,9 @@ class TestModel(BaseModel):
174180
TestModel(pointer=ptr)
175181
assert "JSON Pointer must start" in str(exc_info.value)
176182

177-
def test_whitespace_constraint_valid(self) -> None:
178-
class TestModel(BaseModel):
179-
text: Annotated[str, StrippedConstraint()]
180-
181-
for text in ["hello", "hello world", "text with internal spaces", ""]:
182-
model = TestModel(text=text)
183-
assert model.text == text
184-
185-
def test_whitespace_constraint_invalid(self) -> None:
186-
class TestModel(BaseModel):
187-
text: Annotated[str, StrippedConstraint()]
188-
189-
for text in [" hello", "hello ", "\thello", "hello\n", " hello world "]:
190-
with pytest.raises(ValidationError) as exc_info:
191-
TestModel(text=text)
192-
assert "cannot have leading or trailing whitespace" in str(exc_info.value)
183+
def test_stripped_constraint_pattern_string(self) -> None:
184+
"""Codegen extracts the regex via constraint.pattern.pattern."""
185+
assert StrippedConstraint().pattern.pattern == r"^(\S(.*\S)?)?\Z"
193186

194187

195188
class TestJsonSchemaGeneration:
@@ -294,6 +287,7 @@ class TestPatternConstraintHierarchy:
294287
SnakeCaseConstraint,
295288
PhoneNumberConstraint,
296289
RegionCodeConstraint,
290+
StrippedConstraint,
297291
WikidataIdConstraint,
298292
],
299293
)

0 commit comments

Comments
 (0)