pytest_httpserver 1.0.10__tar.gz → 1.0.12__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 (80) hide show
  1. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/CHANGES.rst +31 -0
  2. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/PKG-INFO +1 -1
  3. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/doc/api.rst +23 -0
  4. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/doc/conf.py +4 -2
  5. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/doc/howto.rst +146 -0
  6. pytest_httpserver-1.0.12/doc/patch.py +16 -0
  7. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/pyproject.toml +25 -25
  8. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/pytest_httpserver/__init__.py +3 -0
  9. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/pytest_httpserver/blocking_httpserver.py +2 -2
  10. pytest_httpserver-1.0.12/pytest_httpserver/hooks.py +103 -0
  11. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/pytest_httpserver/httpserver.py +98 -9
  12. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/examples/test_howto_custom_handler.py +2 -2
  13. pytest_httpserver-1.0.12/tests/examples/test_howto_custom_hooks.py +17 -0
  14. pytest_httpserver-1.0.12/tests/examples/test_howto_hooks.py +11 -0
  15. pytest_httpserver-1.0.12/tests/examples/test_howto_log_querying.py +56 -0
  16. pytest_httpserver-1.0.12/tests/test_hooks.py +102 -0
  17. pytest_httpserver-1.0.12/tests/test_log_querying.py +82 -0
  18. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/test_permanent.py +1 -1
  19. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/test_release.py +16 -4
  20. pytest_httpserver-1.0.12/tests/test_threaded.py +60 -0
  21. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/CONTRIBUTION.md +0 -0
  22. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/LICENSE +0 -0
  23. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/README.md +0 -0
  24. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/doc/Makefile +0 -0
  25. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/doc/_static/.placeholder +0 -0
  26. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/doc/background.rst +0 -0
  27. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/doc/changes.rst +0 -0
  28. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/doc/fixtures.rst +0 -0
  29. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/doc/guide.rst +0 -0
  30. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/doc/index.rst +0 -0
  31. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/doc/tutorial.rst +0 -0
  32. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/doc/upgrade.rst +0 -0
  33. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/example.py +0 -0
  34. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/example_pytest.py +0 -0
  35. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/pytest_httpserver/py.typed +0 -0
  36. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/pytest_httpserver/pytest_plugin.py +0 -0
  37. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/assets/Makefile +0 -0
  38. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/assets/README +0 -0
  39. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/assets/rootCA.cnf +0 -0
  40. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/assets/rootCA.crt +0 -0
  41. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/assets/rootCA.key +0 -0
  42. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/assets/rootCA.srl +0 -0
  43. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/assets/server.cnf +0 -0
  44. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/assets/server.crt +0 -0
  45. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/assets/server.csr +0 -0
  46. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/assets/server.key +0 -0
  47. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/assets/v3.ext +0 -0
  48. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/conftest.py +0 -0
  49. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/examples/test_example_blocking_httpserver.py +0 -0
  50. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/examples/test_example_query_params1.py +0 -0
  51. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/examples/test_example_query_params2.py +0 -0
  52. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/examples/test_howto_authorization_headers.py +0 -0
  53. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/examples/test_howto_case_insensitive_matcher.py +0 -0
  54. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/examples/test_howto_check.py +0 -0
  55. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/examples/test_howto_check_handler_errors.py +0 -0
  56. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/examples/test_howto_header_value_matcher.py +0 -0
  57. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/examples/test_howto_json_matcher.py +0 -0
  58. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/examples/test_howto_query_params_dict.py +0 -0
  59. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/examples/test_howto_query_params_never_do_this.py +0 -0
  60. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/examples/test_howto_query_params_proper_use.py +0 -0
  61. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/examples/test_howto_regexp.py +0 -0
  62. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/examples/test_howto_timeout_requests.py +0 -0
  63. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/examples/test_howto_url_matcher.py +0 -0
  64. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/examples/test_howto_wait_success.py +0 -0
  65. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/test_blocking_httpserver.py +0 -0
  66. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/test_handler_errors.py +0 -0
  67. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/test_headers.py +0 -0
  68. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/test_ip_protocols.py +0 -0
  69. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/test_json_matcher.py +0 -0
  70. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/test_mixed.py +0 -0
  71. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/test_oneshot.py +0 -0
  72. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/test_ordered.py +0 -0
  73. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/test_parse_qs.py +0 -0
  74. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/test_port_changing.py +0 -0
  75. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/test_querymatcher.py +0 -0
  76. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/test_querystring.py +0 -0
  77. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/test_ssl.py +0 -0
  78. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/test_urimatch.py +0 -0
  79. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/test_wait.py +0 -0
  80. {pytest_httpserver-1.0.10 → pytest_httpserver-1.0.12}/tests/test_with_statement.py +0 -0
