pytest_httpserver 1.1.3__tar.gz → 1.1.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/CHANGES.rst +42 -0
  2. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/PKG-INFO +8 -8
  3. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/doc/api.rst +12 -0
  4. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/doc/conf.py +1 -1
  5. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/doc/howto.rst +61 -0
  6. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/doc/tutorial.rst +73 -10
  7. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/pyproject.toml +32 -23
  8. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/pytest_httpserver/__init__.py +4 -0
  9. pytest_httpserver-1.1.5/pytest_httpserver/bake.py +105 -0
  10. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/pytest_httpserver/blocking_httpserver.py +7 -7
  11. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/pytest_httpserver/hooks.py +5 -5
  12. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/pytest_httpserver/httpserver.py +144 -63
  13. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/pytest_httpserver/pytest_plugin.py +29 -16
  14. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/assets/Makefile +1 -1
  15. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/assets/rootCA.cnf +4 -0
  16. pytest_httpserver-1.1.5/tests/assets/rootCA.crt +24 -0
  17. pytest_httpserver-1.1.5/tests/assets/rootCA.key +28 -0
  18. pytest_httpserver-1.1.5/tests/assets/rootCA.srl +1 -0
  19. pytest_httpserver-1.1.5/tests/assets/server.crt +25 -0
  20. pytest_httpserver-1.1.5/tests/assets/server.csr +18 -0
  21. pytest_httpserver-1.1.5/tests/assets/server.key +28 -0
  22. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/assets/v3.ext +1 -0
  23. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/examples/test_example_blocking_httpserver.py +1 -1
  24. pytest_httpserver-1.1.5/tests/examples/test_howto_bake.py +25 -0
  25. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/examples/test_howto_check.py +1 -1
  26. pytest_httpserver-1.1.5/tests/examples/test_howto_readiness.py +30 -0
  27. pytest_httpserver-1.1.5/tests/test_bake.py +162 -0
  28. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/test_hooks.py +4 -4
  29. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/test_log_querying.py +2 -2
  30. pytest_httpserver-1.1.5/tests/test_readiness.py +124 -0
  31. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/test_release.py +11 -7
  32. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/test_urimatch.py +1 -1
  33. pytest_httpserver-1.1.3/tests/assets/rootCA.crt +0 -22
  34. pytest_httpserver-1.1.3/tests/assets/rootCA.key +0 -27
  35. pytest_httpserver-1.1.3/tests/assets/rootCA.srl +0 -1
  36. pytest_httpserver-1.1.3/tests/assets/server.crt +0 -28
  37. pytest_httpserver-1.1.3/tests/assets/server.csr +0 -18
  38. pytest_httpserver-1.1.3/tests/assets/server.key +0 -28
  39. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/CONTRIBUTION.md +0 -0
  40. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/LICENSE +0 -0
  41. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/README.md +0 -0
  42. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/doc/Makefile +0 -0
  43. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/doc/_static/.placeholder +0 -0
  44. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/doc/background.rst +0 -0
  45. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/doc/changes.rst +0 -0
  46. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/doc/fixtures.rst +0 -0
  47. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/doc/guide.rst +0 -0
  48. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/doc/index.rst +0 -0
  49. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/doc/patch.py +0 -0
  50. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/doc/upgrade.rst +0 -0
  51. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/example.py +0 -0
  52. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/example_pytest.py +0 -0
  53. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/pytest_httpserver/py.typed +0 -0
  54. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/assets/README +0 -0
  55. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/assets/server.cnf +0 -0
  56. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/conftest.py +0 -0
  57. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/examples/test_example_query_params1.py +0 -0
  58. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/examples/test_example_query_params2.py +0 -0
  59. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/examples/test_howto_authorization_headers.py +0 -0
  60. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/examples/test_howto_case_insensitive_matcher.py +0 -0
  61. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/examples/test_howto_check_handler_errors.py +0 -0
  62. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/examples/test_howto_custom_handler.py +0 -0
  63. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/examples/test_howto_custom_hooks.py +0 -0
  64. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/examples/test_howto_custom_request_matcher.py +0 -0
  65. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/examples/test_howto_header_value_matcher.py +0 -0
  66. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/examples/test_howto_hooks.py +0 -0
  67. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/examples/test_howto_json_matcher.py +0 -0
  68. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/examples/test_howto_log_querying.py +0 -0
  69. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/examples/test_howto_query_params_dict.py +0 -0
  70. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/examples/test_howto_query_params_never_do_this.py +0 -0
  71. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/examples/test_howto_query_params_proper_use.py +0 -0
  72. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/examples/test_howto_regexp.py +0 -0
  73. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/examples/test_howto_timeout_requests.py +0 -0
  74. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/examples/test_howto_url_matcher.py +0 -0
  75. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/examples/test_howto_wait_success.py +0 -0
  76. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/test_blocking_httpserver.py +0 -0
  77. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/test_handler_errors.py +0 -0
  78. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/test_headers.py +0 -0
  79. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/test_ip_protocols.py +0 -0
  80. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/test_json_matcher.py +0 -0
  81. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/test_log_leak.py +0 -0
  82. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/test_matcher.py +0 -0
  83. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/test_mixed.py +0 -0
  84. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/test_oneshot.py +0 -0
  85. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/test_ordered.py +0 -0
  86. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/test_parse_qs.py +0 -0
  87. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/test_permanent.py +0 -0
  88. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/test_port_changing.py +0 -0
  89. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/test_querymatcher.py +0 -0
  90. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/test_querystring.py +0 -0
  91. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/test_ssl.py +0 -0
  92. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/test_thread_type.py +0 -0
  93. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/test_threaded.py +0 -0
  94. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/test_wait.py +0 -0
  95. {pytest_httpserver-1.1.3 → pytest_httpserver-1.1.5}/tests/test_with_statement.py +0 -0
