Skip to content

Commit

Permalink
Refine concrete regex implementations
Browse files Browse the repository at this point in the history
Primarily, make the regress forms inherit and share most of their
code.
  • Loading branch information
sirosen committed Jan 1, 2025
1 parent a1c3eb9 commit 1435f8a
Showing 1 changed file with 22 additions and 61 deletions.
83 changes: 22 additions & 61 deletions src/check_jsonschema/regex_variants.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,98 +12,54 @@ class RegexVariantName(enum.Enum):
python = "python"


class _ConcreteImplementation(t.Protocol):
def check_format(self, instance: t.Any) -> bool: ...

def pattern_keyword(
self, validator: t.Any, pattern: str, instance: str, schema: t.Any
) -> t.Iterator[jsonschema.ValidationError]: ...

def patternProperties_keyword(
self,
validator: t.Any,
patternProperties: dict[str, t.Any],
instance: dict[str, t.Any],
schema: t.Any,
) -> t.Iterator[jsonschema.ValidationError]: ...


class RegexImplementation:
"""
A high-level interface for getting at the different possible
implementations of regex behaviors.
"""

_real_implementation: _ConcreteImplementation
_concrete: "_ConcreteImplementation"

def __init__(self, variant: RegexVariantName) -> None:
self.variant = variant

if self.variant == RegexVariantName.default:
self._real_implementation = _UnicodeRegressImplementation()
self._concrete = _RegressImplementation()
elif self.variant == RegexVariantName.nonunicode:
self._real_implementation = _NonunicodeRegressImplementation()
self._concrete = _NonunicodeRegressImplementation()
else:
self._real_implementation = _PythonImplementation()
self._concrete = _PythonImplementation()

self.check_format = self._real_implementation.check_format
self.pattern_keyword = self._real_implementation.pattern_keyword
self.patternProperties_keyword = (
self._real_implementation.patternProperties_keyword
)
self.check_format = self._concrete.check_format
self.pattern_keyword = self._concrete.pattern_keyword
self.patternProperties_keyword = self._concrete.patternProperties_keyword


class _UnicodeRegressImplementation:
def check_format(self, instance: t.Any) -> bool:
if not isinstance(instance, str):
return True
try:
regress.Regex(instance, flags="u")
except regress.RegressError:
return False
return True
class _ConcreteImplementation(t.Protocol):
def check_format(self, instance: t.Any) -> bool: ...

def pattern_keyword(
self, validator: t.Any, pattern: str, instance: str, schema: t.Any
) -> t.Iterator[jsonschema.ValidationError]:
if not validator.is_type(instance, "string"):
return

try:
regress_pattern = regress.Regex(pattern, flags="u")
except regress.RegressError:
yield jsonschema.ValidationError(f"pattern {pattern!r} failed to compile")
if not regress_pattern.find(instance):
yield jsonschema.ValidationError(f"{instance!r} does not match {pattern!r}")
) -> t.Iterator[jsonschema.ValidationError]: ...

def patternProperties_keyword(
self,
validator: t.Any,
patternProperties: dict[str, t.Any],
instance: dict[str, t.Any],
schema: t.Any,
) -> t.Iterator[jsonschema.ValidationError]:
if not validator.is_type(instance, "object"):
return
) -> t.Iterator[jsonschema.ValidationError]: ...

for pattern, subschema in patternProperties.items():
regress_pattern = regress.Regex(pattern, flags="u")
for k, v in instance.items():
if regress_pattern.find(k):
yield from validator.descend(
v,
subschema,
path=k,
schema_path=pattern,
)

class _RegressImplementation:
def _compile_pattern(self, pattern: str) -> regress.Regex:
return regress.Regex(pattern, flags="u")

class _NonunicodeRegressImplementation:
def check_format(self, instance: t.Any) -> bool:
if not isinstance(instance, str):
return True
try:
regress.Regex(instance)
self._compile_pattern(instance)
except regress.RegressError:
return False
return True
Expand All @@ -115,7 +71,7 @@ def pattern_keyword(
return

try:
regress_pattern = regress.Regex(pattern)
regress_pattern = self._compile_pattern(pattern)
except regress.RegressError:
yield jsonschema.ValidationError(f"pattern {pattern!r} failed to compile")
if not regress_pattern.find(instance):
Expand All @@ -132,7 +88,7 @@ def patternProperties_keyword(
return

for pattern, subschema in patternProperties.items():
regress_pattern = regress.Regex(pattern)
regress_pattern = self._compile_pattern(pattern)
for k, v in instance.items():
if regress_pattern.find(k):
yield from validator.descend(
Expand All @@ -143,6 +99,11 @@ def patternProperties_keyword(
)


class _NonunicodeRegressImplementation(_RegressImplementation):
def _compile_pattern(self, pattern: str) -> regress.Regex:
return regress.Regex(pattern)


class _PythonImplementation:
def check_format(self, instance: t.Any) -> bool:
if not isinstance(instance, str):
Expand Down

0 comments on commit 1435f8a

Please sign in to comment.