@@ -2,6 +2,37 @@
2
2
  Release Notes
3
3
  =============
4
4
 
5
+ .. _Release Notes_1.0.12:
6
+
7
+ 1.0.12
8
+ ======
9
+
10
+ .. _Release Notes_1.0.12_Bug Fixes:
11
+
12
+ Bug Fixes
13
+ ---------
14
+
15
+ - Fix pytest-httpserver's own tests related to log querying. No functional
16
+ changes in pytest-httpserver code itself. `#345 <https://github.com/csernazs/pytest-httpserver/issues/345>`_
17
+
18
+
19
+ .. _Release Notes_1.0.11:
20
+
21
+ 1.0.11
22
+ ======
23
+
24
+ .. _Release Notes_1.0.11_New Features:
25
+
26
+ New Features
27
+ ------------
28
+
29
+ - Hooks API
30
+
31
+ - New methods added to query for matching requests in the log.
32
+
33
+ - Threading support to serve requests in parallel
34
+
35
+
5
36
  .. _Release Notes_1.0.10:
6
37
 
7
38
  1.0.10
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pytest_httpserver
3
- Version: 1.0.10
3
+ Version: 1.0.12
4
4
  Summary: pytest-httpserver is a httpserver for pytest
5
5
  Home-page: https://github.com/csernazs/pytest-httpserver
6
6
  License: MIT
@@ -24,6 +24,13 @@ RequestHandler
24
24
  :inherited-members:
25
25
 
26
26
 
27
+ RequestMatcher
28
+ ~~~~~~~~~~~~~~
29
+
30
+ .. autoclass:: RequestMatcher
31
+ :members:
32
+
33
+
27
34
  BlockingHTTPServer
28
35
  ~~~~~~~~~~~~~~~~~~
29
36
 
@@ -93,3 +100,19 @@ by the user.
93
100
 
94
101
  .. autoclass:: pytest_httpserver.httpserver.RequestHandlerList
95
102
  :members:
103
+
104
+
105
+ pytest_httpserver.hooks
106
+ -----------------------
107
+
108
+ .. automodule:: pytest_httpserver.hooks
109
+
110
+
111
+ .. autoclass:: pytest_httpserver.hooks.Chain
112
+ :members:
113
+
114
+ .. autoclass:: pytest_httpserver.hooks.Delay
115
+ :members:
116
+
117
+ .. autoclass:: pytest_httpserver.hooks.Garbage
118
+ :members:
@@ -24,6 +24,7 @@ from typing import Dict
24
24
 
25
25
  sys.path.insert(0, os.path.abspath(".."))
26
26
 
27
+ import doc.patch
27
28
 
28
29
  # -- General configuration ------------------------------------------------
29
30
 
@@ -37,11 +38,12 @@ sys.path.insert(0, os.path.abspath(".."))
37
38
  extensions = [
38
39
  "sphinx.ext.autodoc",
39
40
  "sphinx.ext.intersphinx",
41
+ "sphinx.ext.autosectionlabel",
40
42
  ]
41
43
 
42
44
  intersphinx_mapping = {
43
45
  "python": ("https://docs.python.org/3", (None, "python-inv.txt")),
44
- "werkzeug": ("https://werkzeug.palletsprojects.com/en/2.1.x", None),
46
+ "werkzeug": ("https://werkzeug.palletsprojects.com/en/3.0.x", None),
45
47
  }
46
48
 
47
49
  # Add any paths that contain templates here, relative to this directory.
@@ -66,7 +68,7 @@ author = "Zsolt Cserna"
66
68
  # built documents.
67
69
  #
68
70
  # The short X.Y version.
69
- version = "1.0.10"
71
+ version = "1.0.12"
70
72
  # The full version, including alpha/beta/rc tags.
71
73
  release = version
72
74
 
@@ -512,3 +512,149 @@ Example:
512
512
 
513
513
  .. literalinclude :: ../tests/examples/test_example_blocking_httpserver.py
514
514
  :language: python