@@ -2,6 +2,48 @@
2
2
  Release Notes
3
3
  =============
4
4
 
5
+ .. _Release Notes_1.1.5:
6
+
7
+ 1.1.5
8
+ =====
9
+
10
+ .. _Release Notes_1.1.5_New Features:
11
+
12
+ New Features
13
+ ------------
14
+
15
+ - Add ``bake()`` method to ``HTTPServer`` for creating pre-configured
16
+ request expectation proxies (``BakedHTTPServer``). This allows sharing
17
+ common keyword arguments (e.g. ``method``, ``headers``) across multiple
18
+ ``expect_request()`` calls with last-wins merging semantics.
19
+ `#470 <https://github.com/csernazs/pytest-httpserver/pull/470>`_
20
+ Contributed by `@HayaoSuzuki <https://github.com/HayaoSuzuki>`_
21
+
22
+
23
+ .. _Release Notes_1.1.4:
24
+
25
+ 1.1.4
26
+ =====
27
+
28
+ .. _Release Notes_1.1.4_New Features:
29
+
30
+ New Features
31
+ ------------
32
+
33
+ - More robust server startup by checking server readiness (disabled by
34
+ default).
35
+ `#462 <https://github.com/csernazs/pytest-httpserver/pull/462>`_
36
+
37
+
38
+ .. _Release Notes_1.1.4_Deprecation Notes:
39
+
40
+ Deprecation Notes
41
+ -----------------
42
+
43
+ - Python 3.9 has been deprecated as it reached EOL 2025-10-31. While the code
44
+ may work on this version, there will be no CI tests running on it.
45
+
46
+
5
47
  .. _Release Notes_1.1.3:
6
48
 
7
49
  1.1.3
@@ -1,27 +1,27 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: pytest_httpserver
3
- Version: 1.1.3
3
+ Version: 1.1.5
4
4
  Summary: pytest-httpserver is a httpserver for pytest
5
- Home-page: https://github.com/csernazs/pytest-httpserver
6
- License: MIT
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
7
  Author: Zsolt Cserna
8
8
  Author-email: cserna.zsolt@gmail.com
9
- Requires-Python: >=3.9
9
+ Requires-Python: >=3.10
10
10
  Classifier: Development Status :: 3 - Alpha
11
- Classifier: Framework :: Pytest
12
11
  Classifier: Intended Audience :: Developers
13
- Classifier: License :: OSI Approved :: MIT License
14
12
  Classifier: Operating System :: OS Independent
15
13
  Classifier: Programming Language :: Python :: 3
16
- Classifier: Programming Language :: Python :: 3.9
17
14
  Classifier: Programming Language :: Python :: 3.10
18
15
  Classifier: Programming Language :: Python :: 3.11
19
16
  Classifier: Programming Language :: Python :: 3.12
20
17
  Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
21
19
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Classifier: Framework :: Pytest
22
21
  Requires-Dist: Werkzeug (>=2.0.0)
23
22
  Project-URL: Bug Tracker, https://github.com/csernazs/pytest-httpserver/issues
24
23
  Project-URL: Documentation, https://pytest-httpserver.readthedocs.io/en/latest/
24
+ Project-URL: Homepage, https://github.com/csernazs/pytest-httpserver
25
25
  Project-URL: Repository, https://github.com/csernazs/pytest-httpserver
26
26
  Description-Content-Type: text/markdown
27
27
 
@@ -24,6 +24,12 @@ RequestHandler
24
24
  :inherited-members:
25
25
 
26
26
 
27
+ RequestMatcherKwargs
28
+ ~~~~~~~~~~~~~~~~~~~~
29
+
30
+ .. autoclass:: RequestMatcherKwargs
31
+ :members:
32
+
27
33
  RequestMatcher
28
34
  ~~~~~~~~~~~~~~
29
35
 
@@ -31,6 +37,12 @@ RequestMatcher
31
37
  :members:
32
38
 
33
39
 
40
+ BakedHTTPServer
41
+ ~~~~~~~~~~~~~~~~
42
+
43
+ .. autoclass:: BakedHTTPServer
44
+ :members:
45
+
34
46
  BlockingHTTPServer
35
47
  ~~~~~~~~~~~~~~~~~~
36
48
 
@@ -68,7 +68,7 @@ author = "Zsolt Cserna"
68
68
  # built documents.
69
69
  #
70
70
  # The short X.Y version.
71
- version = "1.1.3"
71
+ version = "1.1.5"
72
72
  # The full version, including alpha/beta/rc tags.
73
73
  release = version
74
74
 
@@ -299,6 +299,41 @@ the ``httpserver`` fixture).
299
299
  return ("127.0.0.1", 8000)
300
300
 
301
301
 
302
+ Waiting for server to be ready
303
+ ------------------------------
304
+
305
+ By default, the ``httpserver`` fixture ensures that the server is bound to the
306
+ configured address and listening for incoming connections. While this is
307
+ sufficient for most use cases, you can optionally wait for the server to be
308
+ fully ready to serve HTTP requests before proceeding with your tests. This is
309
+ particularly useful when your client has strict timeout requirements or when the
310
+ HTTP server has a slow startup time.
311
+
312
+ To enable this check, *pytest-httpserver* issues an HTTP probe request to the
313
+ server before your test starts to ensure the server is ready to serve requests.
314
+ You can configure the timeout for this probe request as needed.
315
+
316
+
317
+ .. warning::
318
+
319
+ This check is not supported in SSL mode, as the probe request is not
320
+ configured to accept the self-signed certificate used by the server. To
321
+ enable this feature with SSL, override the ``wait_for_server_ready()``
322
+ method with your implementation.
323
+
324
+
325
+ To enable this feature, set the ``startup_timeout`` keyword argument when
326
+ initializing ``HTTPServer``. This parameter specifies the maximum time to wait
327
+ for the server to become ready to serve HTTP requests.
328
+
329
+
330
+ .. literalinclude :: ../tests/examples/test_howto_readiness.py
331
+ :language: python
332
+
333
+
334
+ In the case the server times out, the test will fail.
335
+
336
+
302
337
  Multi-threading support
303
338
  -----------------------
304
339
 
@@ -669,3 +704,29 @@ Example:
669
704
  will register the hooks, and hooks will be called sequentially, one by one. Each
670
705
  hook will receive the response what the previous hook returned, and the last
671
706
  hook called will return the final response which will be sent back to the client.
