Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions responses/registries.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,42 @@


class FirstMatchRegistry:
"""Default registry: the first registered `Response` that matches the request is used.

Responses are checked in the order they were added. If more than one response
matches, the earliest match is used once and then removed, so later calls get
the next match; the last remaining match is reused for every further call.
"""

def __init__(self) -> None:
self._responses: List["BaseResponse"] = []

@property
def registered(self) -> List["BaseResponse"]:
"""List of the registered `Response` objects, in the order they were added."""
return self._responses

def reset(self) -> None:
"""Remove all registered responses."""
self._responses = []

def find(
self, request: "PreparedRequest"
) -> Tuple[Optional["BaseResponse"], List[str]]:
"""Find the first registered `Response` that matches the request.

Parameters
----------
request : PreparedRequest
Request that was caught by the custom adapter.

Returns
-------
Tuple[Optional["BaseResponse"], List[str]]
Matched `Response` object (or None) and the reasons why the other
registered responses did not match.

"""
found = None
found_match = None
match_failed_reasons = []
Expand All @@ -47,6 +70,12 @@ def find(
return found_match, match_failed_reasons

def add(self, response: "BaseResponse") -> "BaseResponse":
"""Register a response and return it.

If the same `Response` instance is already registered, a deep copy is

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
If the same `Response` instance is already registered, a deep copy is
If the same ``Response`` instance is already registered, a deep copy is

python docstrings are RST shaped.

registered and returned instead, so the same object is never
registered twice.
"""
if any(response is resp for resp in self.registered):
# if user adds multiple responses that reference the same instance.
# do a comparison by memory allocation address.
Expand All @@ -57,13 +86,25 @@ def add(self, response: "BaseResponse") -> "BaseResponse":
return response

def remove(self, response: "BaseResponse") -> List["BaseResponse"]:
"""Remove every registered response with the same method and URL as ``response``.

Returns the removed responses.
"""
removed_responses = []
while response in self.registered:
self.registered.remove(response)
removed_responses.append(response)
return removed_responses

def replace(self, response: "BaseResponse") -> "BaseResponse":
"""Replace the registered response with the same method and URL as ``response``.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since self.registered.index(response) compares BaseResponse instances using __eq__ (evaluating matchers alongside method and URL), should the docstring note that matching rules/matchers are also evaluated when locating the response to replace?

Raises
------
ValueError
If no response with that method and URL is registered.

"""
try:
index = self.registered.index(response)
except ValueError:
Expand Down
Loading