Skip to content

Commit 277cece

Browse files
Prepare for 2.1.0 (#528)
1 parent d9aef75 commit 277cece

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+535
-583
lines changed

.github/workflows/main.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ jobs:
3030
strategy:
3131
fail-fast: false
3232
matrix:
33-
python-version: [ 3.9, "3.10", "3.11", "3.12", "3.13"]
33+
python-version: [3.9, "3.10", "3.11", "3.12", "3.13"]
3434
runs-on: windows-latest
3535
if: github.event_name == 'pull_request' || github.event_name == 'push'
3636

@@ -181,7 +181,7 @@ jobs:
181181

182182
- name: Install build dependencies
183183
run: |
184-
pip install cython==3.0.11
184+
pip install cython==3.0.12
185185
pip install --upgrade build
186186
187187
- name: Compile Cython extensions
@@ -227,10 +227,10 @@ jobs:
227227
merge-multiple: true
228228
path: dist
229229

230-
- name: Use Python 3.11
230+
- name: Use Python 3.12
231231
uses: actions/setup-python@v5
232232
with:
233-
python-version: '3.11'
233+
python-version: '3.12'
234234

235235
- name: Install dependencies
236236
run: |

CHANGELOG.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,62 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [2.1.0] - 2025-03-23
9+
10+
- Remove support for Python 3.8, by @bymoye.
11+
- Fix a bug in the `ClientSession`, happening when the server returns a response
12+
body without specifying `Content-Length` and without specifying a
13+
`Transfer-Encoding`.
14+
- Fix a bug in the `ClientSession`, happening when a server closes the
15+
connection, and the response content is not set as completed.
16+
- Add a default `User-Agent` to web requests sent using the `ClientSession`,
17+
the user agent is: `python-blacksheep/{__version__}`.
18+
- Add an async method `raise_for_status` to the `Response` object, which raises
19+
an exception of type `FailedRequestError` if the response status is not in
20+
the range **200-299**. The method is asynchronous because in case of failure
21+
it waits for the response body to be downloaded, to include it in the raised
22+
exception.
23+
- Add support for specifying a prefix for the `Router`, and for configuring a
24+
global prefix for all routes using the env variable `APP_ROUTE_PREFIX`.
25+
If specified, the prefix is applied to all routes registered in the
26+
application router. The prefix is used automatically by the
27+
`serve_files` method, the `get_absolute_url_to_path` method, and by the
28+
OpenAPI UI feature, to serve documentation to the correct path.
29+
This feature is useful when exposing applications behind proxies using
30+
path based routing, to maintain the same path between the proxy server and
31+
the BlackSheep application. This is an alternative approach to the one used
32+
by the `root_path` offered by `ASGI` (still supported in `BlackSheep`).
33+
ASGI `root_path` and route prefix in BlackSheep are alternative ways to
34+
address the same issue, and should not be used together.
35+
- Improve the OpenAPI UI to support router prefixes, and fetching the
36+
specification file using relative links.
37+
- Upgrade to `Cython` to `3.0.12` in the GitHub Workflow.
38+
- Handle setuptools warning: _SetuptoolsDeprecationWarning: License classifiers are deprecated_.
39+
- Improve `pyproject.toml` to use `tool.setuptools.packages.find`.
40+
- Add the missing "utf8" encoding to the `request.path` property decode call.
41+
- Add a middleware to handle automatic redirects from URLs that do not end with
42+
a "/" towards the same path with a trailing slash. This is useful for
43+
endpoints that serve HTML documents, to ensure that relative URLs in the
44+
response body are correctly resolved
45+
(`from blacksheep.server.redirects import get_trailing_slash_middleware`).
46+
- Add a built-in strategy to handle startup errors and display an error page
47+
when an application fails during initialization, in
48+
`blacksheep.server.diagnostics.get_diagnostic_app`. Error details are not
49+
displayed by default, but can be displayed setting the environment variable
50+
`APP_SHOW_ERROR_DETAILS` to a value such as `1` or `true`.
51+
- Use `asyncio_mode=auto` for `pytest` (remove `@pytest.mark.asyncio` decorators).
52+
- Use `Python 3.12` to publish the package, in the GitHub Workflow.
53+
- Modify the `Application` object to instantiate requests in a dedicated method
54+
`instantiate_request`. This is to better support code that modifies how
55+
incoming requests are created.
56+
- Modify the `OpenAPIHandler` class to support specifying the list of `Server`
57+
objects in the constructor.
58+
- Update `essentials-openapi` pinned version, by @stollero.
59+
- Bump jinja2 from 3.1.4 to 3.1.6.
60+
- Bump cryptography from 44.0.0 to 44.0.1.
61+
- Remove `py` from the list of dependencies in `requirements.txt`.
62+
- Correct some docstrings.
63+
864
## [2.0.8] - 2025-01-25
965

1066
- Add Python 3.13 to the build matrix and several maintenance fixes, by @waketzheng.

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ format:
9090
check-flake8:
9191
@echo "$(BOLD)Checking flake8$(RESET)"
9292
@flake8 blacksheep 2>&1
93+
@flake8 itests 2>&1
9394
@flake8 tests 2>&1
9495

9596

blacksheep/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"""
55

66
__author__ = "Roberto Prevato <roberto.prevato@gmail.com>"
7-
__version__ = "2.0.8"
7+
__version__ = "2.1.0"
88

99
from .contents import Content as Content
1010
from .contents import FormContent as FormContent

blacksheep/client/connection.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ def connection_lost(self, exc) -> None:
319319
self.response.content, IncomingContent
320320
):
321321
self.response.content.exc = ConnectionLostError()
322+
self.response.content.complete.set()
322323

323324
if self._pending_task:
324325
self.response_ready.set()
@@ -341,6 +342,11 @@ def on_headers_complete(self) -> None:
341342

342343
def _has_content(self) -> bool:
343344
assert self.response is not None
345+
346+
content_type = self.response.get_first_header(b"content-type")
347+
if content_type:
348+
return True
349+
344350
content_length = self.response.get_first_header(b"content-length")
345351

346352
if content_length:

blacksheep/client/session.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from typing import Any, AnyStr, Callable, Dict, List, Optional, Tuple, Type, Union, cast
55
from urllib.parse import urlencode
66

7-
from blacksheep import URL, Content, InvalidURL, Request, Response
7+
from blacksheep import URL, Content, InvalidURL, Request, Response, __version__
88
from blacksheep.common.types import HeadersType, ParamsType, URLType, normalize_headers
99
from blacksheep.middlewares import get_middlewares_chain
1010
from blacksheep.utils.aio import get_running_loop
@@ -52,6 +52,8 @@ def __init__(self, request, cookies: Optional[CookieJar] = None):
5252

5353

5454
class ClientSession:
55+
USER_AGENT = f"python-blacksheep/{__version__}".encode("utf-8")
56+
5557
def __init__(
5658
self,
5759
loop: Optional[AbstractEventLoop] = None,
@@ -330,6 +332,9 @@ async def send(self, request: Request) -> Response:
330332
async def _send_core(self, request: Request) -> Response:
331333
self.check_permanent_redirects(request)
332334

335+
if not request.has_header(b"user-agent"):
336+
request.add_header(b"user-agent", self.USER_AGENT)
337+
333338
return await self._send_using_connection(request)
334339

335340
async def _send_using_connection(self, request, attempt: int = 1) -> Response:

blacksheep/common/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,11 @@ def extend(obj, cls):
66
performance fee, so that said fee is paid only when features are used.
77
"""
88
base_cls = obj.__class__
9-
base_cls_name = obj.__class__.__name__ + "_" + cls.__name__
9+
10+
# Check if the mixin is already applied
11+
if cls in base_cls.__mro__:
12+
return
13+
14+
# Create a new class that combines the mixin and the original class
15+
base_cls_name = f"{base_cls.__name__}_{cls.__name__}"
1016
obj.__class__ = type(base_cls_name, (cls, base_cls), {})

blacksheep/contents.pyi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ def parse_www_form(content: str) -> Dict[str, Union[str, List[str]]]:
103103
"""Parses application/x-www-form-urlencoded content"""
104104

105105
def write_www_form_urlencoded(
106-
data: Union[Dict[str, str], List[Tuple[str, str]]]
106+
data: Union[Dict[str, str], List[Tuple[str, str]]],
107107
) -> bytes: ...
108108

109109
class ServerSentEvent:

blacksheep/exceptions.pxd

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,7 @@ cdef class InvalidArgument(Exception):
2727

2828
cdef class InvalidOperation(Exception):
2929
pass
30+
31+
32+
cdef class FailedRequestError(HTTPException):
33+
cdef public str data

blacksheep/exceptions.pyi

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,11 @@ class InternalServerError(HTTPException):
5050
class NotImplementedByServer(HTTPException):
5151
def __init__(self, message: str = "Not Implemented"):
5252
super().__init__(501, message)
53+
54+
class FailedRequestError(HTTPException):
55+
def __init__(self, status: int, data: str) -> None:
56+
super().__init__(
57+
status,
58+
f"The response status code does not indicate success: {status}. Response body: {data}",
59+
)
60+
self.data = data

0 commit comments

Comments
 (0)