515
+
516
+
517
+ Querying the log
518
+ ----------------
519
+
520
+ *pytest-httpserver* keeps a log of request-response pairs in a python list. This
521
+ log can be accessed by the ``log`` attibute of the httpserver instance, but
522
+ there are methods made specifically to query the log.
523
+
524
+ Each of the log querying methods accepts a
525
+ :py:class:`pytest_httpserver.RequestMatcher` object which uses the same matching
526
+ logic which is used by the server itself. Its parameters are the same to the
527
+ parameters specified for the server's `except_request` (and the similar) methods.
528
+
529
+ The methods for querying:
530
+
531
+ * :py:meth:`pytest_httpserver.HTTPServer.get_matching_requests_count` returns
532
+ how many requests are matching in the log as an int
533
+
534
+ * :py:meth:`pytest_httpserver.HTTPServer.assert_request_made` asserts the given
535
+ amount of requests are matching in the log. By default it checks for one (1)
536
+ request but other value can be specified. For example, 0 can be specified to
537
+ check for requests not made.
538
+
539
+ * :py:meth:`pytest_httpserver.HTTPServer.iter_matching_requests` is a generator
540
+ yielding Request-Response tuples of the matching entries in the log. This
541
+ offers greater flexibility (compared to the other methods)
542
+
543
+ Example:
544
+
545
+ .. literalinclude :: ../tests/examples/test_howto_log_querying.py
546
+ :language: python
547
+
548
+
549
+ Serving requests in parallel
550
+ ----------------------------
551
+
552
+ *pytest-httpserver* serves the request in a single-threaded, blocking way. That
553
+ means that if multiple requests are made to it, those will be served one by one.
554
+
555
+ There can be cases where parallel processing is required, for those cases
556
+ *pytest-httpserver* allows running a server which start one thread per request
557
+ handler, so the requests are served in parallel way (depending on Global
558
+ Interpreter Lock this is not truly parallel, but from the I/O point of view it
559
+ is).
560
+
561
+ To set this up, you have two possibilities.
562
+
563
+
564
+ Overriding httpserver fixture
565
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
566
+
567
+ One is to customize how the HTTPServer object is created. This is possible by
568
+ defining the following fixture:
569
+
570
+ .. code:: python
571
+
572
+ @pytest.fixture(scope="session")
573
+ def make_httpserver() -> Iterable[HTTPServer]:
574
+ server = HTTPServer(threaded=True) # set threaded=True to enable thread support
575
+ server.start()
576
+ yield server
577
+ server.clear()
578
+ if server.is_running():
579
+ server.stop()
580
+
581
+
582
+ This will override the ``httpserver`` fixture in your tests.
583
+
584
+ Creating a different httpserver fixture
585
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
586
+
587
+ This way, you can create a different httpserver fixture and you can use it
588
+ besides the main one.
589
+
590
+ .. code:: python
591
+
592
+ @pytest.fixture()
593
+ def threaded() -> Iterable[HTTPServer]:
594
+ server = HTTPServer(threaded=True)
595
+ server.start()
596
+ yield server
597
+ server.clear()
598
+ if server.is_running():
599
+ server.stop()
600
+
601
+
602
+ def test_threaded(threaded: HTTPServer): ...
603
+
604
+
605
+ This will start and stop the server for each tests, which causes about 0.5
606
+ seconds waiting when the server is stopped. It won't override the ``httpserver``
607
+ fixture so you can keep the original single-threaded behavior.
608
+
609
+ .. warning::
610
+ Handler threads which are still running when the test is finished, will be
611
+ left behind and won't be join()ed between the tests. If you want to ensure
612
+ that all threads are properly cleaned up and you want to wait for them,
613
+ consider using the second option (:ref:`Creating a different httpserver fixture`)
614
+ described above.
615
+
616
+
617
+ Adding side effects
618
+ -------------------
619
+
620
+ Sometimes there's a need to add side effects to the handling of the requests.
621
+ Such side effect could be adding some amount of delay to the serving or adding
622
+ some garbage to response data.
623
+
624
+ While these can be achieved by using
625
+ :py:meth:`pytest_httpserver.RequestHandler.respond_with_handler` where you can
626
+ implement your own function to serve the request, *pytest-httpserver* provides a
627
+ hooks API where you can add side effects to request handlers such as
628
+ :py:meth:`pytest_httpserver.RequestHandler.respond_with_json` and others.
629
+ This allows to use the existing API of registering handlers.
630
+
631
+ Example:
632
+
633
+ .. literalinclude :: ../tests/examples/test_howto_hooks.py
634
+ :language: python
635
+
636
+ :py:mod:`pytest_httpserver.hooks` module provides some pre-defined hooks to
637
+ use.
638
+
639
+ You can implement your own hook as well. The requirement is to have a callable
640
+ object (a function) ``Callable[[Request, Response], Response]``. In details:
641
+
642
+ * Parameter :py:class:`werkzeug.Request` which represents the request
643
+ sent by the client.
644
+
645
+ * Parameter :py:class:`werkzeug.Response` which represents the response
646
+ made by the handler.
647
+
648
+ * Returns a :py:class:`werkzeug.Response` object which represents the
649
+ response will be returned to the client.
650
+
651
+
652
+ Example:
653
+
654
+ .. literalinclude :: ../tests/examples/test_howto_custom_hooks.py
655
+ :language: python
656
+
657
+ ``with_post_hook`` can be called multiple times, in this case *pytest-httpserver*
658
+ will register the hooks, and hooks will be called sequentially, one by one. Each
659
+ hook will receive the response what the previous hook returned, and the last
660
+ hook called will return the final response which will be sent back to the client.
@@ -0,0 +1,16 @@
1
+ # this is required to make sphinx able to find references for classes put inside
2
+ # typing.TYPE_CHECKING block
3
+
4
+ from ssl import SSLContext
5
+
6
+ from werkzeug import Request
7
+ from werkzeug import Response
8
+
9
+ import pytest_httpserver.blocking_httpserver
10
+ import pytest_httpserver.httpserver
11
+
12
+ pytest_httpserver.httpserver.SSLContext = SSLContext
13
+ pytest_httpserver.blocking_httpserver.SSLContext = SSLContext
14
+
15
+ pytest_httpserver.blocking_httpserver.Request = Request
16
+ pytest_httpserver.blocking_httpserver.Response = Response
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "pytest_httpserver"
3
- version = "1.0.10"
3
+ version = "1.0.12"
4
4
  description = "pytest-httpserver is a httpserver for pytest"
