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.
Files changed (56) hide show
  1. pybearings-0.2.0/.gitignore +24 -0
  2. pybearings-0.2.0/LICENSE +21 -0
  3. pybearings-0.2.0/PKG-INFO +57 -0
  4. pybearings-0.2.0/README.md +44 -0
  5. pybearings-0.2.0/hatch_build.py +110 -0
  6. pybearings-0.2.0/pascal/capi/bearings.capi.pas +1060 -0
  7. pybearings-0.2.0/pascal/capi/bearings.h +89 -0
  8. pybearings-0.2.0/pascal/capi/libbearings.pas +41 -0
  9. pybearings-0.2.0/pascal/http/bearings.http.error.pas +432 -0
  10. pybearings-0.2.0/pascal/http/bearings.http.exchange.pas +3558 -0
  11. pybearings-0.2.0/pascal/http1/bearings.http.date.pas +304 -0
  12. pybearings-0.2.0/pascal/http1/bearings.http.headers.pas +1107 -0
  13. pybearings-0.2.0/pascal/http1/bearings.http.uri.pas +630 -0
  14. pybearings-0.2.0/pascal/http1/bearings.http1.config.pas +280 -0
  15. pybearings-0.2.0/pascal/http1/bearings.http1.conn.pas +1639 -0
  16. pybearings-0.2.0/pascal/http1/bearings.http1.decode.pas +955 -0
  17. pybearings-0.2.0/pascal/http1/bearings.http1.dispatch.pas +1446 -0
  18. pybearings-0.2.0/pascal/http1/bearings.http1.encode.pas +793 -0
  19. pybearings-0.2.0/pascal/http1/bearings.http1.io.pas +1188 -0
  20. pybearings-0.2.0/pascal/http1/bearings.http1.parser.pas +909 -0
  21. pybearings-0.2.0/pascal/http1/bearings.http1.reason.pas +236 -0
  22. pybearings-0.2.0/pascal/http1/bearings.http1.role.pas +1485 -0
  23. pybearings-0.2.0/pascal/rt/bearings.rt.buf.pas +699 -0
  24. pybearings-0.2.0/pascal/rt/bearings.rt.clock.pas +57 -0
  25. pybearings-0.2.0/pascal/rt/bearings.rt.cpu.pas +354 -0
  26. pybearings-0.2.0/pascal/rt/bearings.rt.loop.pas +786 -0
  27. pybearings-0.2.0/pascal/rt/bearings.rt.net.pas +901 -0
  28. pybearings-0.2.0/pascal/rt/bearings.rt.proxyproto.pas +247 -0
  29. pybearings-0.2.0/pascal/rt/bearings.rt.sd.pas +183 -0
  30. pybearings-0.2.0/pascal/rt/bearings.rt.stream.pas +430 -0
  31. pybearings-0.2.0/pascal/rt/bearings.rt.sync.pas +580 -0
  32. pybearings-0.2.0/pascal/rt/bearings.rt.sys.aarch64.inc +154 -0
  33. pybearings-0.2.0/pascal/rt/bearings.rt.sys.pas +219 -0
  34. pybearings-0.2.0/pascal/rt/bearings.rt.sys.x86_64.inc +182 -0
  35. pybearings-0.2.0/pascal/rt/bearings.rt.timer.pas +451 -0
  36. pybearings-0.2.0/pascal/rt/bearings.rt.workers.pas +468 -0
  37. pybearings-0.2.0/pascal/rt/bearings.version.pas +19 -0
  38. pybearings-0.2.0/pascal/server/bearings.server.accesslog.pas +721 -0
  39. pybearings-0.2.0/pascal/server/bearings.server.metrics.pas +271 -0
  40. pybearings-0.2.0/pascal/server/bearings.server.pas +1683 -0
  41. pybearings-0.2.0/pascal/ws/bearings.ws.conn.pas +1553 -0
  42. pybearings-0.2.0/pascal/ws/bearings.ws.frame.pas +618 -0
  43. pybearings-0.2.0/pascal/ws/bearings.ws.handshake.pas +443 -0
  44. pybearings-0.2.0/pascal/ws/bearings.ws.topics.pas +769 -0
  45. pybearings-0.2.0/pyproject.toml +168 -0
  46. pybearings-0.2.0/src/bearings/__init__.py +16 -0
  47. pybearings-0.2.0/src/bearings/_ffi.py +27 -0
  48. pybearings-0.2.0/src/bearings/py.typed +0 -0
  49. pybearings-0.2.0/src/bearings/server.py +348 -0
  50. pybearings-0.2.0/src/bearings/websocket.py +61 -0
  51. pybearings-0.2.0/src/bearings/wsgi.py +443 -0
  52. pybearings-0.2.0/tests/conftest.py +139 -0
  53. pybearings-0.2.0/tests/test_raw.py +224 -0
  54. pybearings-0.2.0/tests/test_server.py +278 -0
  55. pybearings-0.2.0/tests/test_websocket.py +355 -0
  56. 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/
@@ -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]}"