Skip to content
Snippets Groups Projects
Commit 3e6dba15 authored by Carsten Schoenert's avatar Carsten Schoenert
Browse files

New upstream version 1.0.9

parent f55f224d
No related branches found
No related tags found
No related merge requests found
......@@ -9,7 +9,7 @@ on:
- master
env:
POETRY_VERSION: "1.3.0"
POETRY_VERSION: "1.7.1"
jobs:
cs:
......@@ -39,7 +39,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11"]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
steps:
......
......@@ -2,6 +2,20 @@
Release Notes
=============
.. _Release Notes_1.0.9:
1.0.9
=====
.. _Release Notes_1.0.9_New Features:
New Features
------------
- Add ``__repr__`` to ``RequestHandler`` object so when it is compared (eg. with
the ``log`` attribute of the server) it will show the matcher parameters.
.. _Release Notes_1.0.8:
1.0.8
......
......@@ -6,3 +6,4 @@ coverage:
default:
target: 90%
threshold: 50%
patch: off
......@@ -66,7 +66,7 @@ author = "Zsolt Cserna"
# built documents.
#
# The short X.Y version.
version = "1.0.8"
version = "1.0.9"
# The full version, including alpha/beta/rc tags.
release = version
......
......@@ -39,6 +39,12 @@ This tells that when the request arrives to the *http://localhost/foobar* URL,
it must respond with the provided json. The library accepts here any python
object which is json serializable. Here, a dict is provided.
.. note::
It is important to specify what response to be sent back to the client
otherwise *pytest-httpserver* will error with ``Matching request handler
found but no response defined`` message on an incoming request.
In the next line, an http request is sent with the *requests* library:
.. code:: python
......
This diff is collapsed.
[tool.poetry]
name = "pytest_httpserver"
version = "1.0.8"
version = "1.0.9"
description = "pytest-httpserver is a httpserver for pytest"
authors = ["Zsolt Cserna <cserna.zsolt@gmail.com>"]
license = "MIT"
......@@ -24,7 +24,7 @@ include = [
]
[tool.poetry.dependencies]
python = ">=3.8,<4.0"
python = ">=3.8"
Werkzeug = ">= 2.0.0"
......
......@@ -136,7 +136,7 @@ class HeaderValueMatcher:
def authorization_header_value_matcher(actual: Optional[str], expected: str) -> bool:
callable = getattr(Authorization, "from_header", None)
if callable is None: # Werkzeug < 2.3.0
callable = werkzeug.http.parse_authorization_header
callable = werkzeug.http.parse_authorization_header # type: ignore[attr-defined]
return callable(actual) == callable(expected)
@staticmethod
......@@ -560,6 +560,15 @@ class RequestHandler(RequestHandlerBase):
def respond_with_response(self, response: Response):
self.request_handler = lambda request: response
def __repr__(self) -> str:
class_name = self.__class__.__name__
retval = (
f"<{class_name} uri={self.matcher.uri!r} method={self.matcher.method!r} "
f"query_string={self.matcher.query_string!r} headers={self.matcher.headers!r} data={self.matcher.data!r} "
f"json={self.matcher.json!r}>"
)
return retval
class RequestHandlerList(list):
"""
......@@ -971,7 +980,15 @@ class HTTPServer(HTTPServerBase): # pylint: disable=too-many-instance-attribute
If `handler_type` is `HandlerType.ORDERED` an ordered request handler is created. Comparing to oneshot handler,
ordered handler also determines the order of the requests to be served. For example if there are two ordered
handlers registered, the first request must hit the first handler, and the second request must hit the second
one, and not vice versa. If one or more ordered handler defined, those must be exhausted first.
one, and not vice versa. If one or more ordered handler defined, those
must be exhausted first.
.. note::
Once this method is called, the response should also be specified by
calling one of the respond methods of the returned
:py:class:`RequestHandler` object, otherwise
:py:class:`NoHandlerError` will be raised on an incoming request.
:param uri: URI of the request. This must be an absolute path starting with ``/``, a
......
---
features:
- |
Add ``__repr__`` to ``RequestHandler`` object so when it is compared (eg. with
the ``log`` attribute of the server) it will show the matcher parameters.
......@@ -16,7 +16,6 @@ def httpserver():
yield server
server.clear()
if server.is_running():
server.stop()
......
......@@ -3,8 +3,6 @@ from typing import List
from typing import Tuple
import pytest
import werkzeug.urls
from werkzeug.datastructures import MultiDict
parse_qsl_semicolon_cases = [
("&", []),
......@@ -22,9 +20,3 @@ parse_qsl_semicolon_cases = [
@pytest.mark.parametrize("qs,expected", parse_qsl_semicolon_cases)
def test_qsl(qs: str, expected: List[Tuple[bytes, bytes]]):
assert urllib.parse.parse_qsl(qs, keep_blank_values=True) == expected
@pytest.mark.skip(reason="skipped to avoid werkzeug warnings")
@pytest.mark.parametrize("qs,expected", parse_qsl_semicolon_cases)
def test_qsl_werkzeug(qs: str, expected: List[Tuple[bytes, bytes]]):
assert werkzeug.urls.url_decode(qs) == MultiDict(expected)
......@@ -106,3 +106,17 @@ def test_response_handler_replaced(httpserver: HTTPServer):
response = requests.get(httpserver.url_for("/foobar"))
assert response.json() == {"foo": "bar"}
assert response.status_code == 200
def test_request_handler_repr(httpserver: HTTPServer):
handler = httpserver.expect_request("/foo", method="POST")
assert (
repr(handler)
== "<RequestHandler uri='/foo' method='POST' query_string=None headers={} data=None json=<UNDEFINED>>"
)
handler = httpserver.expect_request("/query", query_string={"a": "123"})
assert (
repr(handler) == "<RequestHandler uri='/query' method='__ALL' query_string={'a': '123'} "
"headers={} data=None json=<UNDEFINED>>"
)
......@@ -19,6 +19,7 @@ pytestmark = pytest.mark.release
NAME = "pytest-httpserver"
NAME_UNDERSCORE = NAME.replace("-", "_")
PY_MAX_VERSION = (3, 12)
@pytest.fixture(scope="session")
......@@ -120,20 +121,20 @@ def test_python_version(build: Build, pyproject):
pyproject_meta = pyproject["tool"]["poetry"]
wheel_meta = build.wheel.get_meta(version=pyproject_meta["version"])
python_dependency = pyproject_meta["dependencies"]["python"]
m = re.match(r">=(\d+\.\d+),<(\d+\.\d+)", python_dependency)
m = re.match(r">=(\d+\.\d+)", python_dependency)
if m:
min_version, max_version = m.groups()
min_version, *_ = m.groups()
else:
raise ValueError(python_dependency)
min_version_tuple = version_to_tuple(min_version)
max_version_tuple = version_to_tuple(max_version)
for classifier in wheel_meta.get_all("Classifier"):
if classifier.startswith("Programming Language :: Python ::"):
version_tuple = version_to_tuple(classifier.split("::")[-1].strip())
if len(version_tuple) > 1:
assert version_tuple >= min_version_tuple and version_tuple < max_version_tuple
assert version_tuple >= min_version_tuple
assert version_tuple <= PY_MAX_VERSION
def test_wheel_no_extra_contents(build: Build, version: str):
......@@ -168,7 +169,6 @@ def test_sdist_contents(build: Build, version: str):
"pyproject.toml",
"pytest_httpserver",
"README.md",
"setup.py",
"tests",
},
"doc": {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment