walbox 1.0.0b0__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.
walbox-1.0.0b0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mochama Adriano
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,206 @@
1
+ Metadata-Version: 2.4
2
+ Name: walbox
3
+ Version: 1.0.0b0
4
+ Summary: Async PostgreSQL logical-replication runtime for the transactional outbox pattern
5
+ Keywords: postgresql,logical-replication,outbox,asyncio,cdc,change-data-capture
6
+ Author: Mochama Adriano
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Framework :: AsyncIO
14
+ Classifier: Topic :: Database
15
+ Classifier: Typing :: Typed
16
+ Requires-Dist: psycopg>=3.2
17
+ Requires-Python: >=3.13
18
+ Project-URL: Homepage, https://github.com/mochams/walbox
19
+ Project-URL: Repository, https://github.com/mochams/walbox
20
+ Project-URL: Issues, https://github.com/mochams/walbox/issues
21
+ Project-URL: Author, https://github.com/mochams
22
+ Description-Content-Type: text/markdown
23
+
24
+ # walbox
25
+
26
+ [![PyPI](https://img.shields.io/pypi/v/walbox.svg)](https://pypi.org/project/walbox/)
27
+ [![CI](https://github.com/mochams/walbox/actions/workflows/ci.yml/badge.svg)](https://github.com/mochams/walbox/actions/workflows/ci.yml)
28
+ [![Python 3.13+](https://img.shields.io/badge/python-3.13%2B-blue.svg)](https://www.python.org/)
29
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
30
+
31
+ Async Python runtime for consuming PostgreSQL logical replication as a stream of
32
+ committed transactions, built for the transactional outbox pattern: write an outbox
33
+ row in the same transaction as your business data, then stream committed inserts to
34
+ an external system with no polling and no `LISTEN`/`NOTIFY`.
35
+
36
+ - **At-least-once delivery**: a durable local checkpoint, never silent loss
37
+ - **Backpressure-aware**: a slow handler can't blow up memory or starve PostgreSQL's keepalives
38
+ - **Reconnects and resumes** automatically from the last durable checkpoint
39
+ - **Graceful shutdown**: finishes in-flight work and checkpoints it before exiting
40
+ - **Asyncio-native**, one dependency (`psycopg`)
41
+
42
+ ## Install
43
+
44
+ ```sh
45
+ pip install walbox
46
+ ```
47
+
48
+ Requires `libpq` available at build/run time (typically a system package, e.g.
49
+ `libpq-dev` on Debian/Ubuntu). To try walbox without a system `libpq`, install
50
+ psycopg's self-contained wheel alongside it: `pip install walbox "psycopg[binary]"`.
51
+
52
+ ## Quickstart
53
+
54
+ ```sql
55
+ -- Run once, manually. walbox creates its replication slot idempotently, but
56
+ -- never creates or alters the publication itself (see "PostgreSQL configuration"
57
+ -- below).
58
+ CREATE TABLE outbox (
59
+ id BIGSERIAL PRIMARY KEY,
60
+ entity_type TEXT NOT NULL,
61
+ entity_id TEXT NOT NULL,
62
+ event_type TEXT NOT NULL,
63
+ payload JSONB NOT NULL,
64
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
65
+ );
66
+
67
+ CREATE PUBLICATION walbox_pub FOR TABLE outbox;
68
+ ```
69
+
70
+ ```py
71
+ import asyncio
72
+ import signal
73
+
74
+ from walbox import (
75
+ ChangeKind,
76
+ PostgresCheckpointStore,
77
+ ReplicationClient,
78
+ ReplicationOptions,
79
+ Transaction,
80
+ )
81
+
82
+
83
+ async def publish_to_broker(payload: dict) -> None:
84
+ # Replace with your actual publish call.
85
+ print("publishing:", payload)
86
+
87
+
88
+ async def handle(tx: Transaction) -> None:
89
+ for change in tx.changes:
90
+ if change.table != "public.outbox" or change.kind != ChangeKind.INSERT:
91
+ continue
92
+ await publish_to_broker(change.new)
93
+
94
+ await tx.checkpoint.save(tx.commit_lsn)
95
+
96
+
97
+ async def main() -> None:
98
+ dsn = "your-postgres-dsn"
99
+ checkpoint_store = PostgresCheckpointStore(dsn, consumer_name="my-consumer")
100
+
101
+ options = ReplicationOptions(
102
+ consumer_name="my-consumer",
103
+ dsn=dsn,
104
+ slot_name="outbox_slot",
105
+ publication_name="walbox_pub",
106
+ checkpoint_store=checkpoint_store,
107
+ manage_checkpoint=False, # handle() checkpoints explicitly, after publishing.
108
+ )
109
+
110
+ client = ReplicationClient(options)
111
+
112
+ loop = asyncio.get_running_loop()
113
+ for sig in (signal.SIGTERM, signal.SIGINT):
114
+ loop.add_signal_handler(sig, client.close)
115
+
116
+ await client.run(handle)
117
+
118
+
119
+ if __name__ == "__main__":
120
+ asyncio.run(main())
121
+ ```
122
+
123
+ A complete, runnable version lives in [`examples/outbox.py`](examples/outbox.py),
124
+ including the same-transaction checkpoint pattern for a Postgres sink (see
125
+ "Exactly-once effects" below).
126
+
127
+ ## PostgreSQL configuration
128
+
129
+ - `wal_level = logical` in `postgresql.conf` (requires a server restart).
130
+ - Size `max_replication_slots`/`max_wal_senders` with headroom for at least one
131
+ slot/sender per consumer; `max_wal_senders` should be at least as large as
132
+ `max_replication_slots`.
133
+ - The connecting role needs `REPLICATION`: `ALTER ROLE consumer_role REPLICATION;`
134
+ (or `CREATE ROLE ... WITH REPLICATION LOGIN;`).
135
+ - A `pg_hba.conf` entry granting that role access to the `replication`
136
+ pseudo-database, e.g. `host replication consumer_role 10.0.0.0/8 scram-sha-256`.
137
+ - On PostgreSQL 15+, the connecting role additionally needs `SELECT` on the
138
+ published tables (15 tightened this; 14 doesn't enforce it).
139
+ - The published table needs a usable `REPLICA IDENTITY` for `UPDATE`/`DELETE` to see
140
+ old-row data. A primary key (`DEFAULT`) is enough for most outbox-style tables; a
141
+ table with none needs `ALTER TABLE ... REPLICA IDENTITY FULL` (or `USING INDEX`).
142
+ - walbox creates its replication slot idempotently if missing. It does **not** create
143
+ the publication: `CREATE PUBLICATION` is a manual, one-time step (see Limitations).
144
+
145
+ ## Exactly-once effects
146
+
147
+ walbox provides **at-least-once delivery** with a durable replay position. It does
148
+ **not** implement or claim end-to-end exactly-once effects. The flow: Postgres
149
+ transaction → outbox row → logical replication → handler → external sink.
150
+ Exactly-once *effects* come from combining the transactional outbox write with
151
+ durable checkpointing and an idempotent/deduplicating sink: either dedupe on
152
+ `outbox.id`, or, when the sink is itself PostgreSQL, use
153
+ `PostgresCheckpointStore`'s same-transaction pattern (`handle_with_atomic_checkpoint`
154
+ in [`examples/outbox.py`](examples/outbox.py)). If the process crashes after an
155
+ external publish succeeds but before the checkpoint is durable, the transaction
156
+ **will** be delivered again. That's intentional, not a bug.
157
+
158
+ ## Failure semantics
159
+
160
+ walbox is correct if the process crashes at *any* point, whether before, during, or
161
+ after the handler runs, mid-checkpoint, mid-reconnect, mid-shutdown, or partway
162
+ through a large streamed transaction. The result is always "delivered again" or "not
163
+ delivered yet," never silent loss and never a torn transaction. The full
164
+ crash-point-by-crash-point table is in
165
+ [`ARCHITECTURE.md`](ARCHITECTURE.md#failure-semantics).
166
+
167
+ ## Supported versions
168
+
169
+ - **PostgreSQL 14+**: the floor for protocol version 2 / `streaming 'on'`, which
170
+ walbox always negotiates. A pre-14 server is unsupported, not silently degraded.
171
+ - **Python 3.13+**: `asyncio.Queue.shutdown()`, which backpressure and graceful
172
+ shutdown depend on, is a 3.13 addition.
173
+
174
+ Both are deliberate v0.1 floors, not aspirations to relax later.
175
+
176
+ ## Limitations
177
+
178
+ - Streamed-transaction memory isn't accounted against `max_pending_transactions`, so
179
+ large or numerous concurrent streamed transactions can grow memory independent of
180
+ that bound.
181
+ - No built-in metrics exporter, only a synchronous `on_metrics` callback; wiring it
182
+ to Prometheus/StatsD/etc. is left to the application.
183
+ - Strictly sequential, single-consumer handling, with no concurrent handler execution.
184
+ - Manual publication management: walbox never creates or alters the publication.
185
+ - Truncate's `CASCADE`/`RESTART IDENTITY` flags, and Type/Origin message content, are
186
+ decoded but never surfaced to the application.
187
+
188
+ ## Status
189
+
190
+ The code is tested and correct for everything described above: 100% branch coverage,
191
+ including integration tests against real PostgreSQL for every failure scenario in the
192
+ table above. "Pre-1.0" here is about the API surface still settling, not about whether
193
+ it's safe to run.
194
+
195
+ Concretely: the public export list won't shrink, though construction signatures and
196
+ field names may still shift between pre-1.0 releases. The `Metrics` callback shape and
197
+ streamed-vs-non-streamed `Transaction` semantics are most likely to still change before
198
+ 1.0. Once 1.0 ships, the stable surface follows semver.
199
+
200
+ ## See also
201
+
202
+ - [`ARCHITECTURE.md`](ARCHITECTURE.md): system design, the correctness invariant, the error hierarchy
203
+ - [`docs/README.md`](docs/README.md): the RFCs behind each feature
204
+ - [`CONTRIBUTING.md`](CONTRIBUTING.md): development setup, tests, code style
205
+ - [`PROJECT.md`](PROJECT.md): project status and tooling rationale
206
+ - [`LICENSE`](LICENSE): MIT
@@ -0,0 +1,183 @@
1
+ # walbox
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/walbox.svg)](https://pypi.org/project/walbox/)
4
+ [![CI](https://github.com/mochams/walbox/actions/workflows/ci.yml/badge.svg)](https://github.com/mochams/walbox/actions/workflows/ci.yml)
5
+ [![Python 3.13+](https://img.shields.io/badge/python-3.13%2B-blue.svg)](https://www.python.org/)
6
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
7
+
8
+ Async Python runtime for consuming PostgreSQL logical replication as a stream of
9
+ committed transactions, built for the transactional outbox pattern: write an outbox
10
+ row in the same transaction as your business data, then stream committed inserts to
11
+ an external system with no polling and no `LISTEN`/`NOTIFY`.
12
+
13
+ - **At-least-once delivery**: a durable local checkpoint, never silent loss
14
+ - **Backpressure-aware**: a slow handler can't blow up memory or starve PostgreSQL's keepalives
15
+ - **Reconnects and resumes** automatically from the last durable checkpoint
16
+ - **Graceful shutdown**: finishes in-flight work and checkpoints it before exiting
17
+ - **Asyncio-native**, one dependency (`psycopg`)
18
+
19
+ ## Install
20
+
21
+ ```sh
22
+ pip install walbox
23
+ ```
24
+
25
+ Requires `libpq` available at build/run time (typically a system package, e.g.
26
+ `libpq-dev` on Debian/Ubuntu). To try walbox without a system `libpq`, install
27
+ psycopg's self-contained wheel alongside it: `pip install walbox "psycopg[binary]"`.
28
+
29
+ ## Quickstart
30
+
31
+ ```sql
32
+ -- Run once, manually. walbox creates its replication slot idempotently, but
33
+ -- never creates or alters the publication itself (see "PostgreSQL configuration"
34
+ -- below).
35
+ CREATE TABLE outbox (
36
+ id BIGSERIAL PRIMARY KEY,
37
+ entity_type TEXT NOT NULL,
38
+ entity_id TEXT NOT NULL,
39
+ event_type TEXT NOT NULL,
40
+ payload JSONB NOT NULL,
41
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
42
+ );
43
+
44
+ CREATE PUBLICATION walbox_pub FOR TABLE outbox;
45
+ ```
46
+
47
+ ```py
48
+ import asyncio
49
+ import signal
50
+
51
+ from walbox import (
52
+ ChangeKind,
53
+ PostgresCheckpointStore,
54
+ ReplicationClient,
55
+ ReplicationOptions,
56
+ Transaction,
57
+ )
58
+
59
+
60
+ async def publish_to_broker(payload: dict) -> None:
61
+ # Replace with your actual publish call.
62
+ print("publishing:", payload)
63
+
64
+
65
+ async def handle(tx: Transaction) -> None:
66
+ for change in tx.changes:
67
+ if change.table != "public.outbox" or change.kind != ChangeKind.INSERT:
68
+ continue
69
+ await publish_to_broker(change.new)
70
+
71
+ await tx.checkpoint.save(tx.commit_lsn)
72
+
73
+
74
+ async def main() -> None:
75
+ dsn = "your-postgres-dsn"
76
+ checkpoint_store = PostgresCheckpointStore(dsn, consumer_name="my-consumer")
77
+
78
+ options = ReplicationOptions(
79
+ consumer_name="my-consumer",
80
+ dsn=dsn,
81
+ slot_name="outbox_slot",
82
+ publication_name="walbox_pub",
83
+ checkpoint_store=checkpoint_store,
84
+ manage_checkpoint=False, # handle() checkpoints explicitly, after publishing.
85
+ )
86
+
87
+ client = ReplicationClient(options)
88
+
89
+ loop = asyncio.get_running_loop()
90
+ for sig in (signal.SIGTERM, signal.SIGINT):
91
+ loop.add_signal_handler(sig, client.close)
92
+
93
+ await client.run(handle)
94
+
95
+
96
+ if __name__ == "__main__":
97
+ asyncio.run(main())
98
+ ```
99
+
100
+ A complete, runnable version lives in [`examples/outbox.py`](examples/outbox.py),
101
+ including the same-transaction checkpoint pattern for a Postgres sink (see
102
+ "Exactly-once effects" below).
103
+
104
+ ## PostgreSQL configuration
105
+
106
+ - `wal_level = logical` in `postgresql.conf` (requires a server restart).
107
+ - Size `max_replication_slots`/`max_wal_senders` with headroom for at least one
108
+ slot/sender per consumer; `max_wal_senders` should be at least as large as
109
+ `max_replication_slots`.
110
+ - The connecting role needs `REPLICATION`: `ALTER ROLE consumer_role REPLICATION;`
111
+ (or `CREATE ROLE ... WITH REPLICATION LOGIN;`).
112
+ - A `pg_hba.conf` entry granting that role access to the `replication`
113
+ pseudo-database, e.g. `host replication consumer_role 10.0.0.0/8 scram-sha-256`.
114
+ - On PostgreSQL 15+, the connecting role additionally needs `SELECT` on the
115
+ published tables (15 tightened this; 14 doesn't enforce it).
116
+ - The published table needs a usable `REPLICA IDENTITY` for `UPDATE`/`DELETE` to see
117
+ old-row data. A primary key (`DEFAULT`) is enough for most outbox-style tables; a
118
+ table with none needs `ALTER TABLE ... REPLICA IDENTITY FULL` (or `USING INDEX`).
119
+ - walbox creates its replication slot idempotently if missing. It does **not** create
120
+ the publication: `CREATE PUBLICATION` is a manual, one-time step (see Limitations).
121
+
122
+ ## Exactly-once effects
123
+
124
+ walbox provides **at-least-once delivery** with a durable replay position. It does
125
+ **not** implement or claim end-to-end exactly-once effects. The flow: Postgres
126
+ transaction → outbox row → logical replication → handler → external sink.
127
+ Exactly-once *effects* come from combining the transactional outbox write with
128
+ durable checkpointing and an idempotent/deduplicating sink: either dedupe on
129
+ `outbox.id`, or, when the sink is itself PostgreSQL, use
130
+ `PostgresCheckpointStore`'s same-transaction pattern (`handle_with_atomic_checkpoint`
131
+ in [`examples/outbox.py`](examples/outbox.py)). If the process crashes after an
132
+ external publish succeeds but before the checkpoint is durable, the transaction
133
+ **will** be delivered again. That's intentional, not a bug.
134
+
135
+ ## Failure semantics
136
+
137
+ walbox is correct if the process crashes at *any* point, whether before, during, or
138
+ after the handler runs, mid-checkpoint, mid-reconnect, mid-shutdown, or partway
139
+ through a large streamed transaction. The result is always "delivered again" or "not
140
+ delivered yet," never silent loss and never a torn transaction. The full
141
+ crash-point-by-crash-point table is in
142
+ [`ARCHITECTURE.md`](ARCHITECTURE.md#failure-semantics).
143
+
144
+ ## Supported versions
145
+
146
+ - **PostgreSQL 14+**: the floor for protocol version 2 / `streaming 'on'`, which
147
+ walbox always negotiates. A pre-14 server is unsupported, not silently degraded.
148
+ - **Python 3.13+**: `asyncio.Queue.shutdown()`, which backpressure and graceful
149
+ shutdown depend on, is a 3.13 addition.
150
+
151
+ Both are deliberate v0.1 floors, not aspirations to relax later.
152
+
153
+ ## Limitations
154
+
155
+ - Streamed-transaction memory isn't accounted against `max_pending_transactions`, so
156
+ large or numerous concurrent streamed transactions can grow memory independent of
157
+ that bound.
158
+ - No built-in metrics exporter, only a synchronous `on_metrics` callback; wiring it
159
+ to Prometheus/StatsD/etc. is left to the application.
160
+ - Strictly sequential, single-consumer handling, with no concurrent handler execution.
161
+ - Manual publication management: walbox never creates or alters the publication.
162
+ - Truncate's `CASCADE`/`RESTART IDENTITY` flags, and Type/Origin message content, are
163
+ decoded but never surfaced to the application.
164
+
165
+ ## Status
166
+
167
+ The code is tested and correct for everything described above: 100% branch coverage,
168
+ including integration tests against real PostgreSQL for every failure scenario in the
169
+ table above. "Pre-1.0" here is about the API surface still settling, not about whether
170
+ it's safe to run.
171
+
172
+ Concretely: the public export list won't shrink, though construction signatures and
173
+ field names may still shift between pre-1.0 releases. The `Metrics` callback shape and
174
+ streamed-vs-non-streamed `Transaction` semantics are most likely to still change before
175
+ 1.0. Once 1.0 ships, the stable surface follows semver.
176
+
177
+ ## See also
178
+
179
+ - [`ARCHITECTURE.md`](ARCHITECTURE.md): system design, the correctness invariant, the error hierarchy
180
+ - [`docs/README.md`](docs/README.md): the RFCs behind each feature
181
+ - [`CONTRIBUTING.md`](CONTRIBUTING.md): development setup, tests, code style
182
+ - [`PROJECT.md`](PROJECT.md): project status and tooling rationale
183
+ - [`LICENSE`](LICENSE): MIT
@@ -0,0 +1,162 @@
1
+ [build-system]
2
+ requires = ["uv_build>=0.11.0,<0.13.0"]
3
+ build-backend = "uv_build"
4
+
5
+ [project]
6
+ name = "walbox"
7
+ version = "1.0.0b0"
8
+ description = "Async PostgreSQL logical-replication runtime for the transactional outbox pattern"
9
+ readme = "README.md"
10
+ requires-python = ">=3.13"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ keywords = [
14
+ "postgresql",
15
+ "logical-replication",
16
+ "outbox",
17
+ "asyncio",
18
+ "cdc",
19
+ "change-data-capture",
20
+ ]
21
+ classifiers = [
22
+ "Development Status :: 3 - Alpha",
23
+ "Intended Audience :: Developers",
24
+ "Programming Language :: Python :: 3",
25
+ "Programming Language :: Python :: 3.13",
26
+ "Framework :: AsyncIO",
27
+ "Topic :: Database",
28
+ "Typing :: Typed",
29
+ ]
30
+ dependencies = ["psycopg>=3.2"]
31
+
32
+ [[project.authors]]
33
+ name = "Mochama Adriano"
34
+
35
+ [project.urls]
36
+ Homepage = "https://github.com/mochams/walbox"
37
+ Repository = "https://github.com/mochams/walbox"
38
+ Issues = "https://github.com/mochams/walbox/issues"
39
+ Author = "https://github.com/mochams"
40
+
41
+ [dependency-groups]
42
+ dev = [
43
+ "cryptography>=50.0.0",
44
+ "prek>=0.4.13",
45
+ "psycopg[binary]>=3.2",
46
+ "pyrefly>=1.2.0",
47
+ "pytest>=9.1.1",
48
+ "pytest-asyncio>=1.4.0",
49
+ "pytest-cov>=7.1.0",
50
+ "pytest-timeout>=2.4.0",
51
+ "pytest-xdist>=3.8.0",
52
+ "ruff>=0.16.1",
53
+ "testcontainers[postgres]>=4.9",
54
+ ]
55
+
56
+ [tool.uv.build-backend]
57
+ module-name = "walbox"
58
+ module-root = "."
59
+
60
+ [tool.ruff]
61
+ extend-exclude = ["docs"]
62
+ line-length = 88
63
+ indent-width = 4
64
+ target-version = "py313"
65
+
66
+ [tool.ruff.lint]
67
+ preview = true
68
+ select = ["ALL"]
69
+ ignore = [
70
+ "assert",
71
+ "mutable-class-default",
72
+ "collapsible-if",
73
+ "missing-copyright-notice",
74
+ "missing-return-type-undocumented-public-function",
75
+ "missing-return-type-private-function",
76
+ "no-self-use",
77
+ "unused-method-argument",
78
+ "undefined-local-with-import-star-usage",
79
+ "class-as-data-structure",
80
+ ]
81
+ exclude = ["tests/*.py"]
82
+ fixable = ["ALL"]
83
+ unfixable = []
84
+ dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
85
+
86
+ [tool.ruff.lint.isort]
87
+ force-single-line = true
88
+ case-sensitive = true
89
+
90
+ [tool.ruff.lint.pydocstyle]
91
+ convention = "google"
92
+
93
+ [tool.ruff.lint.pylint]
94
+ max-args = 7
95
+
96
+ [tool.ruff.format]
97
+ preview = true
98
+ exclude = [
99
+ "COM812",
100
+ "ISC001",
101
+ ]
102
+ quote-style = "double"
103
+ indent-style = "space"
104
+ skip-magic-trailing-comma = false
105
+ line-ending = "auto"
106
+ docstring-code-format = true
107
+ docstring-code-line-length = 88
108
+
109
+ [tool.pyrefly]
110
+ min-severity = "warn"
111
+ project-excludes = [
112
+ "tests",
113
+ "docs",
114
+ "examples",
115
+ ]
116
+
117
+ [tool.pytest.ini_options]
118
+ asyncio_mode = "auto"
119
+ asyncio_default_fixture_loop_scope = "function"
120
+ addopts = [
121
+ "--strict-markers",
122
+ "--cov=walbox",
123
+ "--cov=examples",
124
+ "--cov-fail-under=100",
125
+ "--cov-report=term-missing",
126
+ "--durations=10",
127
+ "--timeout=10",
128
+ "-n 0",
129
+ ]
130
+ testpaths = ["tests"]
131
+ markers = ["postgres: requires a real PostgreSQL instance, started via testcontainers (make test-integration)"]
132
+ log_cli = "false"
133
+ log_cli_level = "ERROR"
134
+ log_cli_format = "%(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s)"
135
+ log_cli_date_format = "%Y-%m-%d %H:%M:%S"
136
+
137
+ [tool.coverage.run]
138
+ branch = true
139
+ disable_warnings = [
140
+ "include-ignored",
141
+ "no-data-collected",
142
+ ]
143
+ parallel = true
144
+ concurrency = [
145
+ "multiprocessing",
146
+ "thread",
147
+ ]
148
+
149
+ [tool.coverage.report]
150
+ exclude_also = [
151
+ "def __repr__",
152
+ "def __str__",
153
+ 'if self\.debug',
154
+ "raise AssertionError",
155
+ "raise NotImplementedError",
156
+ "if 0:",
157
+ "if __name__ == .__main__.:",
158
+ '@(abc\.)?abstractmethod',
159
+ '^\s*\.\.\.$',
160
+ "if TYPE_CHECKING:",
161
+ ]
162
+ skip_covered = "true"