Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-mocket for openSUSE:Factory checked in at 2026-07-08 17:32:23 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-mocket (Old) and /work/SRC/openSUSE:Factory/.python-mocket.new.1982 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-mocket" Wed Jul 8 17:32:23 2026 rev:50 rq:1364114 version:3.14.2 Changes: -------- --- /work/SRC/openSUSE:Factory/python-mocket/python-mocket.changes 2026-02-24 15:38:26.362017480 +0100 +++ /work/SRC/openSUSE:Factory/.python-mocket.new.1982/python-mocket.changes 2026-07-08 17:32:37.697291972 +0200 @@ -1,0 +2,10 @@ +Mon Jul 6 19:15:46 UTC 2026 - Dirk Müller <[email protected]> + +- update to 3.14.2: + * tests: add tests for hexdump and hexload, including + ValueError on invalid hex + * Better `can_handle_fun` documentation + * Raise an error when both `can_handle_fun` and + `match_querystring` are used + +------------------------------------------------------------------- Old: ---- mocket-3.14.1.tar.gz New: ---- mocket-3.14.2.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-mocket.spec ++++++ --- /var/tmp/diff_new_pack.oG7cdT/_old 2026-07-08 17:32:39.433352326 +0200 +++ /var/tmp/diff_new_pack.oG7cdT/_new 2026-07-08 17:32:39.437352464 +0200 @@ -36,7 +36,7 @@ %{?sle15_python_module_pythons} Name: python-mocket%{psuffix} -Version: 3.14.1 +Version: 3.14.2 Release: 0 Summary: Python socket mock framework License: BSD-3-Clause ++++++ mocket-3.14.1.tar.gz -> mocket-3.14.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mocket-3.14.1/PKG-INFO new/mocket-3.14.2/PKG-INFO --- old/mocket-3.14.1/PKG-INFO 2020-02-02 01:00:00.000000000 +0100 +++ new/mocket-3.14.2/PKG-INFO 2020-02-02 01:00:00.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: mocket -Version: 3.14.1 +Version: 3.14.2 Summary: Socket Mock Framework - for all kinds of socket animals, web-clients included - with gevent/asyncio/SSL support Project-URL: Homepage, https://pypi.org/project/mocket Project-URL: Repository, https://github.com/mindflayer/python-mocket @@ -48,7 +48,7 @@ Requires-Dist: pytest; extra == 'test' Requires-Dist: pytest-asyncio; extra == 'test' Requires-Dist: pytest-cov; extra == 'test' -Requires-Dist: redis; extra == 'test' +Requires-Dist: redis<8; extra == 'test' Requires-Dist: requests; extra == 'test' Requires-Dist: sure; extra == 'test' Requires-Dist: trio; extra == 'test' @@ -291,6 +291,21 @@ Example of how to mock a call with a custom request matching logic ================================================================== +``can_handle_fun`` lets you define matching logic beyond the default +``path + querystring`` behavior. + +The callback receives: + +- ``path``: request path (for example ``/ip``) +- ``qs_dict``: parsed query string as returned by ``urllib.parse.parse_qs(..., keep_blank_values=True)`` + +.. note:: + + When ``can_handle_fun`` is provided, it fully defines matching behavior. + In this case ``match_querystring`` is ignored. Mocket will raise a ``ValueError`` + if you specify both ``can_handle_fun`` and ``match_querystring=False`` together, + as this is likely a mistake. + .. code-block:: python import json @@ -299,8 +314,11 @@ from mocket.mocks.mockhttp import Entry import requests + @mocketize def test_can_handle(): + url = "https://httpbin.org" + Entry.single_register( Entry.GET, url, @@ -315,10 +333,59 @@ headers={"content-type": "application/json"}, can_handle_fun=lambda path, qs_dict: path == "/ip" and not qs_dict, ) + resp = requests.get("https://httpbin.org/ip") assert resp.status_code == 200 assert resp.json() == {"message": "There you go!"} +Useful patterns +--------------- + +Regex path matching: + +.. code-block:: python + + import re + + Entry.single_register( + Entry.GET, + "https://api.example.com", + body="ok", + can_handle_fun=lambda path, qs_dict: bool(re.match(r"^/users/\d+$", path)), + ) + +Query parameter checks: + +.. code-block:: python + + Entry.single_register( + Entry.GET, + "https://api.example.com", + body="ok", + can_handle_fun=lambda path, qs_dict: ( + path == "/search" + and qs_dict.get("q") == ["mocket"] + and qs_dict.get("limit", ["10"])[0].isdigit() + ), + ) + +Case-insensitive path checks: + +.. code-block:: python + + Entry.single_register( + Entry.GET, + "https://api.example.com", + body="ok", + can_handle_fun=lambda path, qs_dict: path.lower() == "/healthz", + ) + +Troubleshooting tips +-------------------- + +- ``parse_qs`` values are lists, so compare against ``["value"]``. +- Use ``qs_dict.get("key")`` instead of ``qs_dict["key"]`` when parameters are optional. +- Keep callbacks side-effect free; they may run multiple times during request processing. Example of how to record real socket traffic ============================================ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mocket-3.14.1/README.rst new/mocket-3.14.2/README.rst --- old/mocket-3.14.1/README.rst 2020-02-02 01:00:00.000000000 +0100 +++ new/mocket-3.14.2/README.rst 2020-02-02 01:00:00.000000000 +0100 @@ -231,6 +231,21 @@ Example of how to mock a call with a custom request matching logic ================================================================== +``can_handle_fun`` lets you define matching logic beyond the default +``path + querystring`` behavior. + +The callback receives: + +- ``path``: request path (for example ``/ip``) +- ``qs_dict``: parsed query string as returned by ``urllib.parse.parse_qs(..., keep_blank_values=True)`` + +.. note:: + + When ``can_handle_fun`` is provided, it fully defines matching behavior. + In this case ``match_querystring`` is ignored. Mocket will raise a ``ValueError`` + if you specify both ``can_handle_fun`` and ``match_querystring=False`` together, + as this is likely a mistake. + .. code-block:: python import json @@ -239,8 +254,11 @@ from mocket.mocks.mockhttp import Entry import requests + @mocketize def test_can_handle(): + url = "https://httpbin.org" + Entry.single_register( Entry.GET, url, @@ -255,10 +273,59 @@ headers={"content-type": "application/json"}, can_handle_fun=lambda path, qs_dict: path == "/ip" and not qs_dict, ) + resp = requests.get("https://httpbin.org/ip") assert resp.status_code == 200 assert resp.json() == {"message": "There you go!"} +Useful patterns +--------------- + +Regex path matching: + +.. code-block:: python + + import re + + Entry.single_register( + Entry.GET, + "https://api.example.com", + body="ok", + can_handle_fun=lambda path, qs_dict: bool(re.match(r"^/users/\d+$", path)), + ) + +Query parameter checks: + +.. code-block:: python + + Entry.single_register( + Entry.GET, + "https://api.example.com", + body="ok", + can_handle_fun=lambda path, qs_dict: ( + path == "/search" + and qs_dict.get("q") == ["mocket"] + and qs_dict.get("limit", ["10"])[0].isdigit() + ), + ) + +Case-insensitive path checks: + +.. code-block:: python + + Entry.single_register( + Entry.GET, + "https://api.example.com", + body="ok", + can_handle_fun=lambda path, qs_dict: path.lower() == "/healthz", + ) + +Troubleshooting tips +-------------------- + +- ``parse_qs`` values are lists, so compare against ``["value"]``. +- Use ``qs_dict.get("key")`` instead of ``qs_dict["key"]`` when parameters are optional. +- Keep callbacks side-effect free; they may run multiple times during request processing. Example of how to record real socket traffic ============================================ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mocket-3.14.1/mocket/__init__.py new/mocket-3.14.2/mocket/__init__.py --- old/mocket-3.14.1/mocket/__init__.py 2020-02-02 01:00:00.000000000 +0100 +++ new/mocket-3.14.2/mocket/__init__.py 2020-02-02 01:00:00.000000000 +0100 @@ -33,4 +33,4 @@ "FakeSSLContext", ) -__version__ = "3.14.1" +__version__ = "3.14.2" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mocket-3.14.1/mocket/mocks/mockhttp.py new/mocket-3.14.2/mocket/mocks/mockhttp.py --- old/mocket-3.14.1/mocket/mocks/mockhttp.py 2020-02-02 01:00:00.000000000 +0100 +++ new/mocket-3.14.2/mocket/mocks/mockhttp.py 2020-02-02 01:00:00.000000000 +0100 @@ -237,7 +237,16 @@ responses: Response(s) to return match_querystring: Whether to match query strings can_handle_fun: Custom matching function + + Raises: + ValueError: If both can_handle_fun and match_querystring are specified """ + if can_handle_fun and not match_querystring: + raise ValueError( + "cannot specify both 'can_handle_fun' and 'match_querystring=False': " + "when using a custom matching function, 'match_querystring' is ignored" + ) + self._can_handle_fun = can_handle_fun if can_handle_fun else self._can_handle uri = urlsplit(uri) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mocket-3.14.1/pyproject.toml new/mocket-3.14.2/pyproject.toml --- old/mocket-3.14.1/pyproject.toml 2020-02-02 01:00:00.000000000 +0100 +++ new/mocket-3.14.2/pyproject.toml 2020-02-02 01:00:00.000000000 +0100 @@ -51,7 +51,7 @@ "pytest-asyncio", "asgiref", "requests", - "redis", + "redis<8", "gevent", "sure", "flake8>5", @@ -126,6 +126,9 @@ # https://en.wikipedia.org/wiki/Cyclomatic_complexity#Limiting_complexity_during_development max-complexity = 8 +[tool.ruff.lint.isort] +known-first-party = ["mocket"] + [tool.mypy] python_version = "3.13" files = [ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mocket-3.14.1/tests/test_http.py new/mocket-3.14.2/tests/test_http.py --- old/mocket-3.14.1/tests/test_http.py 2020-02-02 01:00:00.000000000 +0100 +++ new/mocket-3.14.2/tests/test_http.py 2020-02-02 01:00:00.000000000 +0100 @@ -482,3 +482,39 @@ response = requests.get("http://testme.org/foobar?b=2") self.assertEqual(response.status_code, 200) self.assertEqual(response.json(), {"message": "Missed!"}) + + def test_can_handle_fun_with_match_querystring_false_raises(self): + """Test that using both can_handle_fun and match_querystring=False raises ValueError.""" + with self.assertRaises(ValueError) as context: + Entry( + "http://testme.org/path", + Entry.GET, + responses=["test"], + can_handle_fun=lambda path, qs: True, + match_querystring=False, + ) + self.assertIn( + "cannot specify both 'can_handle_fun' and 'match_querystring=False'", + str(context.exception), + ) + + def test_can_handle_fun_with_match_querystring_true_works(self): + """Test that using can_handle_fun with match_querystring=True works fine.""" + entry = Entry( + "http://testme.org/path", + Entry.GET, + responses=["test"], + can_handle_fun=lambda path, qs: True, + match_querystring=True, + ) + self.assertIsNotNone(entry) + + def test_can_handle_fun_alone_works(self): + """Test that using can_handle_fun alone (without specifying match_querystring) works.""" + entry = Entry( + "http://testme.org/path", + Entry.GET, + responses=["test"], + can_handle_fun=lambda path, qs: True, + ) + self.assertIsNotNone(entry) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mocket-3.14.1/tests/test_utils.py new/mocket-3.14.2/tests/test_utils.py --- old/mocket-3.14.1/tests/test_utils.py 2020-02-02 01:00:00.000000000 +0100 +++ new/mocket-3.14.2/tests/test_utils.py 2020-02-02 01:00:00.000000000 +0100 @@ -4,7 +4,7 @@ import decorator -from mocket.utils import get_mocketize +from mocket.utils import get_mocketize, hexdump, hexload def mock_decorator(func: Callable[[], None]) -> None: @@ -29,3 +29,27 @@ dec.call_args_list[0].assert_compare_to((mock_decorator,), {"kwsyntax": True}) # Second time without kwsyntax, which succeeds dec.call_args_list[1].assert_compare_to((mock_decorator,)) + + +class HexdumpTestCase(TestCase): + def test_hexdump_converts_bytes_to_spaced_hex(self) -> None: + assert hexdump(b"Hi") == "48 69" + + def test_hexdump_empty_bytes(self) -> None: + assert hexdump(b"") == "" + + def test_hexdump_roundtrip_with_hexload(self) -> None: + data = b"bar foobar foo" + assert hexload(hexdump(data)) == data + + +class HexloadTestCase(TestCase): + def test_hexload_converts_spaced_hex_to_bytes(self) -> None: + assert hexload("48 69") == b"Hi" + + def test_hexload_empty_string(self) -> None: + assert hexload("") == b"" + + def test_hexload_invalid_hex_raises_value_error(self) -> None: + with self.assertRaises(ValueError): + hexload("ZZ ZZ")
