pybearings 0.2.0__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.
- pybearings-0.2.0/.gitignore +24 -0
- pybearings-0.2.0/LICENSE +21 -0
- pybearings-0.2.0/PKG-INFO +57 -0
- pybearings-0.2.0/README.md +44 -0
- pybearings-0.2.0/hatch_build.py +110 -0
- pybearings-0.2.0/pascal/capi/bearings.capi.pas +1060 -0
- pybearings-0.2.0/pascal/capi/bearings.h +89 -0
- pybearings-0.2.0/pascal/capi/libbearings.pas +41 -0
- pybearings-0.2.0/pascal/http/bearings.http.error.pas +432 -0
- pybearings-0.2.0/pascal/http/bearings.http.exchange.pas +3558 -0
- pybearings-0.2.0/pascal/http1/bearings.http.date.pas +304 -0
- pybearings-0.2.0/pascal/http1/bearings.http.headers.pas +1107 -0
- pybearings-0.2.0/pascal/http1/bearings.http.uri.pas +630 -0
- pybearings-0.2.0/pascal/http1/bearings.http1.config.pas +280 -0
- pybearings-0.2.0/pascal/http1/bearings.http1.conn.pas +1639 -0
- pybearings-0.2.0/pascal/http1/bearings.http1.decode.pas +955 -0
- pybearings-0.2.0/pascal/http1/bearings.http1.dispatch.pas +1446 -0
- pybearings-0.2.0/pascal/http1/bearings.http1.encode.pas +793 -0
- pybearings-0.2.0/pascal/http1/bearings.http1.io.pas +1188 -0
- pybearings-0.2.0/pascal/http1/bearings.http1.parser.pas +909 -0
- pybearings-0.2.0/pascal/http1/bearings.http1.reason.pas +236 -0
- pybearings-0.2.0/pascal/http1/bearings.http1.role.pas +1485 -0
- pybearings-0.2.0/pascal/rt/bearings.rt.buf.pas +699 -0
- pybearings-0.2.0/pascal/rt/bearings.rt.clock.pas +57 -0
- pybearings-0.2.0/pascal/rt/bearings.rt.cpu.pas +354 -0
- pybearings-0.2.0/pascal/rt/bearings.rt.loop.pas +786 -0
- pybearings-0.2.0/pascal/rt/bearings.rt.net.pas +901 -0
- pybearings-0.2.0/pascal/rt/bearings.rt.proxyproto.pas +247 -0
- pybearings-0.2.0/pascal/rt/bearings.rt.sd.pas +183 -0
- pybearings-0.2.0/pascal/rt/bearings.rt.stream.pas +430 -0
- pybearings-0.2.0/pascal/rt/bearings.rt.sync.pas +580 -0
- pybearings-0.2.0/pascal/rt/bearings.rt.sys.aarch64.inc +154 -0
- pybearings-0.2.0/pascal/rt/bearings.rt.sys.pas +219 -0
- pybearings-0.2.0/pascal/rt/bearings.rt.sys.x86_64.inc +182 -0
- pybearings-0.2.0/pascal/rt/bearings.rt.timer.pas +451 -0
- pybearings-0.2.0/pascal/rt/bearings.rt.workers.pas +468 -0
- pybearings-0.2.0/pascal/rt/bearings.version.pas +19 -0
- pybearings-0.2.0/pascal/server/bearings.server.accesslog.pas +721 -0
- pybearings-0.2.0/pascal/server/bearings.server.metrics.pas +271 -0
- pybearings-0.2.0/pascal/server/bearings.server.pas +1683 -0
- pybearings-0.2.0/pascal/ws/bearings.ws.conn.pas +1553 -0
- pybearings-0.2.0/pascal/ws/bearings.ws.frame.pas +618 -0
- pybearings-0.2.0/pascal/ws/bearings.ws.handshake.pas +443 -0
- pybearings-0.2.0/pascal/ws/bearings.ws.topics.pas +769 -0
- pybearings-0.2.0/pyproject.toml +168 -0
- pybearings-0.2.0/src/bearings/__init__.py +16 -0
- pybearings-0.2.0/src/bearings/_ffi.py +27 -0
- pybearings-0.2.0/src/bearings/py.typed +0 -0
- pybearings-0.2.0/src/bearings/server.py +348 -0
- pybearings-0.2.0/src/bearings/websocket.py +61 -0
- pybearings-0.2.0/src/bearings/wsgi.py +443 -0
- pybearings-0.2.0/tests/conftest.py +139 -0
- pybearings-0.2.0/tests/test_raw.py +224 -0
- pybearings-0.2.0/tests/test_server.py +278 -0
- pybearings-0.2.0/tests/test_websocket.py +355 -0
- pybearings-0.2.0/tests/test_wsgi.py +498 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# build output (compiles, benchmarks, test runs, fetched tools)
|
|
2
|
+
/build/
|
|
3
|
+
# fpmake output (tools/install-package.sh)
|
|
4
|
+
/units/
|
|
5
|
+
/fpmake
|
|
6
|
+
/*.fpm
|
|
7
|
+
/conformance/target/
|
|
8
|
+
target/
|
|
9
|
+
__pycache__/
|
|
10
|
+
*.o
|
|
11
|
+
*.ppu
|
|
12
|
+
*.a
|
|
13
|
+
*.compiled
|
|
14
|
+
# editors and temporary files
|
|
15
|
+
*~
|
|
16
|
+
*.swp
|
|
17
|
+
*.orig
|
|
18
|
+
*.rej
|
|
19
|
+
.DS_Store
|
|
20
|
+
# python/: compiled by python/hatch_build.py
|
|
21
|
+
/python/src/bearings/libbearings.so
|
|
22
|
+
/python/src/bearings/bearings.h
|
|
23
|
+
/python/.venv/
|
|
24
|
+
/python/dist/
|
pybearings-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Juan-Pablo Scaletti
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: pybearings
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Bearings, the Free Pascal HTTP/1.1 server, as a WSGI server for Python
|
|
5
|
+
Author-email: Juan Pablo Scaletti <juanpablo@jpscaletti.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
8
|
+
Classifier: Programming Language :: Python :: Free Threading :: 3 - Stable
|
|
9
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Server
|
|
10
|
+
Requires-Python: >=3.14
|
|
11
|
+
Requires-Dist: cffi>=2.0
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# pyBearings
|
|
15
|
+
|
|
16
|
+
[Bearings](https://github.com/fp-40/Bearings), the Free Pascal HTTP/1.1 server, as a WSGI server for Python. Linux only (x86_64 and ARM64); free-threaded Python works.
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
from bearings import serve
|
|
20
|
+
|
|
21
|
+
serve(app, port=8000) # until SIGINT or SIGTERM
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The event loops are Bearings' own threads and never run Python. Every request runs the WSGI app on a thread of the worker pool, so the app may block.
|
|
25
|
+
|
|
26
|
+
`Server(app, **options)` starts and stops a server from code (tests, a process that does other things): `start()`, `port`, `stop(grace)`, `wait()`, `metrics()`, or `with Server(app) as server:`. Its docstring lists the options: the address, a Unix socket or an inherited socket, the number of event loops and workers, a queue limit answered with 503, a connection limit, the access log and the metrics.
|
|
27
|
+
|
|
28
|
+
With `websocket=`, a `WebSocketHandler`, the server also accepts WebSockets on `ws_path`: its `opened`, `received` and `closed` run on the worker pool, one event at a time per connection, and `ws_send`, `ws_close`, `subscribe`, `unsubscribe` and `publish` reach the connections by id, from any thread. A publish goes to every subscriber of a topic, encoded once.
|
|
29
|
+
|
|
30
|
+
With `raw=True`, the server calls `app(head, input, listener)` instead of a WSGI app. `head` is the request head as Bearings sends it (the method, path, query, version, client address and port, then a name and a value per header, each followed by `"\0"`), `input` is the body and `listener` says where the server listens. The app returns `(status, headers, body)`. A framework that builds its own request object saves building an environ only to take it apart again. `RawHandler` describes the arguments.
|
|
31
|
+
|
|
32
|
+
The package loads `libbearings.so` (`src/capi/` in this repository) with cffi in ABI mode, so it needs no C compiler: building the wheel needs Free Pascal 3.2.2 (`fpc`), which `hatch_build.py` runs.
|
|
33
|
+
|
|
34
|
+
## Development
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
uv sync --python 3.14t # compiles libbearings.so into src/bearings/
|
|
38
|
+
uv run pytest
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Wheels
|
|
42
|
+
|
|
43
|
+
`uv build --wheel` builds the wheel for this machine's CPU (`manylinux_2_34_x86_64` here).
|
|
44
|
+
The manylinux tag is the newest glibc symbol version `libbearings.so` needs, read with `objdump -T` (binutils).
|
|
45
|
+
For ARM64, give it the cross compiler of `tools/get-cross-aarch64.sh`; the wheel is tagged for the CPU that compiler targets:
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
PYBEARINGS_FPC=../build/tools/bin/fpc-aarch64 uv build --wheel # manylinux_2_34_aarch64
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`uv build` also builds the sdist, which holds the Pascal sources of `../src/` in `pascal/`, and builds the wheel from it.
|
|
52
|
+
Installing from the sdist needs Free Pascal 3.2.2 and binutils.
|
|
53
|
+
|
|
54
|
+
`./test-aarch64.sh` does that, installs the wheel in a free-threaded CPython
|
|
55
|
+
for ARM64 run under qemu-user, and runs the tests against it. qemu-user runs
|
|
56
|
+
on the host's memory model, which is stronger than ARM's: it shows that the
|
|
57
|
+
wheel builds, loads and works, not races that only ARM hardware has.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# pyBearings
|
|
2
|
+
|
|
3
|
+
[Bearings](https://github.com/fp-40/Bearings), the Free Pascal HTTP/1.1 server, as a WSGI server for Python. Linux only (x86_64 and ARM64); free-threaded Python works.
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
from bearings import serve
|
|
7
|
+
|
|
8
|
+
serve(app, port=8000) # until SIGINT or SIGTERM
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The event loops are Bearings' own threads and never run Python. Every request runs the WSGI app on a thread of the worker pool, so the app may block.
|
|
12
|
+
|
|
13
|
+
`Server(app, **options)` starts and stops a server from code (tests, a process that does other things): `start()`, `port`, `stop(grace)`, `wait()`, `metrics()`, or `with Server(app) as server:`. Its docstring lists the options: the address, a Unix socket or an inherited socket, the number of event loops and workers, a queue limit answered with 503, a connection limit, the access log and the metrics.
|
|
14
|
+
|
|
15
|
+
With `websocket=`, a `WebSocketHandler`, the server also accepts WebSockets on `ws_path`: its `opened`, `received` and `closed` run on the worker pool, one event at a time per connection, and `ws_send`, `ws_close`, `subscribe`, `unsubscribe` and `publish` reach the connections by id, from any thread. A publish goes to every subscriber of a topic, encoded once.
|
|
16
|
+
|
|
17
|
+
With `raw=True`, the server calls `app(head, input, listener)` instead of a WSGI app. `head` is the request head as Bearings sends it (the method, path, query, version, client address and port, then a name and a value per header, each followed by `"\0"`), `input` is the body and `listener` says where the server listens. The app returns `(status, headers, body)`. A framework that builds its own request object saves building an environ only to take it apart again. `RawHandler` describes the arguments.
|
|
18
|
+
|
|
19
|
+
The package loads `libbearings.so` (`src/capi/` in this repository) with cffi in ABI mode, so it needs no C compiler: building the wheel needs Free Pascal 3.2.2 (`fpc`), which `hatch_build.py` runs.
|
|
20
|
+
|
|
21
|
+
## Development
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
uv sync --python 3.14t # compiles libbearings.so into src/bearings/
|
|
25
|
+
uv run pytest
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Wheels
|
|
29
|
+
|
|
30
|
+
`uv build --wheel` builds the wheel for this machine's CPU (`manylinux_2_34_x86_64` here).
|
|
31
|
+
The manylinux tag is the newest glibc symbol version `libbearings.so` needs, read with `objdump -T` (binutils).
|
|
32
|
+
For ARM64, give it the cross compiler of `tools/get-cross-aarch64.sh`; the wheel is tagged for the CPU that compiler targets:
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
PYBEARINGS_FPC=../build/tools/bin/fpc-aarch64 uv build --wheel # manylinux_2_34_aarch64
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`uv build` also builds the sdist, which holds the Pascal sources of `../src/` in `pascal/`, and builds the wheel from it.
|
|
39
|
+
Installing from the sdist needs Free Pascal 3.2.2 and binutils.
|
|
40
|
+
|
|
41
|
+
`./test-aarch64.sh` does that, installs the wheel in a free-threaded CPython
|
|
42
|
+
for ARM64 run under qemu-user, and runs the tests against it. qemu-user runs
|
|
43
|
+
on the host's memory model, which is stronger than ARM's: it shows that the
|
|
44
|
+
wheel builds, loads and works, not races that only ARM hardware has.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""Compiles libbearings.so with Free Pascal.
|
|
2
|
+
|
|
3
|
+
An editable install gets it next to the package's modules (for this
|
|
4
|
+
machine). A wheel gets it from a directory of its own per CPU, so a wheel
|
|
5
|
+
for another CPU leaves the editable install alone: `PYBEARINGS_FPC` names
|
|
6
|
+
the compiler, for example the ARM64 cross compiler of
|
|
7
|
+
tools/get-cross-aarch64.sh, and the wheel is tagged for the CPU that
|
|
8
|
+
compiler targets (`fpc -iTP`), and for the newest glibc symbol version
|
|
9
|
+
libbearings.so needs (`objdump -T`).
|
|
10
|
+
|
|
11
|
+
PYBEARINGS_FPC=../build/tools/bin/fpc-aarch64 uv build --wheel
|
|
12
|
+
|
|
13
|
+
The Pascal sources are in src/ of the repository, or in pascal/ of an
|
|
14
|
+
unpacked sdist (force-include in pyproject.toml).
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import os
|
|
18
|
+
import re
|
|
19
|
+
import shutil
|
|
20
|
+
import subprocess
|
|
21
|
+
import sys
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
UNITS = ("rt", "http", "http1", "server", "ws", "capi")
|
|
28
|
+
|
|
29
|
+
# fpc -iTP -> the wheel's platform tag
|
|
30
|
+
CPUS = {
|
|
31
|
+
"x86_64": "x86_64",
|
|
32
|
+
"aarch64": "aarch64",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def manylinux(library: Path) -> str:
|
|
37
|
+
"""The manylinux tag for `library`: the newest glibc symbol version it
|
|
38
|
+
needs, which is the oldest glibc that loads it."""
|
|
39
|
+
objdump = shutil.which("objdump")
|
|
40
|
+
if not objdump:
|
|
41
|
+
raise RuntimeError("tagging the pybearings wheel needs objdump (binutils)")
|
|
42
|
+
|
|
43
|
+
symbols = subprocess.run(
|
|
44
|
+
[objdump, "-T", str(library)], check=True, capture_output=True, text=True
|
|
45
|
+
).stdout
|
|
46
|
+
versions = {
|
|
47
|
+
tuple(int(n) for n in v.split("."))
|
|
48
|
+
for v in re.findall(r"\bGLIBC_(\d+(?:\.\d+)+)\b", symbols)
|
|
49
|
+
}
|
|
50
|
+
if not versions:
|
|
51
|
+
raise RuntimeError(f"no glibc symbol in {library}: is it linked against glibc?")
|
|
52
|
+
|
|
53
|
+
major, minor = max(versions)[:2]
|
|
54
|
+
return f"manylinux_{major}_{minor}"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class CustomBuildHook(BuildHookInterface):
|
|
58
|
+
def initialize(self, version: str, build_data: dict) -> None:
|
|
59
|
+
if not sys.platform.startswith("linux"):
|
|
60
|
+
raise RuntimeError("Bearings runs only on Linux")
|
|
61
|
+
|
|
62
|
+
fpc = os.environ.get("PYBEARINGS_FPC") or shutil.which("fpc")
|
|
63
|
+
if not fpc:
|
|
64
|
+
raise RuntimeError("building pybearings needs Free Pascal >= 3.2.2 (fpc)")
|
|
65
|
+
|
|
66
|
+
fpc = str(Path(fpc).resolve()) if os.sep in fpc else fpc
|
|
67
|
+
cpu = subprocess.run([fpc, "-iTP"], check=True, capture_output=True, text=True).stdout.strip()
|
|
68
|
+
|
|
69
|
+
if cpu not in CPUS:
|
|
70
|
+
raise RuntimeError(f"pybearings builds for {', '.join(CPUS)}, not {cpu}")
|
|
71
|
+
|
|
72
|
+
if (Path(self.root) / "pascal").is_dir(): # an unpacked sdist
|
|
73
|
+
root = Path(self.root)
|
|
74
|
+
src = root / "pascal"
|
|
75
|
+
else: # python/ of the repository
|
|
76
|
+
root = Path(self.root).parent
|
|
77
|
+
src = root / "src"
|
|
78
|
+
if version == "editable":
|
|
79
|
+
out = Path(self.root) / "src" / "bearings" # imported from here
|
|
80
|
+
else:
|
|
81
|
+
out = root / "build" / f"python-{cpu}"
|
|
82
|
+
|
|
83
|
+
units = root / "build" / f"python-units-{cpu}"
|
|
84
|
+
for directory in (out, units):
|
|
85
|
+
directory.mkdir(parents=True, exist_ok=True)
|
|
86
|
+
|
|
87
|
+
command = [
|
|
88
|
+
fpc,
|
|
89
|
+
"-Mobjfpc",
|
|
90
|
+
"-Sh",
|
|
91
|
+
"-O2",
|
|
92
|
+
"-fPIC",
|
|
93
|
+
"-vew",
|
|
94
|
+
*(f"-Fu{src / unit}" for unit in UNITS),
|
|
95
|
+
f"-Fi{src / 'rt'}",
|
|
96
|
+
f"-FU{units}",
|
|
97
|
+
f"-o{out / 'libbearings.so'}",
|
|
98
|
+
str(src / "capi" / "libbearings.pas"),
|
|
99
|
+
]
|
|
100
|
+
subprocess.run(command, check=True)
|
|
101
|
+
shutil.copyfile(src / "capi" / "bearings.h", out / "bearings.h")
|
|
102
|
+
|
|
103
|
+
if version != "editable":
|
|
104
|
+
build_data["force_include"][str(out / "libbearings.so")] = (
|
|
105
|
+
"bearings/libbearings.so"
|
|
106
|
+
)
|
|
107
|
+
build_data["force_include"][str(out / "bearings.h")] = "bearings/bearings.h"
|
|
108
|
+
|
|
109
|
+
build_data["pure_python"] = False
|
|
110
|
+
build_data["tag"] = f"py3-none-{manylinux(out / 'libbearings.so')}_{CPUS[cpu]}"
|