Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-httptools for
openSUSE:Factory checked in at 2026-03-30 18:29:40
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-httptools (Old)
and /work/SRC/openSUSE:Factory/.python-httptools.new.1999 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-httptools"
Mon Mar 30 18:29:40 2026 rev:8 rq:1343385 version:0.7.1
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-httptools/python-httptools.changes
2024-01-07 21:38:55.068997417 +0100
+++
/work/SRC/openSUSE:Factory/.python-httptools.new.1999/python-httptools.changes
2026-03-30 18:29:42.199090362 +0200
@@ -1,0 +2,12 @@
+Fri Mar 27 13:52:03 UTC 2026 - Georg Pfuetzenreuter
<[email protected]>
+
+- Update to 0.7.1:
+ * Drop Python 3.8, add Python 3.14
+ * Bump llhttp to 9.3.0 (by @ngoldbaum @Carreau @fantix in
+ 59bf94f for #129)
+ * Static Type-checking for httptools (#100) (by @Vizonex
+ @KRRT7 in b55f5fe for #100)
+- Update vendored llhttp to 9.3.0
+- Remove test conditionals for obsolete distributions
+
+-------------------------------------------------------------------
Old:
----
httptools-0.6.1.tar.gz
llhttp-release-v6.0.6.tar.gz
New:
----
httptools-0.7.1.tar.gz
llhttp-release-v9.3.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-httptools.spec ++++++
--- /var/tmp/diff_new_pack.RaQUKs/_old 2026-03-30 18:29:42.935120953 +0200
+++ /var/tmp/diff_new_pack.RaQUKs/_new 2026-03-30 18:29:42.939121119 +0200
@@ -1,7 +1,7 @@
#
# spec file for package python-httptools
#
-# Copyright (c) 2024 SUSE LLC
+# Copyright (c) 2026 SUSE LLC and contributors
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -18,16 +18,15 @@
%{?sle15_python_module_pythons}
Name: python-httptools
-Version: 0.6.1
+Version: 0.7.1
Release: 0
Summary: Python framework independent HTTP protocol utils
License: MIT
Group: Development/Languages/Python
URL: https://github.com/MagicStack/httptools
Source0:
https://github.com/MagicStack/httptools/archive/v%{version}.tar.gz#/httptools-%{version}.tar.gz
-Source1:
https://github.com/nodejs/llhttp/archive/refs/tags/release/v6.0.6.tar.gz#/llhttp-release-v6.0.6.tar.gz
+Source1:
https://github.com/nodejs/llhttp/archive/refs/tags/release/v9.3.0.tar.gz#/llhttp-release-v9.3.0.tar.gz
Source2:
https://github.com/nodejs/http-parser/archive/refs/tags/v2.9.4.tar.gz#/http-parser-2.9.4.tar.gz
-# BuildRequires: %{python_module Cython >= 0.29.24 with %python-Cython < 3}
BuildRequires: %{python_module Cython}
BuildRequires: %{python_module pip}
BuildRequires: %{python_module pytest}
@@ -57,10 +56,7 @@
%python_expand %fdupes %{buildroot}%{$python_sitearch}
%check
-%if 0%{suse_version} >= 1550
-# pytest on suse <= 15.4 does not support the required pytest importlib import
mode
-%pytest_arch -k 'not test_parser_response_1'
-%endif
+%pytest_arch
%files %{python_files}
%doc README.md
++++++ httptools-0.6.1.tar.gz -> httptools-0.7.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/httptools-0.6.1/.github/workflows/release.yml
new/httptools-0.7.1/.github/workflows/release.yml
--- old/httptools-0.6.1/.github/workflows/release.yml 2023-10-16
19:30:31.000000000 +0200
+++ new/httptools-0.7.1/.github/workflows/release.yml 2025-10-10
05:41:45.000000000 +0200
@@ -5,7 +5,7 @@
branches:
- "master"
- "ci"
- - "[0-9]+.[0-9x]+*"
+ - "[0-9]+.[0-9]+.[0-9x]+*"
paths:
- "httptools/_version.py"
@@ -37,9 +37,9 @@
mkdir -p dist/
echo "${VERSION}" > dist/VERSION
- - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
# v3.1.3
+ - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882
# v4.4.3
with:
- name: dist
+ name: dist-version
path: dist/
build-sdist:
@@ -47,22 +47,21 @@
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
+ - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
with:
fetch-depth: 50
submodules: true
- - name: Set up Python
- uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 #
v4.7.1
+ - name: Install the latest version of uv
+ uses: astral-sh/setup-uv@eb1897b8dc4b5d5bfe39a428a8f2304605e0983c #
7.0.0
- name: Build source distribution
run: |
- python -m pip install -U setuptools wheel pip
- python setup.py sdist
+ uv build --sdist
- - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
# v3.1.3
+ - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882
# v4.4.3
with:
- name: dist
+ name: dist-sdist
path: dist/*.tar.*
build-wheels:
@@ -73,19 +72,17 @@
os: [ubuntu-latest, macos-latest, windows-latest]
cibw_arch: ["auto64", "aarch64", "universal2"]
cibw_python:
- - "cp38-*"
- - "cp39-*"
- - "cp310-*"
- - "cp311-*"
- - "cp312-*"
+ - "cp39"
+ - "cp310"
+ - "cp311"
+ - "cp312"
+ - "cp313"
+ - "cp314"
exclude:
- os: ubuntu-latest
cibw_arch: universal2
- os: macos-latest
cibw_arch: aarch64
- - os: macos-latest
- cibw_python: "cp37-*"
- cibw_arch: universal2
- os: windows-latest
cibw_arch: universal2
- os: windows-latest
@@ -99,30 +96,30 @@
PIP_DISABLE_PIP_VERSION_CHECK: 1
steps:
- - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
+ - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
with:
fetch-depth: 50
submodules: true
- name: Set up QEMU
if: matrix.os == 'ubuntu-latest' && matrix.cibw_arch == 'aarch64'
- uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3
# v3.0.0
+ uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf
# v3.2.0
with:
platforms: arm64
- - uses: pypa/cibuildwheel@fff9ec32ed25a9c576750c91e06b410ed0c15db7 #
v2.16.2
+ - uses: pypa/cibuildwheel@7c619efba910c04005a835b110b057fc28fd6e93 #
v3.2.0
env:
CIBW_BUILD_VERBOSITY: 1
- CIBW_BUILD: ${{ matrix.cibw_python }}
+ CIBW_BUILD: ${{ matrix.cibw_python }}-*
CIBW_ARCHS: ${{ matrix.cibw_arch }}
CIBW_TEST_EXTRAS: "test"
CIBW_TEST_COMMAND: "python {project}/tests/__init__.py"
CIBW_TEST_COMMAND_WINDOWS: "python {project}\\tests\\__init__.py"
CIBW_TEST_SKIP: "*universal2:arm64"
- - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
# v3.1.3
+ - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882
# v4.4.3
with:
- name: dist
+ name: dist-${{ matrix.os }}-${{ matrix.cibw_arch }}-${{
matrix.cibw_python }}
path: wheelhouse/*.whl
publish:
@@ -130,14 +127,15 @@
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
+ - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
with:
fetch-depth: 5
submodules: false
- - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a
# v3.0.2
+ - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16
# v4.1.8
with:
- name: dist
+ pattern: dist-*
+ merge-multiple: true
path: dist/
- name: Extract Release Version
@@ -171,7 +169,7 @@
ls -al dist/
- name: Upload to PyPI
- uses:
pypa/gh-action-pypi-publish@b7f401de30cb6434a1e19f805ff006643653240e # v1.8.10
+ uses:
pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
with:
user: __token__
password: ${{ secrets.PYPI_TOKEN }}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/httptools-0.6.1/.github/workflows/tests.yml
new/httptools-0.7.1/.github/workflows/tests.yml
--- old/httptools-0.6.1/.github/workflows/tests.yml 2023-10-16
19:30:31.000000000 +0200
+++ new/httptools-0.7.1/.github/workflows/tests.yml 2025-10-10
05:41:45.000000000 +0200
@@ -14,14 +14,14 @@
runs-on: ${{ matrix.os }}
strategy:
matrix:
- python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
+ python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
os: [windows-latest, ubuntu-latest, macos-latest]
env:
PIP_DISABLE_PIP_VERSION_CHECK: 1
steps:
- - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
+ - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
with:
fetch-depth: 50
submodules: true
@@ -37,7 +37,7 @@
__version__\s*=\s*(?:['"])([[:PEP440:]])(?:['"])
- name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 #
v4.7.1
+ uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 #
v5.2.0
if: steps.release.outputs.version == 0
with:
python-version: ${{ matrix.python-version }}
@@ -46,5 +46,6 @@
if: steps.release.outputs.version == 0
run: |
python -m pip install -U pip setuptools wheel
- python -m pip install -e .[test]
- python -m unittest -v tests.suite
+ python -m pip install .[test]
+ cd tests
+ python -m unittest -v
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/httptools-0.6.1/MANIFEST.in
new/httptools-0.7.1/MANIFEST.in
--- old/httptools-0.6.1/MANIFEST.in 2023-10-16 19:30:31.000000000 +0200
+++ new/httptools-0.7.1/MANIFEST.in 2025-10-10 05:41:45.000000000 +0200
@@ -1,2 +1,3 @@
+recursive-include httptools *.pxd *.pyx
recursive-include vendor *.c *.h LICENSE* README*
include MANIFEST.in LICENSE
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/httptools-0.6.1/Makefile new/httptools-0.7.1/Makefile
--- old/httptools-0.6.1/Makefile 2023-10-16 19:30:31.000000000 +0200
+++ new/httptools-0.7.1/Makefile 2025-10-10 05:41:45.000000000 +0200
@@ -3,27 +3,26 @@
PYTHON ?= python3
ROOT = $(dir $(realpath $(firstword $(MAKEFILE_LIST))))
-
+UV := $(shell command -v uv 2> /dev/null)
+ifdef UV
+ PYTHON := uv run
+ PIP := uv pip
+else
+ PIP := pip
+endif
compile:
- python3 setup.py build_ext --inplace
-
-
-release: compile test
- python3 setup.py sdist upload
-
+ $(PIP) install -e .
test: compile
- python3 -m unittest -v
+ $(PYTHON) -m unittest -v
clean:
find $(ROOT)/httptools/parser -name '*.c' | xargs rm -f
+ find $(ROOT)/httptools/parser -name '*.so' | xargs rm -f
find $(ROOT)/httptools/parser -name '*.html' | xargs rm -f
+ rm -rf build
distclean: clean
git --git-dir="$(ROOT)/vendor/http-parser/.git" clean -dfx
git --git-dir="$(ROOT)/vendor/llhttp/.git" clean -dfx
-
-
-testinstalled:
- cd /tmp && $(PYTHON) $(ROOT)/tests/__init__.py
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/httptools-0.6.1/README.md
new/httptools-0.7.1/README.md
--- old/httptools-0.6.1/README.md 2023-10-16 19:30:31.000000000 +0200
+++ new/httptools-0.7.1/README.md 2025-10-10 05:41:45.000000000 +0200
@@ -99,9 +99,7 @@
3. Activate the environment with `source envname/bin/activate`
-4. Install development requirements with `pip install -e .[test]`
-
-5. Run `make` and `make test`.
+4. Run `make` and `make test`.
# License
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/httptools-0.6.1/httptools/_version.py
new/httptools-0.7.1/httptools/_version.py
--- old/httptools-0.6.1/httptools/_version.py 2023-10-16 19:30:31.000000000
+0200
+++ new/httptools-0.7.1/httptools/_version.py 2025-10-10 05:41:45.000000000
+0200
@@ -10,4 +10,4 @@
# supported platforms, publish the packages on PyPI, merge the PR
# to the target branch, create a Git tag pointing to the commit.
-__version__ = '0.6.1'
+__version__ = '0.7.1'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/httptools-0.6.1/httptools/parser/__init__.py
new/httptools-0.7.1/httptools/parser/__init__.py
--- old/httptools-0.6.1/httptools/parser/__init__.py 2023-10-16
19:30:31.000000000 +0200
+++ new/httptools-0.7.1/httptools/parser/__init__.py 2025-10-10
05:41:45.000000000 +0200
@@ -1,3 +1,4 @@
+from .protocol import HTTPProtocol
from .parser import * # NoQA
from .errors import * # NoQA
from .url_parser import * # NoQA
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/httptools-0.6.1/httptools/parser/cparser.pxd
new/httptools-0.7.1/httptools/parser/cparser.pxd
--- old/httptools-0.6.1/httptools/parser/cparser.pxd 2023-10-16
19:30:31.000000000 +0200
+++ new/httptools-0.7.1/httptools/parser/cparser.pxd 2025-10-10
05:41:45.000000000 +0200
@@ -154,3 +154,14 @@
const char* llhttp_method_name(llhttp_method_t method)
void llhttp_set_error_reason(llhttp_t* parser, const char* reason);
+
+ void llhttp_set_lenient_headers(llhttp_t* parser, bint enabled);
+ void llhttp_set_lenient_chunked_length(llhttp_t* parser, bint enabled);
+ void llhttp_set_lenient_keep_alive(llhttp_t* parser, bint enabled);
+ void llhttp_set_lenient_transfer_encoding(llhttp_t* parser, bint enabled);
+ void llhttp_set_lenient_version(llhttp_t* parser, bint enabled);
+ void llhttp_set_lenient_data_after_close(llhttp_t* parser, bint enabled);
+ void llhttp_set_lenient_optional_lf_after_cr(llhttp_t* parser, bint
enabled);
+ void llhttp_set_lenient_optional_cr_before_lf(llhttp_t* parser, bint
enabled);
+ void llhttp_set_lenient_optional_crlf_after_chunk(llhttp_t* parser, bint
enabled);
+ void llhttp_set_lenient_spaces_after_chunk_size(llhttp_t* parser, bint
enabled);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/httptools-0.6.1/httptools/parser/parser.pyi
new/httptools-0.7.1/httptools/parser/parser.pyi
--- old/httptools-0.6.1/httptools/parser/parser.pyi 1970-01-01
01:00:00.000000000 +0100
+++ new/httptools-0.7.1/httptools/parser/parser.pyi 2025-10-10
05:41:45.000000000 +0200
@@ -0,0 +1,57 @@
+from typing import Union, Any
+from array import array
+from .protocol import HTTPProtocol
+
+class HttpParser:
+ def __init__(self, protocol: Union[HTTPProtocol, Any]) -> None:
+ """
+ protocol -- a Python object with the following methods
+ (all optional):
+
+ - on_message_begin()
+ - on_url(url: bytes)
+ - on_header(name: bytes, value: bytes)
+ - on_headers_complete()
+ - on_body(body: bytes)
+ - on_message_complete()
+ - on_chunk_header()
+ - on_chunk_complete()
+ - on_status(status: bytes)
+ """
+
+ def get_http_version(self) -> str:
+ """Return an HTTP protocol version."""
+ ...
+
+ def should_keep_alive(self) -> bool:
+ """Return ``True`` if keep-alive mode is preferred."""
+ ...
+
+ def should_upgrade(self) -> bool:
+ """Return ``True`` if the parsed request is a valid Upgrade request.
+ The method exposes a flag set just before on_headers_complete.
+ Calling this method earlier will only yield `False`."""
+ ...
+
+ def feed_data(self, data: Union[bytes, bytearray, memoryview, array]) ->
None:
+ """Feed data to the parser.
+
+ Will eventually trigger callbacks on the ``protocol``
+ object.
+
+ On HTTP upgrade, this method will raise an
+ ``HttpParserUpgrade`` exception, with its sole argument
+ set to the offset of the non-HTTP data in ``data``.
+ """
+
+class HttpRequestParser(HttpParser):
+ """Used for parsing http requests from the server's side"""
+
+ def get_method(self) -> bytes:
+ """Return HTTP request method (GET, HEAD, etc)"""
+
+class HttpResponseParser(HttpParser):
+ """Used for parsing http requests from the client's side"""
+
+ def get_status_code(self) -> int:
+ """Return the status code of the HTTP response"""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/httptools-0.6.1/httptools/parser/parser.pyx
new/httptools-0.7.1/httptools/parser/parser.pyx
--- old/httptools-0.6.1/httptools/parser/parser.pyx 2023-10-16
19:30:31.000000000 +0200
+++ new/httptools-0.7.1/httptools/parser/parser.pyx 2025-10-10
05:41:45.000000000 +0200
@@ -1,6 +1,8 @@
#cython: language_level=3
from __future__ import print_function
+from typing import Optional
+
from cpython.mem cimport PyMem_Malloc, PyMem_Free
from cpython cimport PyObject_GetBuffer, PyBuffer_Release, PyBUF_SIMPLE, \
Py_buffer, PyBytes_AsString
@@ -144,6 +146,51 @@
### Public API ###
+ def set_dangerous_leniencies(
+ self,
+ lenient_headers: Optional[bool] = None,
+ lenient_chunked_length: Optional[bool] = None,
+ lenient_keep_alive: Optional[bool] = None,
+ lenient_transfer_encoding: Optional[bool] = None,
+ lenient_version: Optional[bool] = None,
+ lenient_data_after_close: Optional[bool] = None,
+ lenient_optional_lf_after_cr: Optional[bool] = None,
+ lenient_optional_cr_before_lf: Optional[bool] = None,
+ lenient_optional_crlf_after_chunk: Optional[bool] = None,
+ lenient_spaces_after_chunk_size: Optional[bool] = None,
+ ):
+ cdef cparser.llhttp_t* parser = self._cparser
+ if lenient_headers is not None:
+ cparser.llhttp_set_lenient_headers(
+ parser, lenient_headers)
+ if lenient_chunked_length is not None:
+ cparser.llhttp_set_lenient_chunked_length(
+ parser, lenient_chunked_length)
+ if lenient_keep_alive is not None:
+ cparser.llhttp_set_lenient_keep_alive(
+ parser, lenient_keep_alive)
+ if lenient_transfer_encoding is not None:
+ cparser.llhttp_set_lenient_transfer_encoding(
+ parser, lenient_transfer_encoding)
+ if lenient_version is not None:
+ cparser.llhttp_set_lenient_version(
+ parser, lenient_version)
+ if lenient_data_after_close is not None:
+ cparser.llhttp_set_lenient_data_after_close(
+ parser, lenient_data_after_close)
+ if lenient_optional_lf_after_cr is not None:
+ cparser.llhttp_set_lenient_optional_lf_after_cr(
+ parser, lenient_optional_lf_after_cr)
+ if lenient_optional_cr_before_lf is not None:
+ cparser.llhttp_set_lenient_optional_cr_before_lf(
+ parser, lenient_optional_cr_before_lf)
+ if lenient_optional_crlf_after_chunk is not None:
+ cparser.llhttp_set_lenient_optional_crlf_after_chunk(
+ parser, lenient_optional_crlf_after_chunk)
+ if lenient_spaces_after_chunk_size is not None:
+ cparser.llhttp_set_lenient_spaces_after_chunk_size(
+ parser, lenient_spaces_after_chunk_size)
+
def get_http_version(self):
cdef cparser.llhttp_t* parser = self._cparser
return '{}.{}'.format(parser.http_major, parser.http_minor)
@@ -161,7 +208,7 @@
cparser.llhttp_errno_t err
Py_buffer *buf
bint owning_buf = False
- char* err_pos
+ const char* err_pos
if PyMemoryView_Check(data):
buf = PyMemoryView_GET_BUFFER(data)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/httptools-0.6.1/httptools/parser/protocol.py
new/httptools-0.7.1/httptools/parser/protocol.py
--- old/httptools-0.6.1/httptools/parser/protocol.py 1970-01-01
01:00:00.000000000 +0100
+++ new/httptools-0.7.1/httptools/parser/protocol.py 2025-10-10
05:41:45.000000000 +0200
@@ -0,0 +1,15 @@
+from typing import Protocol
+
+
+class HTTPProtocol(Protocol):
+ """Used for providing static type-checking when parsing through the http
protocol"""
+
+ def on_message_begin() -> None: ...
+ def on_url(url: bytes) -> None: ...
+ def on_header(name: bytes, value: bytes) -> None: ...
+ def on_headers_complete() -> None: ...
+ def on_body(body: bytes) -> None: ...
+ def on_message_complete() -> None: ...
+ def on_chunk_header() -> None: ...
+ def on_chunk_complete() -> None: ...
+ def on_status(status: bytes) -> None: ...
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/httptools-0.6.1/httptools/parser/url_parser.pyi
new/httptools-0.7.1/httptools/parser/url_parser.pyi
--- old/httptools-0.6.1/httptools/parser/url_parser.pyi 1970-01-01
01:00:00.000000000 +0100
+++ new/httptools-0.7.1/httptools/parser/url_parser.pyi 2025-10-10
05:41:45.000000000 +0200
@@ -0,0 +1,27 @@
+from typing import Union
+from array import array
+
+class URL:
+ schema: bytes
+ host: bytes
+ port: int
+ path: bytes
+ query: bytes
+ fragment: bytes
+ userinfo: bytes
+
+def parse_url(url: Union[bytes, bytearray, memoryview, array]) -> URL:
+ """Parse URL strings into a structured Python object.
+
+ Returns an instance of ``httptools.URL`` class with the
+ following attributes:
+
+ - schema: bytes
+ - host: bytes
+ - port: int
+ - path: bytes
+ - query: bytes
+ - fragment: bytes
+ - userinfo: bytes
+ """
+ ...
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/httptools-0.6.1/pyproject.toml
new/httptools-0.7.1/pyproject.toml
--- old/httptools-0.6.1/pyproject.toml 1970-01-01 01:00:00.000000000 +0100
+++ new/httptools-0.7.1/pyproject.toml 2025-10-10 05:41:45.000000000 +0200
@@ -0,0 +1,29 @@
+[build-system]
+build-backend = "setuptools.build_meta"
+requires = ["setuptools==80.9.0"]
+
+[project]
+name = "httptools"
+dynamic = ["version"]
+classifiers = [
+ "Intended Audience :: Developers",
+ "Programming Language :: Python :: 3",
+ "Operating System :: POSIX",
+ "Operating System :: MacOS :: MacOS X",
+ "Environment :: Web Environment",
+ "Development Status :: 5 - Production/Stable",
+]
+requires-python = ">=3.9"
+authors = [
+ {name = "Yury Selivanov", email="[email protected]"},
+]
+license = "MIT"
+license-files = ["LICENSE"]
+description = "A collection of framework independent HTTP protocol utils."
+readme = "README.md"
+
+[project.urls]
+Homepage = "https://github.com/MagicStack/httptools"
+
+[project.optional-dependencies]
+test = [] # for backward compatibility
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/httptools-0.6.1/setup.py new/httptools-0.7.1/setup.py
--- old/httptools-0.6.1/setup.py 2023-10-16 19:30:31.000000000 +0200
+++ new/httptools-0.7.1/setup.py 2025-10-10 05:41:45.000000000 +0200
@@ -1,21 +1,17 @@
import sys
-vi = sys.version_info
-if vi < (3, 8):
- raise RuntimeError('httptools require Python 3.8 or greater')
-else:
- import os.path
- import pathlib
+import os.path
+import pathlib
- from setuptools import setup, Extension
- from setuptools.command.build_ext import build_ext as build_ext
+from setuptools import setup, Extension
+from setuptools.command.build_ext import build_ext as build_ext
CFLAGS = ['-O2']
ROOT = pathlib.Path(__file__).parent
-CYTHON_DEPENDENCY = 'Cython(>=0.29.24,<0.30.0)'
+CYTHON_DEPENDENCY = 'Cython>=3.1.0'
class httptools_build_ext(build_ext):
@@ -52,6 +48,8 @@
self.cython_always = False
self.cython_annotate = None
self.cython_directives = None
+ if 'editable_wheel' in sys.argv:
+ self.inplace = True
def finalize_options(self):
# finalize_options() may be called multiple times on the
@@ -82,12 +80,9 @@
try:
import Cython
except ImportError:
- raise RuntimeError(
- 'please install Cython to compile httptools from source')
+ import setuptools.build_meta
- if Cython.__version__ < '0.29':
- raise RuntimeError(
- 'httptools requires Cython version 0.29 or greater')
+ raise
setuptools.build_meta.SetupRequirementsError([CYTHON_DEPENDENCY])
from Cython.Build import cythonize
@@ -145,10 +140,6 @@
super().build_extensions()
-with open(str(ROOT / 'README.md')) as f:
- long_description = f.read()
-
-
with open(str(ROOT / 'httptools' / '_version.py')) as f:
for line in f:
if line.startswith('__version__ ='):
@@ -160,36 +151,10 @@
'unable to read the version from httptools/_version.py')
-setup_requires = []
-
-if (not (ROOT / 'httptools' / 'parser' / 'parser.c').exists() or
- '--cython-always' in sys.argv):
- # No Cython output, require Cython to build.
- setup_requires.append(CYTHON_DEPENDENCY)
-
-
setup(
- name='httptools',
version=VERSION,
- description='A collection of framework independent HTTP protocol utils.',
- long_description=long_description,
- long_description_content_type='text/markdown',
- url='https://github.com/MagicStack/httptools',
- classifiers=[
- 'License :: OSI Approved :: MIT License',
- 'Intended Audience :: Developers',
- 'Programming Language :: Python :: 3',
- 'Operating System :: POSIX',
- 'Operating System :: MacOS :: MacOS X',
- 'Environment :: Web Environment',
- 'Development Status :: 5 - Production/Stable',
- ],
platforms=['macOS', 'POSIX', 'Windows'],
- python_requires='>=3.8.0',
zip_safe=False,
- author='Yury Selivanov',
- author_email='[email protected]',
- license='MIT',
packages=['httptools', 'httptools.parser'],
cmdclass={
'build_ext': httptools_build_ext,
@@ -212,11 +177,4 @@
],
include_package_data=True,
exclude_package_data={"": ["*.c", "*.h"]},
- test_suite='tests.suite',
- setup_requires=setup_requires,
- extras_require={
- 'test': [
- CYTHON_DEPENDENCY
- ]
- }
)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/httptools-0.6.1/tests/test_parser.py
new/httptools-0.7.1/tests/test_parser.py
--- old/httptools-0.6.1/tests/test_parser.py 2023-10-16 19:30:31.000000000
+0200
+++ new/httptools-0.7.1/tests/test_parser.py 2025-10-10 05:41:45.000000000
+0200
@@ -6,6 +6,18 @@
RESPONSE1_HEAD = b'''HTTP/1.1 200 OK
Date: Mon, 23 May 2005 22:38:34 GMT
+Server: Apache/1.3.3.7 (Unix) (Red-Hat/Linux)
+Last-Modified: Wed, 08 Jan 2003 23:11:55 GMT
+ETag: "3f80f-1b6-3e1cb03b"
+Content-Type: text/html; charset=UTF-8
+Content-Length: 130
+Accept-Ranges: bytes
+Connection: close
+
+'''.replace(b'\n', b'\r\n')
+
+RESPONSE1_SPACES_IN_HEAD = b'''HTTP/1.1 200 OK
+Date: Mon, 23 May 2005 22:38:34 GMT
Server: Apache/1.3.3.7
(Unix) (Red-Hat/Linux)
Last-Modified: Wed, 08 Jan 2003 23:11:55 GMT
@@ -33,7 +45,7 @@
User-Agent: Fooo\r
Host: bar\r
Transfer-Encoding: chunked\r
-
+\r
5\r\nhello\r\n6\r\n world\r\n'''
CHUNKED_REQUEST1_2 = b'''0\r\nVary: *\r\nUser-Agent: spam\r\n\r\n'''
@@ -42,7 +54,7 @@
User-Agent: Fooo\r
Host: bar\r
Transfer-Encoding: chunked\r
-
+\r
b\r\n+\xce\xcfM\xb5MI,I\x04\x00\r\n0\r\n\r\n'''
@@ -54,7 +66,7 @@
Upgrade: WebSocket\r
Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r
Origin: http://example.com\r
-
+\r
Hot diggity dogg'''
UPGRADE_RESPONSE1 = b'''HTTP/1.1 101 Switching Protocols
@@ -89,7 +101,7 @@
self.assertEqual(len(headers), 8)
self.assertEqual(headers.get(b'Connection'), b'close')
self.assertEqual(headers.get(b'Content-Type'),
- b'text/html; charset=UTF-8')
+ b'text/html; charset=UTF-8')
self.assertFalse(m.on_body.called)
p.feed_data(bytearray(RESPONSE1_BODY))
@@ -109,6 +121,53 @@
'Expected HTTP/'):
p.feed_data(b'12123123')
+ def test_parser_response_leninent_headers_1(self):
+ m = mock.Mock()
+
+ headers = {}
+ m.on_header.side_effect = headers.__setitem__
+
+ p = httptools.HttpResponseParser(m)
+
+ with self.assertRaisesRegex(
+ httptools.HttpParserError,
+ "whitespace after header value",
+ ):
+ p.feed_data(memoryview(RESPONSE1_SPACES_IN_HEAD))
+
+ def test_parser_response_leninent_headers_2(self):
+ m = mock.Mock()
+
+ headers = {}
+ m.on_header.side_effect = headers.__setitem__
+
+ p = httptools.HttpResponseParser(m)
+
+ p.set_dangerous_leniencies(lenient_headers=True)
+ p.feed_data(memoryview(RESPONSE1_SPACES_IN_HEAD))
+
+ self.assertEqual(p.get_http_version(), '1.1')
+ self.assertEqual(p.get_status_code(), 200)
+
+ m.on_status.assert_called_once_with(b'OK')
+
+ m.on_headers_complete.assert_called_once_with()
+ self.assertEqual(m.on_header.call_count, 8)
+ self.assertEqual(len(headers), 8)
+ self.assertEqual(headers.get(b'Connection'), b'close')
+ self.assertEqual(headers.get(b'Content-Type'),
+ b'text/html; charset=UTF-8')
+
+ self.assertFalse(m.on_body.called)
+ p.feed_data(bytearray(RESPONSE1_BODY))
+ m.on_body.assert_called_once_with(RESPONSE1_BODY)
+
+ m.on_message_complete.assert_called_once_with()
+
+ self.assertFalse(m.on_url.called)
+ self.assertFalse(m.on_chunk_header.called)
+ self.assertFalse(m.on_chunk_complete.called)
+
def test_parser_response_2(self):
with self.assertRaisesRegex(TypeError, 'a bytes-like object'):
httptools.HttpResponseParser(None).feed_data('')
++++++ llhttp-release-v6.0.6.tar.gz -> llhttp-release-v9.3.0.tar.gz ++++++
++++ 18929 lines of diff (skipped)