5
5
  authors = ["Zsolt Cserna <cserna.zsolt@gmail.com>"]
6
6
  license = "MIT"
@@ -38,43 +38,43 @@ pytest_httpserver = "pytest_httpserver.pytest_plugin"
38
38
  optional = true
39
39
 
40
40
  [tool.poetry.group.develop.dependencies]
41
- pre-commit = "^2.20.0"
42
- requests = "^2.28.1"
43
- Sphinx = "^5.1.1"
44
- sphinx-rtd-theme = "^1.0.0"
45
- reno = "^3.5.0"
46
- mypy = "^0.971"
47
- types-requests = "^2.28.9"
48
- pytest = "^7.1.3"
49
- pytest-cov = ">=3,<5"
41
+ pre-commit = ">=2.20,<4.0"
42
+ requests = "*"
43
+ Sphinx = ">=5.1.1,<8.0.0"
44
+ sphinx-rtd-theme = ">=1,<3"
45
+ reno = "*"
46
+ types-requests = "*"
47
+ pytest = ">=7.1.3,<9.0.0"
48
+ pytest-cov = ">=3,<6"
50
49
  coverage = ">=6.4.4,<8.0.0"
51
- types-toml = "^0.10.8"
50
+ types-toml = "*"
52
51
  toml = "^0.10.2"
53
- black = "^23.1.0"
54
- ruff = "^0.2.1"
52
+ black = "*"
53
+ ruff = "*"
54
+ mypy = "*"
55
55
 
56
56
 
57
57
  [tool.poetry.group.doc]
58
58
  optional = true
59
59
 
60
60
  [tool.poetry.group.doc.dependencies]
61
- Sphinx = "^5.1.1"
62
- sphinx-rtd-theme = "^1.0.0"
61
+ Sphinx = ">=5.1.1,<8.0.0"
62
+ sphinx-rtd-theme = ">=1,<3"
63
63
 
64
64
 
65
65
  [tool.poetry.group.test]
66
66
  optional = true
67
67
 
68
68
  [tool.poetry.group.test.dependencies]
69
- pytest = "^7.1.3"
70
- pytest-cov = ">=3,<5"
71
- coverage = ">=6.4.4,<8.0.0"
72
- requests = "^2.28.1"
73
- mypy = "^0.971"
74
- types-requests = "^2.28.9"
75
- pre-commit = "^2.20.0"
76
- types-toml = "^0.10.8"
77
- toml = "^0.10.2"
69
+ pytest = "*"
70
+ pytest-cov = "*"
71
+ coverage = "*"
72
+ requests = "*"
73
+ types-requests = "*"
74
+ pre-commit = "*"
75
+ types-toml = "*"
76
+ toml = "*"
77
+ mypy = "*"
78
78
 
79
79
  [build-system]
80
80
  requires = ["poetry-core>=1.0.0"]
@@ -87,7 +87,7 @@ markers = [
87
87
  ]
88
88
 
89
89
  [tool.mypy]
90
- files = ["pytest_httpserver", "scripts", "tests", "doc"]
90
+ files = ["pytest_httpserver", "scripts", "tests"]
91
91
  implicit_reexport = false
92
92
 
93
93
 
