diff --git a/CHANGES b/CHANGES index 05f9fbc6..4201778a 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,8 @@ +0.25.5 +------ + +* Added `method=` as an optional keyword argument to the replace, remove, and upsert methods on RequestsMock. See #737 + 0.25.4 ------ diff --git a/README.rst b/README.rst index da473bfc..a6b7f372 100644 --- a/README.rst +++ b/README.rst @@ -296,7 +296,10 @@ Response Parameters The following attributes can be passed to a Response mock: method (``str``) - The HTTP method (GET, POST, etc). + The HTTP method (GET, POST, etc). You may also use `method_or_response=` + as a keyword parameter in place of `method`, in which case all attributes + must be passed in as keyword parameters. This is to match the parameter + name on the `remove()`, `replace()`, and `upsert ()` methods. url (``str`` or ``compiled regular expression``) The full resource URL. diff --git a/responses/__init__.py b/responses/__init__.py index bcfc19dc..dce62605 100644 --- a/responses/__init__.py +++ b/responses/__init__.py @@ -813,7 +813,15 @@ def add( >>> headers={'X-Header': 'foo'}, >>> ) + Use the keyword argument method_or_response in place of method: + + >>> responses.add( + >>> method_or_response='GET', + >>> url='http://example.com', + >>> ) + """ + if isinstance(method, BaseResponse): return self._registry.add(method) @@ -889,6 +897,7 @@ def remove( self, method_or_response: "_HTTPMethodOrResponse" = None, url: "Optional[_URLPatternType]" = None, + method: "_HTTPMethodOrResponse" = None, ) -> List[BaseResponse]: """ Removes a response previously added using ``add()``, identified @@ -899,12 +908,16 @@ def remove( >>> responses.add(responses.GET, 'http://example.org') >>> responses.remove(responses.GET, 'http://example.org') """ - if isinstance(method_or_response, BaseResponse): - response = method_or_response + actual_method_or_response = self._check_method_or_response( + method, method_or_response + ) + + if isinstance(actual_method_or_response, BaseResponse): + response = actual_method_or_response else: assert url is not None - assert isinstance(method_or_response, str) - response = BaseResponse(method=method_or_response, url=url) + assert isinstance(actual_method_or_response, str) + response = BaseResponse(method=actual_method_or_response, url=url) return self._registry.remove(response) @@ -913,6 +926,7 @@ def replace( method_or_response: "_HTTPMethodOrResponse" = None, url: "Optional[_URLPatternType]" = None, body: "_Body" = "", + method: "_HTTPMethodOrResponse" = None, *args: Any, **kwargs: Any, ) -> BaseResponse: @@ -925,12 +939,18 @@ def replace( >>> responses.add(responses.GET, 'http://example.org', json={'data': 1}) >>> responses.replace(responses.GET, 'http://example.org', json={'data': 2}) """ - if isinstance(method_or_response, BaseResponse): - response = method_or_response + actual_method_or_response = self._check_method_or_response( + method, method_or_response + ) + + if isinstance(actual_method_or_response, BaseResponse): + response = actual_method_or_response else: assert url is not None - assert isinstance(method_or_response, str) - response = Response(method=method_or_response, url=url, body=body, **kwargs) + assert isinstance(actual_method_or_response, str) + response = Response( + method=actual_method_or_response, url=url, body=body, **kwargs + ) return self._registry.replace(response) @@ -939,6 +959,7 @@ def upsert( method_or_response: "_HTTPMethodOrResponse" = None, url: "Optional[_URLPatternType]" = None, body: "_Body" = "", + method: "_HTTPMethodOrResponse" = None, *args: Any, **kwargs: Any, ) -> BaseResponse: @@ -951,10 +972,32 @@ def upsert( >>> responses.add(responses.GET, 'http://example.org', json={'data': 1}) >>> responses.upsert(responses.GET, 'http://example.org', json={'data': 2}) """ + actual_method_or_response = self._check_method_or_response( + method, method_or_response + ) + try: - return self.replace(method_or_response, url, body, *args, **kwargs) + return self.replace(actual_method_or_response, url, body, *args, **kwargs) except ValueError: - return self.add(method_or_response, url, body, *args, **kwargs) + return self.add(actual_method_or_response, url, body, *args, **kwargs) + + def _check_method_or_response( + self, + method: "_HTTPMethodOrResponse", + method_or_response: "_HTTPMethodOrResponse", + ) -> "_HTTPMethodOrResponse": + """ + Checks that only one of method or method_or_response is not None. + + Returns whichever one should be used if no exception is raised. + """ + assert (method is not None and method_or_response is None) or ( + method is None and method_or_response is not None + ), "Only one of `method` or `method_or_response` should be used." + + # For backwards compatibility method_or_response takes priority over + # method, but by the time we hit here only one or the other should valid + return method_or_response if method_or_response is not None else method def add_callback( self, diff --git a/responses/tests/test_responses.py b/responses/tests/test_responses.py index c2750095..b71713c0 100644 --- a/responses/tests/test_responses.py +++ b/responses/tests/test_responses.py @@ -174,6 +174,39 @@ def run(): assert_reset() +def test_replace_keyword_arg_method(): + @responses.activate + def run(): + responses.add(responses.GET, "http://example.com/one", body="one") + responses.replace( + method=responses.GET, url="http://example.com/one", body="testone" + ) + resp = requests.get("http://example.com/one") + assert_response(resp, "testone") + + responses.add(responses.GET, "http://example.com/two", body="two") + replacement_dict = { + "method": "GET", + "url": "http://example.com/two", + "body": "testtwo", + } + responses.replace(**replacement_dict) + resp = requests.get("http://example.com/two") + assert_response(resp, "testtwo") + + responses.add( + Response(method=responses.GET, url="http://example.com/three", body="three") + ) + responses.replace( + method=Response(responses.GET, "http://example.com/three", body="testthree") + ) + resp = requests.get("http://example.com/three") + assert_response(resp, "testthree") + + run() + assert_reset() + + @pytest.mark.parametrize( "original,replacement", [ @@ -282,6 +315,39 @@ def run(): assert_reset() +def test_upsert_keyword_arg_method(): + @responses.activate + def run(): + responses.add(responses.GET, "http://example.com/one", body="one") + responses.upsert( + method=responses.GET, url="http://example.com/one", body="testone" + ) + resp = requests.get("http://example.com/one") + assert_response(resp, "testone") + + responses.add(responses.GET, "http://example.com/two", body="two") + replacement_dict = { + "method": "GET", + "url": "http://example.com/two", + "body": "testtwo", + } + responses.upsert(**replacement_dict) + resp = requests.get("http://example.com/two") + assert_response(resp, "testtwo") + + responses.add( + Response(method=responses.GET, url="http://example.com/three", body="three") + ) + responses.upsert( + method=Response(responses.GET, "http://example.com/three", body="testthree") + ) + resp = requests.get("http://example.com/three") + assert_response(resp, "testthree") + + run() + assert_reset() + + def test_remove(): @responses.activate def run(): @@ -308,6 +374,34 @@ def run(): assert_reset() +def test_remove_keyword_arg_method(): + @responses.activate + def run(): + responses.add(responses.GET, "http://example.com/one", body="one") + responses.remove(method=responses.GET, url="http://example.com/one") + with pytest.raises(ConnectionError): + _ = requests.get("http://example.com/one") + + responses.add(responses.GET, "http://example.com/two", body="two") + remove_dict = { + "method": "GET", + "url": "http://example.com/two", + } + responses.remove(**remove_dict) + with pytest.raises(ConnectionError): + _ = requests.get("http://example.com/two") + + responses.add( + Response(method=responses.GET, url="http://example.com/three", body="three") + ) + responses.remove(method=Response(responses.GET, "http://example.com/three")) + with pytest.raises(ConnectionError): + _ = requests.get("http://example.com/three") + + run() + assert_reset() + + @pytest.mark.parametrize( "args1,kwargs1,args2,kwargs2,expected", [