707
+
708
+
709
+ Reducing repetition with bake
710
+ -----------------------------
711
+
712
+ When multiple expectations share common parameters (such as headers or method),
713
+ the ``bake()`` method creates a proxy with pre-configured defaults. Keyword
714
+ arguments passed to ``bake()`` become defaults that are merged with arguments
715
+ provided at call time using last-wins semantics: if the same keyword appears in
716
+ both, the call-time value is used.
717
+
718
+ .. literalinclude :: ../tests/examples/test_howto_bake.py
719
+ :language: python
720
+
721
+ The ``bake()`` method can be chained to layer additional defaults:
722
+
723
+ .. code-block:: python
724
+
725
+ json_post = httpserver.bake(method="POST").bake(
726
+ headers={"Content-Type": "application/json"}
727
+ )
728
+
729
+ All ``expect_request``, ``expect_oneshot_request``, and
730
+ ``expect_ordered_request`` methods are available on the baked object. Other
731
+ attributes such as ``url_for()`` and ``check_assertions()`` are delegated to
732
+ the underlying server transparently.
@@ -419,7 +419,7 @@ unhandled and in the end the test will be failed.
419
419
 
420
420
  In some cases, however, you want to make sure that everything is ok so far,
421
421
  and raise AssertionError when something is not good. Call the
422
- ``check_assertions()`` method of the httpserver object, and this will look at
422
+ ``check()`` method of the httpserver object, and this will look at
423
423
  the server's internal state (which is running in the other thread) and if
424
424
  there's something not right (such as the order of the requests not matching,
425
425
  or there was a non-matching request), it will raise an AssertionError and
@@ -434,8 +434,71 @@ your test will properly fail:
434
434
  requests.get(httpserver.url_for("/foobaz"))
435
435
  requests.get(httpserver.url_for("/foobar")) # gets 500
436
436
 
437
- httpserver.check_assertions() # this will raise AssertionError and make the test failing
437
+ httpserver.check() # this will raise AssertionError and make the test failing
438
438
 
439
+ This will also produce a (hopefully) helpful description about what went wrong::
440
+
441
+ > raise AssertionError(assertion)
442
+ E AssertionError: No handler found for request <Request 'http://localhost:41085/foobaz' [GET]> with data b''.Ordered matchers:
443
+ E <RequestMatcher uri='/foobar' method='__ALL' query_string=None headers={} data=None json=<UNDEFINED>>
444
+ E <RequestMatcher uri='/foobaz' method='__ALL' query_string=None headers={} data=None json=<UNDEFINED>>
445
+ E
446
+ E Oneshot matchers:
447
+ E none
448
+ E
449
+ E Persistent matchers:
450
+ E none
451
+
452
+
453
+ Calling ``check()`` for all tests
454
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
455
+
456
+ Sometimes you want to see the informative message made by ``check()`` if
457
+ your test failed.
458
+
459
+ In such case you can implement a new fixture or override the behavior:
460
+
461
+
462
+ .. code:: python
463
+
464
+ from collections.abc import Iterable
465
+ from pytest_httpserver import HTTPServer
466
+ import requests
467
+
468
+ import pytest
469
+
470
+
471
+ @pytest.fixture
472
+ def httpserver(httpserver: HTTPServer) -> Iterable[HTTPServer]:
473
+ yield httpserver
474
+ httpserver.check() # this will raise AssertionError and make the test failing
475
+
476
+
477
+ def test_client(httpserver: HTTPServer):
478
+ httpserver.expect_request("/foo").respond_with_data("foo")
479
+ httpserver.expect_request("/bar").respond_with_data("bar")
480
+
481
+ resp = requests.get(httpserver.url_for("/foobar")) # gets 500
482
+ resp.raise_for_status() # raises error
483
+
484
+
485
+ When the tests are run with ``-vv`` then it will show both errors::
486
+
487
+ FAILED example2.py::test_client - requests.exceptions.HTTPError: 500 Server Error: INTERNAL SERVER ERROR for url: http://localhost:37425/foobar
488
+ ERROR example2.py::test_client - AssertionError: No handler found for request <Request 'http://localhost:37425/foobar' [GET]> with data b''.
489
+ Ordered matchers:
490
+ none
491
+
492
+ Oneshot matchers:
493
+ none
494
+
495
+ Persistent matchers:
496
+ <RequestMatcher uri='/foo' method='__ALL' query_string=None headers={} data=None json=<UNDEFINED>>
497
+ <RequestMatcher uri='/bar' method='__ALL' query_string=None headers={} data=None json=<UNDEFINED>>
498
+
499
+
500
+ Logs
501
+ ~~~~
439
502
 