@@ -2,6 +2,7 @@
2
2
  This is package provides the main API for the pytest_httpserver package.
3
3
 
4
4
  """
5
+
5
6
  __all__ = [
6
7
  "HTTPServer",
7
8
  "HTTPServerError",
@@ -10,6 +11,7 @@ __all__ = [
10
11
  "WaitingSettings",
11
12
  "HeaderValueMatcher",
12
13
  "RequestHandler",
14
+ "RequestMatcher",
13
15
  "URIPattern",
14
16
  "URI_DEFAULT",
15
17
  "METHOD_ALL",
@@ -27,5 +29,6 @@ from .httpserver import HTTPServer
27
29
  from .httpserver import HTTPServerError
28
30
  from .httpserver import NoHandlerError
29
31
  from .httpserver import RequestHandler
32
+ from .httpserver import RequestMatcher
30
33
  from .httpserver import URIPattern
31
34
  from .httpserver import WaitingSettings
@@ -18,8 +18,8 @@ from pytest_httpserver.httpserver import URIPattern
18
18
  if TYPE_CHECKING:
19
19
  from ssl import SSLContext
20
20
 
21
- from werkzeug.wrappers import Request
22
- from werkzeug.wrappers import Response
21
+ from werkzeug import Request
22
+ from werkzeug import Response
23
23
 
24
24
 
25
25
  class BlockingRequestHandler(RequestHandlerBase):
@@ -0,0 +1,103 @@
1
+ """
2
+ Hooks for pytest-httpserver
3
+ """
4
+
5
+ import os
6
+ import time
7
+ from typing import Callable
8
+
9
+ from werkzeug import Request
10
+ from werkzeug import Response
11
+
12
+
13
+ class Chain:
14
+ """
15
+ Combine multiple hooks into one callable object
16
+
17
+ Hooks specified will be called one by one.
18
+
19
+ Each hook will receive the response object made by the previous hook,
20
+ similar to reduce.
21
+ """
22
+
23
+ def __init__(self, *args: Callable[[Request, Response], Response]):
24
+ """
25
+ :param *args: callable objects specified in the same order they should
26
+ be called.
27
+ """
28
+ self._hooks = args
29
+
30
+ def __call__(self, request: Request, response: Response) -> Response:
31
+ """
32
+ Calls the callable object one by one. The second and further callable
33
+ objects receive the response returned by the previous one, while the
34
+ first one receives the original response object.
35
+ """
36
+ for hook in self._hooks:
37
+ response = hook(request, response)
38
+ return response
39
+
40
+
41
+ class Delay:
42
+ """
43
+ Delays returning the response
44
+ """
45
+
46
+ def __init__(self, seconds: float):
47
+ """
48
+ :param seconds: seconds to sleep before returning the response
49
+ """
50
+ self._seconds = seconds
51
+
52
+ def _sleep(self):
53
+ """
54
+ Sleeps for the seconds specified in the constructor
55
+ """
56
+ time.sleep(self._seconds)
57
+
58
+ def __call__(self, _request: Request, response: Response) -> Response:
59
+ """
60
+ Delays returning the response object for the time specified in the
61
+ constructor. Returns the original response unmodified.
62
+ """
63
+ self._sleep()
64
+ return response
65
+
66
+
67
+ class Garbage:
68
+ def __init__(self, prefix_size: int = 0, suffix_size: int = 0):
69
+ """
70
+ Adds random bytes to the beginning or to the end of the response data.
71
+
72
+ :param prefix_size: amount of random bytes to be added to the beginning
73
+ of the response data
74
+
75
+ :param suffix_size: amount of random bytes to be added to the end
76
+ of the response data
77
+
78
+ """
79
+ assert prefix_size >= 0, "prefix_size should be positive integer"
80
+ assert suffix_size >= 0, "suffix_size should be positive integer"
81
+ self._prefix_size = prefix_size
82
+ self._suffix_size = suffix_size
83
+
84
+ def _get_garbage_bytes(self, size: int) -> bytes:
85
+ """
86
+ Returns the specified amount of random bytes.
87
+
88
+ :param size: amount of bytes to return
89
+ """
90
+ return os.urandom(size)
91
+
92
+ def __call__(self, _request: Request, response: Response) -> Response:
93
+ """
94
+ Adds random bytes to the beginning or to the end of the response data.
95
+
96
+ New random bytes will be generated for every call.
97
+
98
+ Returns the modified response object.
99
+ """
100
+ prefix = self._get_garbage_bytes(self._prefix_size)
101
+ suffix = self._get_garbage_bytes(self._suffix_size)
102
+ response.set_data(prefix + response.get_data() + suffix)
103
+ return response