Skip to content

Commit

Permalink
Introduce Noop to make pattern non optional
Browse files Browse the repository at this point in the history
  • Loading branch information
lundberg committed Sep 15, 2022
1 parent f45ad46 commit a2f0e48
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 10 deletions.
2 changes: 1 addition & 1 deletion respx/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ def name(self, name: str) -> None:
raise NotImplementedError("Can't set name on route.")

@property
def pattern(self) -> Optional[Pattern]:
def pattern(self) -> Pattern:
return self._pattern

@pattern.setter
Expand Down
45 changes: 36 additions & 9 deletions respx/patterns.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,26 @@ def __init__(self, value: Any, lookup: Optional[Lookup] = None) -> None:
def __iter__(self):
yield self

def __bool__(self):
return True

def __and__(self, other: "Pattern") -> "Pattern":
if not bool(other):
return self
elif not bool(self):
return other
return _And((self, other))

def __or__(self, other: "Pattern") -> "Pattern":
if not bool(other):
return self
elif not bool(self):
return other
return _Or((self, other))

def __invert__(self):
if not bool(self):
return self
return _Invert(self)

def __repr__(self): # pragma: nocover
Expand Down Expand Up @@ -159,6 +172,22 @@ def _in(self, value: Any) -> Match:
return Match(value in self.value)


class Noop(Pattern):
def __init__(self) -> None:
super().__init__(None)

def __repr__(self):
return f"<{self.__class__.__name__}>"

def __bool__(self) -> bool:
# Treat this pattern as non-existent, e.g. when filtering or conditioning
return False

def match(self, request: httpx.Request) -> Match:
# If this pattern is part of a combined pattern, always be truthy, i.e. noop
return Match(True)


class PathPattern(Pattern):
path: Optional[str]

Expand Down Expand Up @@ -500,7 +529,7 @@ def clean(self, value: Dict) -> bytes:
return data


def M(*patterns: Pattern, **lookups: Any) -> Optional[Pattern]:
def M(*patterns: Pattern, **lookups: Any) -> Pattern:
extras = None

for pattern__lookup, value in lookups.items():
Expand Down Expand Up @@ -550,12 +579,10 @@ def get_scheme_port(scheme: Optional[str]) -> Optional[int]:
return {"http": 80, "https": 443}.get(scheme or "")


def combine(
patterns: Sequence[Pattern], op: Callable = operator.and_
) -> Optional[Pattern]:
def combine(patterns: Sequence[Pattern], op: Callable = operator.and_) -> Pattern:
patterns = tuple(filter(None, patterns))
if not patterns:
return None
return Noop()
return reduce(op, patterns)


Expand Down Expand Up @@ -598,14 +625,14 @@ def parse_url_patterns(
return bases


def merge_patterns(pattern: Optional[Pattern], **bases: Pattern) -> Optional[Pattern]:
def merge_patterns(pattern: Pattern, **bases: Pattern) -> Pattern:
if not bases:
return pattern

if pattern:
# Flatten pattern
patterns = list(iter(pattern))
# Flatten pattern
patterns: List[Pattern] = list(filter(None, iter(pattern)))

if patterns:
if "host" in (_pattern.key for _pattern in patterns):
# Pattern is "absolute", skip merging
bases = {}
Expand Down
13 changes: 13 additions & 0 deletions tests/test_patterns.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
Lookup,
M,
Method,
Noop,
Params,
Path,
Pattern,
Expand Down Expand Up @@ -66,6 +67,18 @@ def test_match_context():
assert match.context == {"host": "foo.bar", "slug": "baz"}


def test_noop_pattern():
assert bool(Noop()) is False
assert bool(Noop().match(httpx.Request("GET", "https://example.org"))) is True
assert list(filter(None, [Noop()])) == []
assert repr(Noop()) == "<Noop>"
assert isinstance(~Noop(), Noop)
assert Method("GET") & Noop() == Method("GET")
assert Noop() & Method("GET") == Method("GET")
assert Method("GET") | Noop() == Method("GET")
assert Noop() | Method("GET") == Method("GET")


@pytest.mark.parametrize(
"kwargs,url,expected",
[
Expand Down

0 comments on commit a2f0e48

Please sign in to comment.