440
503
  The server writes a log about the requests and responses which were
441
504
  processed. This can be accessed in the `log` attribute of the http server.
@@ -457,7 +520,7 @@ Debugging
457
520
  ~~~~~~~~~
458
521
 
459
522
  If you having multiple requests for the server, adding the call to
460
- ``check_assertions()`` may to debug as it will make the test failed as
523
+ ``check()`` may help to debug as it will make the test failed as
461
524
  soon as possible.
462
525
 
463
526
  .. code:: python
@@ -471,20 +534,20 @@ soon as possible.
471
534
  requests.get(httpserver.url_for("/bar"))
472
535
  requests.get(httpserver.url_for("/foobar"))
473
536
 
474
- httpserver.check_assertions()
537
+ httpserver.check()
475
538
 
476
539
  In the above code, the first request (to **/foo**) is not successful (it gets
477
540
  http status 500), but as the response status is not checked (or any of the
478
- response), and there's no call to ``check_assertions()``, the test continues the
541
+ response), and there's no call to ``check()``, the test continues the
479
542
  running. It gets through the **/bar** request, which is also not successful
480
543
  (and gets http status 500 also like the first one), then goes the last request
481
544
  which is successful (as there's a handler defined for it)
482
545
 
483
- In the end, when checking the check_assertions() raise the error for the first
546
+ In the end, when checking the check() raise the error for the first
484
547
  request, but it is a bit late: figuring out the request which caused the problem
485
548
  could be troublesome. Also, it will report the problem for the first request only.
486
549
 
487
- Adding more call of ``check_assertions()`` will help.
550
+ Adding more call of ``check()`` will help.
488
551
 
489
552
 
490
553
  .. code:: python
@@ -495,13 +558,13 @@ Adding more call of ``check_assertions()`` will help.
495
558
  def test_json_client(httpserver: HTTPServer):
496
559
  httpserver.expect_request("/foobar").respond_with_json({"foo": "bar"})
497
560
  requests.get(httpserver.url_for("/foo"))
498
- httpserver.check_assertions()
561
+ httpserver.check()
499
562
 
500
563
  requests.get(httpserver.url_for("/bar"))
501
- httpserver.check_assertions()
564
+ httpserver.check()
502
565
 
503
566
  requests.get(httpserver.url_for("/foobar"))
504
- httpserver.check_assertions()
567
+ httpserver.check()
505
568
 
506
569
 
507
570
  In the above code, the test will fail after the first request.
@@ -1,20 +1,40 @@
1
- [tool.poetry]
1
+ [project]
2
2
  name = "pytest_httpserver"
3
- version = "1.1.3"
3
+ version = "1.1.5"
4
4
  description = "pytest-httpserver is a httpserver for pytest"
5
- authors = ["Zsolt Cserna <cserna.zsolt@gmail.com>"]
6
- license = "MIT"
7
5
  readme = "README.md"
8
- documentation = "https://pytest-httpserver.readthedocs.io/en/latest/"
6
+ license = "MIT"
7
+ authors = [
8
+ { name = "Zsolt Cserna", email = "cserna.zsolt@gmail.com" }
9
+ ]
9
10
  classifiers = [
10
11
  "Development Status :: 3 - Alpha",
11
12
  "Intended Audience :: Developers",
12
13
  "Operating System :: OS Independent",
14
+ "Programming Language :: Python :: 3",
15
+ "Programming Language :: Python :: 3.10",
16
+ "Programming Language :: Python :: 3.11",
17
+ "Programming Language :: Python :: 3.12",
18
+ "Programming Language :: Python :: 3.13",
19
+ "Programming Language :: Python :: 3.14",
13
20
  "Topic :: Software Development :: Libraries :: Python Modules",
14
21
  "Framework :: Pytest",
15
22
  ]
16
- repository = "https://github.com/csernazs/pytest-httpserver"
23
+ requires-python = ">=3.10"
24
+ dependencies = [
25
+ "Werkzeug >= 2.0.0",
26
+ ]
27
+
28
+ [project.urls]
29
+ Homepage = "https://github.com/csernazs/pytest-httpserver"
30
+ Documentation = "https://pytest-httpserver.readthedocs.io/en/latest/"
31
+ Repository = "https://github.com/csernazs/pytest-httpserver"
32
+ "Bug Tracker" = "https://github.com/csernazs/pytest-httpserver/issues"
33
+
34
+ [project.entry-points.pytest11]
35
+ pytest_httpserver = "pytest_httpserver.pytest_plugin"
17
36
 
37
+ [tool.poetry]
18
38
  include = [
19
39
  { path = "tests", format = "sdist" },
20
40
  { path = "CHANGES.rst", format = "sdist" },
@@ -23,29 +43,19 @@ include = [
23
43
  { path = "doc", format = "sdist" },
24
44
  ]
25
45
 
26
- [tool.poetry.dependencies]
27
- python = ">=3.9"
28
- Werkzeug = ">= 2.0.0"
29
-
30
-
31
- [tool.poetry.plugins.pytest11]
32
- pytest_httpserver = "pytest_httpserver.pytest_plugin"
33
-
34
- [tool.poetry.urls]
35
- "Bug Tracker" = "https://github.com/csernazs/pytest-httpserver/issues"
36
-
37
46
  [tool.poetry.group.develop]
38
47
  optional = true
39
48
 
40
49
  [tool.poetry.group.develop.dependencies]
41
50
  pre-commit = ">=2.20,<5.0"
42
51
  requests = "*"
43
- Sphinx = ">=5.1.1,<8.0.0"
52
+ typing_extensions = { version = ">=4.0", markers = "python_version < '3.11'" }
53
+ Sphinx = ">=5.1.1,<9.0.0"
44
54
  sphinx-rtd-theme = ">=1,<4"
45
55
  reno = "*"
46
56
  types-requests = "*"
47
- pytest = ">=7.1.3,<9.0.0"
48
- pytest-cov = ">=3,<7"
57
+ pytest = ">=7.1.3,<10.0.0"
58
+ pytest-cov = ">=3,<8"
49
59
  coverage = ">=6.4.4,<8.0.0"
50
60
  tomli = { version = "*", markers = "python_version < '3.11'"}
51
61
  black = "*"
@@ -57,7 +67,7 @@ mypy = "*"
57
67
  optional = true
58
68
 
59
69
  [tool.poetry.group.doc.dependencies]
60
- Sphinx = ">=5.1.1,<8.0.0"
70
+ Sphinx = ">=5.1.1,<9.0.0"
61
71
  sphinx-rtd-theme = ">=1,<4"
62
72
 
63
73
 
@@ -116,7 +126,6 @@ lint.ignore = [
116
126
  "PLR0913",
117
127
  "PLR2004",
118
128
  "PLW2901",
119
- "PT004",
120
129
  "PT012",
121
130
  "PT013",
122
131
  "PTH118",
@@ -137,5 +146,5 @@ lint.ignore = [
137
146
  "UP032",
138
147
  ]
139
148
  line-length = 120
140
- target-version = "py39"
149
+ target-version = "py310"
141
150
  exclude = ["doc", "example*.py", "tests/examples/*.py"]
@@ -6,6 +6,7 @@ This is package provides the main API for the pytest_httpserver package.
6
6
  __all__ = [
7
7
  "METHOD_ALL",
8
8
  "URI_DEFAULT",
9
+ "BakedHTTPServer",
9
10
  "BlockingHTTPServer",
10
11
  "BlockingRequestHandler",
11
12
  "Error",
@@ -15,10 +16,12 @@ __all__ = [
15
16
  "NoHandlerError",
16
17
  "RequestHandler",
17
18
  "RequestMatcher",
19
+ "RequestMatcherKwargs",
18
20
  "URIPattern",
19
21
  "WaitingSettings",
20
22
  ]
21
23
 
24
+ from .bake import BakedHTTPServer
22
25
  from .blocking_httpserver import BlockingHTTPServer
23
26
  from .blocking_httpserver import BlockingRequestHandler
24
27
  from .httpserver import METHOD_ALL
@@ -30,5 +33,6 @@ from .httpserver import HTTPServerError
30
33
  from .httpserver import NoHandlerError
31
34
  from .httpserver import RequestHandler
32
35
  from .httpserver import RequestMatcher
36
+ from .httpserver import RequestMatcherKwargs
33
37
  from .httpserver import URIPattern
34
38
  from .httpserver import WaitingSettings
@@ -0,0 +1,105 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+ from typing import Any
5
+
6
+ if TYPE_CHECKING:
7
+ import sys
8
+ from re import Pattern
9
+ from types import TracebackType
10
+
11
+ if sys.version_info >= (3, 11):
12
+ from typing import Self
13
+ else:
14
+ from typing_extensions import Self
15
+
16
+ if sys.version_info >= (3, 12):
17
+ from typing import Unpack
18
+ else:
19
+ from typing_extensions import Unpack
20
+
21
+ from .httpserver import HTTPServer
22
+ from .httpserver import RequestHandler
23
+ from .httpserver import RequestMatcherKwargs
24
+ from .httpserver import URIPattern
25
+
26
+
27
+ class BakedHTTPServer:
28
+ """
29
+ A proxy for :py:class:`HTTPServer` with pre-configured defaults for
30
+ ``expect_request()`` and related methods.
31
+
32
+ Created via :py:meth:`HTTPServer.bake`. Keyword arguments stored at bake
33
+ time are merged with arguments provided at call time using last-wins
34
+ semantics: if the same keyword appears in both, the call-time value is
35
+ used.
36
+
37
+ Any attribute not explicitly defined here is delegated to the wrapped
38
+ :py:class:`HTTPServer`, so ``url_for()``, ``check_assertions()``, etc.
39
+ work transparently.
40
+ """
41
+
42
+ def __init__(self, server: HTTPServer, **kwargs: Unpack[RequestMatcherKwargs]) -> None:
43
+ self._server = server
44
+ self._defaults = kwargs
45
+ self._context_depth: int = 0
46
+ self._started_server: bool = False
47
+
48
+ def __enter__(self) -> Self:
49
+ if self._context_depth == 0:
50
+ self._started_server = not self._server.is_running()
51
+ self._server.__enter__()
52
+ self._context_depth += 1
53
+ return self
54
+
55
+ def __exit__(
56
+ self,
57
+ exc_type: type[BaseException] | None,
58
+ exc_value: BaseException | None,
59
+ traceback: TracebackType | None,
60
+ ) -> None:
61
+ self._context_depth -= 1
62
+ if self._started_server and self._context_depth == 0:
63
+ self._server.__exit__(exc_type, exc_value, traceback)
64
+ self._started_server = False
65
+
66
+ def __getattr__(self, name: str) -> Any:
67
+ return getattr(self._server, name)
68
+
69
+ def __repr__(self) -> str:
70
+ return f"<{self.__class__.__name__} defaults={self._defaults!r} server={self._server!r}>"
71
+
72
+ def _merge_kwargs(self, kwargs: RequestMatcherKwargs) -> RequestMatcherKwargs:
73
+ return self._defaults | kwargs
74
+
75
+ def bake(self, **kwargs: Unpack[RequestMatcherKwargs]) -> Self:
76
+ """
77
+ Create a new :py:class:`BakedHTTPServer` by further layering defaults.
78
+
79
+ The new proxy merges the current defaults with the new ``kwargs``.
80
+ """
81
+ return self.__class__(self._server, **self._merge_kwargs(kwargs))
82
+
83
+ def expect_request(
84
+ self,
85
+ uri: str | URIPattern | Pattern[str],
86
+ **kwargs: Unpack[RequestMatcherKwargs],
87
+ ) -> RequestHandler:
88
+ """Create and register a request handler, using baked defaults."""
89
+ return self._server.expect_request(uri, **self._merge_kwargs(kwargs))
90
+
91
+ def expect_oneshot_request(
92
+ self,
93
+ uri: str | URIPattern | Pattern[str],
94
+ **kwargs: Unpack[RequestMatcherKwargs],
95
+ ) -> RequestHandler:
96
+ """Create and register a oneshot request handler, using baked defaults."""
97
+ return self._server.expect_oneshot_request(uri, **self._merge_kwargs(kwargs))
98
+
99
+ def expect_ordered_request(
100
+ self,
101
+ uri: str | URIPattern | Pattern[str],
102
+ **kwargs: Unpack[RequestMatcherKwargs],
103
+ ) -> RequestHandler:
104
+ """Create and register an ordered request handler, using baked defaults."""
105
+ return self._server.expect_ordered_request(uri, **self._merge_kwargs(kwargs))
@@ -29,10 +29,10 @@ class BlockingRequestHandler(RequestHandlerBase):
29
29
  This class should only be instantiated inside the implementation of the :py:class:`BlockingHTTPServer`.
30
30
  """
31
31
 
32
- def __init__(self):
33
- self.response_queue = Queue()
32
+ def __init__(self) -> None:
33
+ self.response_queue: Queue[Response] = Queue()
34
34
 
35
- def respond_with_response(self, response: Response):
35
+ def respond_with_response(self, response: Response) -> None:
36
36
  self.response_queue.put_nowait(response)
37
37
 
38
38
 
@@ -59,11 +59,11 @@ class BlockingHTTPServer(HTTPServerBase):
59
59
 
60
60
  def __init__(
61
61
  self,
62
- host=DEFAULT_LISTEN_HOST,
63
- port=DEFAULT_LISTEN_PORT,
62
+ host: str = DEFAULT_LISTEN_HOST,
63
+ port: int = DEFAULT_LISTEN_PORT,
64
64
  ssl_context: SSLContext | None = None,
65
65
  timeout: int = 30,
66
- ):
66
+ ) -> None:
67
67
  super().__init__(host, port, ssl_context)
68
68
  self.timeout = timeout
69
69
  self.request_queue: Queue[Request] = Queue()
@@ -76,7 +76,7 @@ class BlockingHTTPServer(HTTPServerBase):
76
76
  data: str | bytes | None = None,
77
77
  data_encoding: str = "utf-8",
78
78
  headers: Mapping[str, str] | None = None,
79
- query_string: None | QueryMatcher | str | bytes | Mapping = None,
79
+ query_string: None | QueryMatcher | str | bytes | Mapping[str, str] = None,
80
80
  header_value_matcher: HeaderValueMatcher | None = None,
81
81
  json: Any = UNDEFINED,
82
82
  timeout: int = 30,
@@ -4,7 +4,7 @@ Hooks for pytest-httpserver
4
4
 
5
5
  import os
6
6
  import time
7
- from typing import Callable
7
+ from collections.abc import Callable
8
8
 
9
9
  from werkzeug import Request
10
10
  from werkzeug import Response
@@ -20,7 +20,7 @@ class Chain:
20
20
  similar to reduce.
21
21
  """
22
22
 
23
- def __init__(self, *args: Callable[[Request, Response], Response]):
23
+ def __init__(self, *args: Callable[[Request, Response], Response]) -> None:
24
24
  """
25
25
  :param *args: callable objects specified in the same order they should
26
26
  be called.
@@ -43,13 +43,13 @@ class Delay:
43
43
  Delays returning the response
44
44
  """
45
45
 
46
- def __init__(self, seconds: float):
46
+ def __init__(self, seconds: float) -> None:
47
47
  """
48
48
  :param seconds: seconds to sleep before returning the response
49
49
  """
50
50
  self._seconds = seconds
51
51
 
52
- def _sleep(self):
52
+ def _sleep(self) -> None:
53
53
  """
54
54
  Sleeps for the seconds specified in the constructor
55
55
  """
@@ -65,7 +65,7 @@ class Delay:
65
65
 
66
66
 
67
67
  class Garbage:
68
- def __init__(self, prefix_size: int = 0, suffix_size: int = 0):
68
+ def __init__(self, prefix_size: int = 0, suffix_size: int = 0) -> None:
69
69
  """
70
70
  Adds random bytes to the beginning or to the end of the response data.
71
71