ironflow-py 0.33.1__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.
- ironflow_py-0.33.1/.gitignore +30 -0
- ironflow_py-0.33.1/CONTRIBUTING.md +27 -0
- ironflow_py-0.33.1/LICENSE +183 -0
- ironflow_py-0.33.1/PKG-INFO +253 -0
- ironflow_py-0.33.1/README.md +219 -0
- ironflow_py-0.33.1/SECURITY.md +33 -0
- ironflow_py-0.33.1/ironflow/__init__.py +83 -0
- ironflow_py-0.33.1/ironflow/_gen/__init__.py +17 -0
- ironflow_py-0.33.1/ironflow/_gen/agent_tools_connect.py +347 -0
- ironflow_py-0.33.1/ironflow/_gen/agent_tools_pb.py +523 -0
- ironflow_py-0.33.1/ironflow/_gen/audit_connect.py +221 -0
- ironflow_py-0.33.1/ironflow/_gen/audit_pb.py +311 -0
- ironflow_py-0.33.1/ironflow/_gen/entity_stream_connect.py +558 -0
- ironflow_py-0.33.1/ironflow/_gen/entity_stream_pb.py +1016 -0
- ironflow_py-0.33.1/ironflow/_gen/environment_connect.py +408 -0
- ironflow_py-0.33.1/ironflow/_gen/environment_pb.py +348 -0
- ironflow_py-0.33.1/ironflow/_gen/event_schema_connect.py +420 -0
- ironflow_py-0.33.1/ironflow/_gen/event_schema_pb.py +506 -0
- ironflow_py-0.33.1/ironflow/_gen/ironflow_connect.py +1612 -0
- ironflow_py-0.33.1/ironflow/_gen/ironflow_pb.py +1776 -0
- ironflow_py-0.33.1/ironflow/_gen/projection_connect.py +1468 -0
- ironflow_py-0.33.1/ironflow/_gen/projection_pb.py +2143 -0
- ironflow_py-0.33.1/ironflow/_gen/pubsub_connect.py +880 -0
- ironflow_py-0.33.1/ironflow/_gen/pubsub_pb.py +1466 -0
- ironflow_py-0.33.1/ironflow/_gen/query_connect.py +152 -0
- ironflow_py-0.33.1/ironflow/_gen/query_pb.py +144 -0
- ironflow_py-0.33.1/ironflow/_gen/timetravel_connect.py +290 -0
- ironflow_py-0.33.1/ironflow/_gen/timetravel_pb.py +486 -0
- ironflow_py-0.33.1/ironflow/_gen/types_pb.py +1224 -0
- ironflow_py-0.33.1/ironflow/_gen/webhook_connect.py +811 -0
- ironflow_py-0.33.1/ironflow/_gen/webhook_pb.py +1123 -0
- ironflow_py-0.33.1/ironflow/_gen/worker_connect.py +148 -0
- ironflow_py-0.33.1/ironflow/_gen/worker_pb.py +1391 -0
- ironflow_py-0.33.1/ironflow/_http.py +526 -0
- ironflow_py-0.33.1/ironflow/client.py +910 -0
- ironflow_py-0.33.1/ironflow/models.py +1757 -0
- ironflow_py-0.33.1/ironflow/py.typed +0 -0
- ironflow_py-0.33.1/ironflow/rpc/__init__.py +25 -0
- ironflow_py-0.33.1/ironflow/rpc/_client.py +1638 -0
- ironflow_py-0.33.1/ironflow/rpc/_runtime.py +737 -0
- ironflow_py-0.33.1/ironflow/rpc/v1.py +415 -0
- ironflow_py-0.33.1/pyproject.toml +161 -0
- ironflow_py-0.33.1/rpc-capabilities.yaml +412 -0
- ironflow_py-0.33.1/tests/__init__.py +0 -0
- ironflow_py-0.33.1/tests/api_snapshot.json +164 -0
- ironflow_py-0.33.1/tests/conftest.py +18 -0
- ironflow_py-0.33.1/tests/harness.py +104 -0
- ironflow_py-0.33.1/tests/integration/__init__.py +1 -0
- ironflow_py-0.33.1/tests/integration/conftest.py +117 -0
- ironflow_py-0.33.1/tests/integration/test_live.py +358 -0
- ironflow_py-0.33.1/tests/rpc_api_snapshot.json +184 -0
- ironflow_py-0.33.1/tests/rpc_server.py +288 -0
- ironflow_py-0.33.1/tests/test_api_snapshot.py +70 -0
- ironflow_py-0.33.1/tests/test_client.py +1021 -0
- ironflow_py-0.33.1/tests/test_errors.py +105 -0
- ironflow_py-0.33.1/tests/test_hostile_server.py +285 -0
- ironflow_py-0.33.1/tests/test_request.py +165 -0
- ironflow_py-0.33.1/tests/test_retry.py +224 -0
- ironflow_py-0.33.1/tests/test_rpc_api_snapshot.py +85 -0
- ironflow_py-0.33.1/tests/test_rpc_client.py +407 -0
- ironflow_py-0.33.1/tests/test_rpc_retry.py +327 -0
- ironflow_py-0.33.1/tests/test_rpc_stream_resume.py +343 -0
- ironflow_py-0.33.1/tests/test_rpc_streams.py +373 -0
- ironflow_py-0.33.1/tests/test_rpc_surface.py +165 -0
- ironflow_py-0.33.1/tests/test_rpc_wire_names.py +195 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Build artifacts
|
|
2
|
+
build/
|
|
3
|
+
dist/
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
|
|
7
|
+
# Bytecode
|
|
8
|
+
__pycache__/
|
|
9
|
+
*.py[cod]
|
|
10
|
+
|
|
11
|
+
# Virtualenvs
|
|
12
|
+
.venv/
|
|
13
|
+
.venv-*/
|
|
14
|
+
|
|
15
|
+
# Tool caches
|
|
16
|
+
.mypy_cache/
|
|
17
|
+
.pytest_cache/
|
|
18
|
+
.ruff_cache/
|
|
19
|
+
.tox/
|
|
20
|
+
htmlcov/
|
|
21
|
+
.coverage
|
|
22
|
+
.coverage.*
|
|
23
|
+
|
|
24
|
+
# OS noise
|
|
25
|
+
.DS_Store
|
|
26
|
+
|
|
27
|
+
# Editor
|
|
28
|
+
*.swp
|
|
29
|
+
.idea/
|
|
30
|
+
.vscode/
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
**This repository is a read-only mirror.** External contributions are not accepted here.
|
|
4
|
+
|
|
5
|
+
The Ironflow Python SDK source is maintained in a private engine repository. This mirror is updated automatically by the release pipeline at each version tag.
|
|
6
|
+
|
|
7
|
+
## Reporting bugs
|
|
8
|
+
|
|
9
|
+
Issues are disabled here. Everything goes to one tracker: [sahina/ironflow-issues](https://github.com/sahina/ironflow-issues/issues/new/choose).
|
|
10
|
+
|
|
11
|
+
- SDK bugs (in `ironflow-py`) → file there and pick **Python SDK** as the component. Include your Python version, platform, `pip --version`, repro steps, and a minimal example.
|
|
12
|
+
- Engine, CLI, dashboard, and desktop bugs → same tracker, pick the matching component.
|
|
13
|
+
- Security issues → [private advisory](https://github.com/sahina/ironflow-issues/security/advisories/new) or see [SECURITY.md](SECURITY.md). Do **not** open a public issue.
|
|
14
|
+
|
|
15
|
+
## Pull requests
|
|
16
|
+
|
|
17
|
+
Pull requests opened against this repository will be closed without review. The only commits expected here come from the release pipeline mirroring source from the engine repo.
|
|
18
|
+
|
|
19
|
+
If you have a fix in mind, file an issue describing the bug and the proposed fix. The engine team will land the change in the private repo and it will appear here at the next release.
|
|
20
|
+
|
|
21
|
+
## Generated code
|
|
22
|
+
|
|
23
|
+
`ironflow/_gen/` is generated from the engine's protobuf definitions and vendored here as source. Do not hand-edit it, in this repo or anywhere else — the next release overwrites it. The generator lives in the engine repo (`make proto-python`).
|
|
24
|
+
|
|
25
|
+
## License inquiries
|
|
26
|
+
|
|
27
|
+
For commercial licensing, see the contact in [LICENSE](LICENSE).
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Ironflow License
|
|
2
|
+
|
|
3
|
+
**Version 1.0 — 2026-05-08**
|
|
4
|
+
|
|
5
|
+
This file contains the binding legal terms under which Ironflow is offered.
|
|
6
|
+
A plain-English summary lives at `docs/explanation/licensing.md`. If the two
|
|
7
|
+
disagree, this file controls.
|
|
8
|
+
|
|
9
|
+
> **Lawyer review status:** v1 DIY-substitute. The licensor entity is
|
|
10
|
+
> resolved. Three sections below are original prose rather than FSL template
|
|
11
|
+
> text and have not been counsel-reviewed. Review is tracked in issue #777 and
|
|
12
|
+
> is due before the first paid customer ships:
|
|
13
|
+
>
|
|
14
|
+
> - "Personal-Use and Evaluation Carve-Out" — supplemental to the FSL
|
|
15
|
+
> Permitted Purpose, added to match the worked examples in
|
|
16
|
+
> `docs/explanation/licensing.md` per the plan body. JetBrains Toolbox and
|
|
17
|
+
> Postman free-tier EULAs were the reference for the carve-out shape, but
|
|
18
|
+
> the wording is ours.
|
|
19
|
+
> - "Governing Law and Venue"
|
|
20
|
+
> - "Commercial Licensing"
|
|
21
|
+
>
|
|
22
|
+
> This is a disclosure, not a publication gate. These terms ship on every
|
|
23
|
+
> public release channel today — npm, the Go module proxy, PyPI, the releases
|
|
24
|
+
> mirror and ironflow.run — and an earlier wording of this block claimed
|
|
25
|
+
> otherwise while four releases went out past it.
|
|
26
|
+
>
|
|
27
|
+
> **Versioning:** revised terms apply on next install. Each release ships
|
|
28
|
+
> the LICENSE that was effective at the time the artifact was built.
|
|
29
|
+
> Continuing to use a previously installed version under its prior terms
|
|
30
|
+
> is permitted; upgrading to a newer release accepts the LICENSE shipped
|
|
31
|
+
> with that release.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Functional Source License, Version 1.1, ALv2 Future License
|
|
36
|
+
|
|
37
|
+
## Abbreviation
|
|
38
|
+
|
|
39
|
+
FSL-1.1-ALv2
|
|
40
|
+
|
|
41
|
+
## Notice
|
|
42
|
+
|
|
43
|
+
Copyright 2026 AEC Office LLC
|
|
44
|
+
|
|
45
|
+
## Terms and Conditions
|
|
46
|
+
|
|
47
|
+
### Licensor ("We")
|
|
48
|
+
|
|
49
|
+
The party offering the Software under these Terms and Conditions.
|
|
50
|
+
|
|
51
|
+
### The Software
|
|
52
|
+
|
|
53
|
+
The "Software" is each version of the software that we make available under
|
|
54
|
+
these Terms and Conditions, as indicated by our inclusion of these Terms and
|
|
55
|
+
Conditions with the Software.
|
|
56
|
+
|
|
57
|
+
### License Grant
|
|
58
|
+
|
|
59
|
+
Subject to your compliance with this License Grant and the Patents,
|
|
60
|
+
Redistribution and Trademark clauses below, we hereby grant you the right to
|
|
61
|
+
use, copy, modify, create derivative works, publicly perform, publicly display
|
|
62
|
+
and redistribute the Software for any Permitted Purpose identified below.
|
|
63
|
+
|
|
64
|
+
### Permitted Purpose
|
|
65
|
+
|
|
66
|
+
A Permitted Purpose is any purpose other than a Competing Use. A Competing Use
|
|
67
|
+
means making the Software available to others in a commercial product or
|
|
68
|
+
service that:
|
|
69
|
+
|
|
70
|
+
1. substitutes for the Software;
|
|
71
|
+
|
|
72
|
+
2. substitutes for any other product or service we offer using the Software
|
|
73
|
+
that exists as of the date we make the Software available; or
|
|
74
|
+
|
|
75
|
+
3. offers the same or substantially similar functionality as the Software.
|
|
76
|
+
|
|
77
|
+
Permitted Purposes specifically include using the Software:
|
|
78
|
+
|
|
79
|
+
1. for your internal use and access;
|
|
80
|
+
|
|
81
|
+
2. for non-commercial education;
|
|
82
|
+
|
|
83
|
+
3. for non-commercial research; and
|
|
84
|
+
|
|
85
|
+
4. in connection with professional services that you provide to a licensee
|
|
86
|
+
using the Software in accordance with these Terms and Conditions.
|
|
87
|
+
|
|
88
|
+
### Patents
|
|
89
|
+
|
|
90
|
+
To the extent your use for a Permitted Purpose would necessarily infringe our
|
|
91
|
+
patents, the license grant above includes a license under our patents. If you
|
|
92
|
+
make a claim against any party that the Software infringes or contributes to
|
|
93
|
+
the infringement of any patent, then your patent license to the Software ends
|
|
94
|
+
immediately.
|
|
95
|
+
|
|
96
|
+
### Redistribution
|
|
97
|
+
|
|
98
|
+
The Terms and Conditions apply to all copies, modifications and derivatives of
|
|
99
|
+
the Software.
|
|
100
|
+
|
|
101
|
+
If you redistribute any copies, modifications or derivatives of the Software,
|
|
102
|
+
you must include a copy of or a link to these Terms and Conditions and not
|
|
103
|
+
remove any copyright notices provided in or with the Software.
|
|
104
|
+
|
|
105
|
+
### Disclaimer
|
|
106
|
+
|
|
107
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR
|
|
108
|
+
IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR
|
|
109
|
+
PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT.
|
|
110
|
+
|
|
111
|
+
IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE
|
|
112
|
+
SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES,
|
|
113
|
+
EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE.
|
|
114
|
+
|
|
115
|
+
### Trademarks
|
|
116
|
+
|
|
117
|
+
Except for displaying the License Details and identifying us as the origin of
|
|
118
|
+
the Software, you have no right under these Terms and Conditions to use our
|
|
119
|
+
trademarks, trade names, service marks or product names.
|
|
120
|
+
|
|
121
|
+
## Grant of Future License
|
|
122
|
+
|
|
123
|
+
We hereby irrevocably grant you an additional license to use the Software under
|
|
124
|
+
the Apache License, Version 2.0 that is effective on the second anniversary of
|
|
125
|
+
the date we make the Software available. On or after that date, you may use the
|
|
126
|
+
Software under the Apache License, Version 2.0, in which case the following
|
|
127
|
+
will apply:
|
|
128
|
+
|
|
129
|
+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
|
130
|
+
this file except in compliance with the License.
|
|
131
|
+
|
|
132
|
+
You may obtain a copy of the License at
|
|
133
|
+
|
|
134
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
135
|
+
|
|
136
|
+
Unless required by applicable law or agreed to in writing, software distributed
|
|
137
|
+
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
138
|
+
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
139
|
+
specific language governing permissions and limitations under the License.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Personal-Use and Evaluation Carve-Out
|
|
144
|
+
|
|
145
|
+
> _Supplemental to the FSL Permitted Purpose above. Pending counsel review._
|
|
146
|
+
|
|
147
|
+
The following uses are explicitly permitted in addition to the FSL Permitted
|
|
148
|
+
Purposes listed above:
|
|
149
|
+
|
|
150
|
+
1. **Personal use.** Use of the Software by a natural person for that
|
|
151
|
+
person's personal, non-commercial purposes — including hobby projects,
|
|
152
|
+
self-education, and side projects that do not generate revenue or
|
|
153
|
+
commercial advantage.
|
|
154
|
+
|
|
155
|
+
2. **Evaluation.** Use of the Software by any individual or organization for
|
|
156
|
+
the purpose of evaluating the Software prior to a commercial purchase
|
|
157
|
+
decision, for a period of up to ninety (90) days from first install.
|
|
158
|
+
Evaluation use does not require notification.
|
|
159
|
+
|
|
160
|
+
These carve-outs are non-transferable and do not extend to redistribution,
|
|
161
|
+
sublicensing, or any Competing Use as defined in the FSL Permitted Purpose
|
|
162
|
+
section.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Governing Law and Venue
|
|
167
|
+
|
|
168
|
+
These Terms and Conditions are governed by the laws of the State of Delaware,
|
|
169
|
+
United States of America, without regard to its conflict-of-laws provisions.
|
|
170
|
+
Any dispute arising out of or relating to these Terms and Conditions shall be
|
|
171
|
+
brought exclusively in the state or federal courts located in Delaware, and
|
|
172
|
+
you consent to the personal jurisdiction of those courts.
|
|
173
|
+
|
|
174
|
+
## Commercial Licensing
|
|
175
|
+
|
|
176
|
+
For uses that fall outside the Permitted Purpose — including any Competing
|
|
177
|
+
Use, or any commercial use case for which a separate paid license is required
|
|
178
|
+
under our Cloud or Enterprise terms — contact license@ironflow.run to discuss
|
|
179
|
+
a commercial license.
|
|
180
|
+
|
|
181
|
+
## SPDX Identifier
|
|
182
|
+
|
|
183
|
+
`LicenseRef-Ironflow-EULA`
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: ironflow-py
|
|
3
|
+
Version: 0.33.1
|
|
4
|
+
Summary: Ironflow Python SDK — event-driven backend platform client
|
|
5
|
+
Project-URL: Homepage, https://ironflow.run
|
|
6
|
+
Project-URL: Documentation, https://docs.ironflow.run
|
|
7
|
+
Project-URL: Repository, https://github.com/sahina/ironflow-py
|
|
8
|
+
Project-URL: Issues, https://github.com/sahina/ironflow-py/issues
|
|
9
|
+
License-Expression: LicenseRef-Ironflow-EULA
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Requires-Dist: connectrpc<0.12,>=0.11.1
|
|
21
|
+
Requires-Dist: pyqwest<0.11,>=0.10
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: build>=1.0; extra == 'dev'
|
|
24
|
+
Requires-Dist: mypy>=1.0; extra == 'dev'
|
|
25
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
26
|
+
Requires-Dist: ruff>=0.1; extra == 'dev'
|
|
27
|
+
Requires-Dist: uvicorn>=0.30; extra == 'dev'
|
|
28
|
+
Provides-Extra: gen
|
|
29
|
+
Requires-Dist: connectrpc==0.11.1; extra == 'gen'
|
|
30
|
+
Requires-Dist: protobuf-py==0.1.1; extra == 'gen'
|
|
31
|
+
Requires-Dist: protoc-gen-connectrpc==0.11.1; extra == 'gen'
|
|
32
|
+
Requires-Dist: protoc-gen-py==0.1.1; extra == 'gen'
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# Ironflow Python SDK
|
|
36
|
+
|
|
37
|
+
Python client for [Ironflow](https://ironflow.run) — the Continuous History platform for backend systems.
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install ironflow-py
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
import ironflow
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
> **Install `ironflow-py`, not `ironflow`.** The distribution name is `ironflow-py`; the
|
|
50
|
+
> import name is `ironflow`. The bare name `ironflow` on PyPI belongs to an unrelated
|
|
51
|
+
> third-party project (a materials-science tool from the pyiron group), which also
|
|
52
|
+
> installs a top-level `ironflow` module — a virtualenv holding both has two claimants on
|
|
53
|
+
> that name and the install order silently decides which wins. Keep them in separate
|
|
54
|
+
> environments.
|
|
55
|
+
|
|
56
|
+
Requires Python 3.10+.
|
|
57
|
+
|
|
58
|
+
Available from v0.33.0. Versions track the Ironflow engine release, so `ironflow-py`
|
|
59
|
+
0.33.0 is the client for engine 0.33.0.
|
|
60
|
+
|
|
61
|
+
## Status
|
|
62
|
+
|
|
63
|
+
Experimental and client-only. It can emit events and query runs, projections,
|
|
64
|
+
KV, and config, but it ships **no worker runtime** — there is no `step.run`, no
|
|
65
|
+
`step.sleep`, and no push or pull mode.
|
|
66
|
+
|
|
67
|
+
**Two clients, two protocols, neither a superset of the other.** `IronflowClient`
|
|
68
|
+
speaks REST and retries idempotent methods. `IronflowRPC` / `AsyncIronflowRPC`
|
|
69
|
+
speak ConnectRPC and reach 46 capabilities REST does not serve — webhook
|
|
70
|
+
management, agent tools, time travel, pub/sub consumer groups, function
|
|
71
|
+
versioning, and environment lookup/key rotation — including four server
|
|
72
|
+
streams. `IronflowRPC` retries only the unary methods the protos annotate
|
|
73
|
+
side-effect-free, and reconnects a subscription only when you position it (see
|
|
74
|
+
[Retries](#retries-and-what-can-still-go-wrong)).
|
|
75
|
+
|
|
76
|
+
Routes the server's manifest annotates with schemas get a typed `models.*` `TypedDict`
|
|
77
|
+
return and keyword-only query parameters; routes it does not annotate still return `Any`
|
|
78
|
+
and take no query kwargs. `TypedDict` is a type-checker construct only — there is **no
|
|
79
|
+
runtime validation**. Headers declared by a route, including `If-Match`,
|
|
80
|
+
`If-None-Match`, and `Idempotency-Key`, are generated as typed keyword arguments.
|
|
81
|
+
`BaseClient.request()` remains the escape hatch for undeclared headers. WebSocket and
|
|
82
|
+
watch endpoints (`/ws`, config watch, KV watch) have no generated methods at all and
|
|
83
|
+
`request()` cannot reach them either; poll the corresponding read method, use a
|
|
84
|
+
ConnectRPC subscription, or use the Go or JavaScript SDK for a live watch.
|
|
85
|
+
|
|
86
|
+
For durable step execution, use the Go or JavaScript SDK.
|
|
87
|
+
|
|
88
|
+
## Quick Start
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from ironflow import IronflowClient
|
|
92
|
+
|
|
93
|
+
client = IronflowClient(
|
|
94
|
+
server_url="http://localhost:9123",
|
|
95
|
+
api_key="ifkey_...",
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# Emit an event
|
|
99
|
+
client.events_create(body={
|
|
100
|
+
"name": "user.created",
|
|
101
|
+
"data": {"user_id": "123", "email": "user@example.com"},
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
# List runs
|
|
105
|
+
runs = client.runs_list()
|
|
106
|
+
|
|
107
|
+
# Get a specific run
|
|
108
|
+
run = client.runs_get("run_abc123")
|
|
109
|
+
|
|
110
|
+
# List projections
|
|
111
|
+
projections = client.projections_list()
|
|
112
|
+
|
|
113
|
+
# Public server inspection
|
|
114
|
+
health = client.health()
|
|
115
|
+
readiness = client.ready()
|
|
116
|
+
capabilities = client.capabilities()
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## ConnectRPC client
|
|
120
|
+
|
|
121
|
+
For the capabilities REST does not serve. Request and response types come from
|
|
122
|
+
`ironflow.rpc.v1`; `ironflow._gen` is private and its layout may change.
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
from ironflow import IronflowRPC
|
|
126
|
+
from ironflow.rpc.v1 import CreateWebhookSourceRequest, SubscribeRequest
|
|
127
|
+
|
|
128
|
+
with IronflowRPC(server_url="http://localhost:9123", api_key="ifkey_...") as rpc:
|
|
129
|
+
source = rpc.webhooks.create_source(
|
|
130
|
+
CreateWebhookSourceRequest(name="Stripe", event_prefix="stripe.")
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# Server streams are ordinary iterators; breaking out cancels.
|
|
134
|
+
for event in rpc.pubsub.subscribe(SubscribeRequest(pattern="topic:orders.*")):
|
|
135
|
+
print(event.event_id)
|
|
136
|
+
break
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
`AsyncIronflowRPC` mirrors it method for method — `await` the unary calls,
|
|
140
|
+
`async for` the streams, and `await rpc.aclose()` instead of `close()`.
|
|
141
|
+
|
|
142
|
+
Failures raise `IronflowRPCError`, which subclasses `IronflowError`, so
|
|
143
|
+
`except IronflowError` still catches every failure from either client. Full
|
|
144
|
+
reference, including timeouts, `NO_TIMEOUT`, and stream lifetime:
|
|
145
|
+
[docs.ironflow.run/reference/api/python-sdk](https://docs.ironflow.run/reference/api/python-sdk).
|
|
146
|
+
|
|
147
|
+
### Retries, and what can still go wrong
|
|
148
|
+
|
|
149
|
+
A unary call that fails with `unavailable` — a refused connection, a socket
|
|
150
|
+
dropped mid-response, a server shedding load — is sent again, up to 3 attempts
|
|
151
|
+
with exponential backoff. Nothing else is retried: every other Connect code is a
|
|
152
|
+
decision the server will reach again identically.
|
|
153
|
+
|
|
154
|
+
**Only side-effect-free methods are retried.** The protobuf definitions annotate
|
|
155
|
+
them `idempotency_level = NO_SIDE_EFFECTS`, and the client reads that annotation
|
|
156
|
+
rather than any list of its own. A method without it — every create, update,
|
|
157
|
+
delete, rotate and emit — is sent exactly once and its failure is raised to you
|
|
158
|
+
immediately, because a transport error cannot tell you whether the server
|
|
159
|
+
committed the write before the connection dropped.
|
|
160
|
+
|
|
161
|
+
Two things this does **not** promise:
|
|
162
|
+
|
|
163
|
+
- **A retried read may execute more than once on the server.** A response lost
|
|
164
|
+
on the way back is indistinguishable from a request that never arrived, so the
|
|
165
|
+
retry re-runs the method. That is harmless for a read by definition, and it is
|
|
166
|
+
the reason the annotation gates the behaviour.
|
|
167
|
+
- **A write is never retried for you.** If you need one repeated safely, repeat
|
|
168
|
+
it yourself with an idempotency key — `EmitRequest` and `PublishRequest` both
|
|
169
|
+
carry `idempotency_key` — and treat a failed write as *unknown*, not *failed*.
|
|
170
|
+
|
|
171
|
+
`timeout=` still bounds the whole call including every retry and backoff, not
|
|
172
|
+
each attempt. Pass `max_attempts=1` to the constructor to switch retries off.
|
|
173
|
+
|
|
174
|
+
**`subscribe` reconnects only if you positioned it.** Set
|
|
175
|
+
`options.start_after_sequence` to the last `event.sequence` you processed, and a
|
|
176
|
+
dropped connection is retried from there. That field is both the cursor and the
|
|
177
|
+
opt-in: without it there is no honest place to resume from, so the stream raises
|
|
178
|
+
and re-subscribing is yours.
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
from ironflow.rpc.v1 import SubscribeRequest, SubscribeOptions
|
|
182
|
+
|
|
183
|
+
cursor = 0
|
|
184
|
+
for event in rpc.pubsub.subscribe(SubscribeRequest(
|
|
185
|
+
pattern="topic:orders.*",
|
|
186
|
+
options=SubscribeOptions(start_after_sequence=cursor),
|
|
187
|
+
)):
|
|
188
|
+
handle(event)
|
|
189
|
+
cursor = event.sequence # persist this if you need to resume across restarts
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
A resumed stream is **at-least-once**: the frame in flight when the connection
|
|
193
|
+
dropped may arrive twice, because the server sending it is not you having
|
|
194
|
+
processed it. Make `handle` idempotent, or dedupe on `event.sequence`.
|
|
195
|
+
|
|
196
|
+
**The other three streams are not reconnected, and do not need to be.**
|
|
197
|
+
`stream_events`, `wait_catchup_stream` and `join_consumer_group` are positioned
|
|
198
|
+
by the server on a durable consumer, so simply calling them again resumes where
|
|
199
|
+
they left off. A client-side cursor there would move a position other readers
|
|
200
|
+
share.
|
|
201
|
+
|
|
202
|
+
## API Coverage
|
|
203
|
+
|
|
204
|
+
This SDK is auto-generated from the Ironflow server's route manifest. Run `make sdk-health`
|
|
205
|
+
for the current method count — it changes on every regeneration, so it is not reproduced
|
|
206
|
+
here. Route coverage is not the same as usable coverage; see the Status section above.
|
|
207
|
+
|
|
208
|
+
See the [SDK Comparison Matrix](https://docs.ironflow.run/reference/sdk-comparison) for
|
|
209
|
+
full coverage details.
|
|
210
|
+
|
|
211
|
+
## Dependencies
|
|
212
|
+
|
|
213
|
+
Two direct runtime dependencies, `connectrpc` and `pyqwest`, which resolve to six
|
|
214
|
+
packages — two of them compiled:
|
|
215
|
+
|
|
216
|
+
```text
|
|
217
|
+
connectrpc ─┬─ protobuf-py ── protobuf-py-ext (native, CPython only)
|
|
218
|
+
├─ pyqwest (Rust)
|
|
219
|
+
│ └─ opentelemetry-api
|
|
220
|
+
└─ typing_extensions
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
They are required, not an extra. The ConnectRPC client is part of the default
|
|
224
|
+
contract, so putting it behind a flag would turn a heavier install into a runtime
|
|
225
|
+
`ImportError` — see ADR 0062 (engine repo, internal).
|
|
226
|
+
|
|
227
|
+
`IronflowClient` itself still uses only the standard library (`urllib`, `json`),
|
|
228
|
+
and `import ironflow` does not load the ConnectRPC stack — that cost is paid on
|
|
229
|
+
first use of `IronflowRPC`.
|
|
230
|
+
|
|
231
|
+
## Where this package is published from
|
|
232
|
+
|
|
233
|
+
The Ironflow engine is closed source. This SDK's source is mirrored to
|
|
234
|
+
[`sahina/ironflow-py`](https://github.com/sahina/ironflow-py) at each release, and PyPI
|
|
235
|
+
uploads happen from there over [Trusted Publishing](https://docs.pypi.org/trusted-publishers/).
|
|
236
|
+
No PyPI API token exists for this project, so every release carries an attestation
|
|
237
|
+
binding the artifact to a public commit you can inspect.
|
|
238
|
+
|
|
239
|
+
The mirror is **read-only**. Pull requests against it are closed without review; source
|
|
240
|
+
changes land in the engine repo and appear at the next release.
|
|
241
|
+
|
|
242
|
+
## Bugs and security
|
|
243
|
+
|
|
244
|
+
Issues are disabled on the mirror. Everything goes to one tracker:
|
|
245
|
+
|
|
246
|
+
- Bugs and feature requests → [sahina/ironflow-issues](https://github.com/sahina/ironflow-issues/issues/new/choose), component **Python SDK**. Include your Python version, platform, and a minimal repro.
|
|
247
|
+
- Security issues → [private advisory](https://github.com/sahina/ironflow-issues/security/advisories/new). Do **not** open a public issue.
|
|
248
|
+
- Commercial licensing → the contact in [LICENSE](LICENSE).
|
|
249
|
+
|
|
250
|
+
## License
|
|
251
|
+
|
|
252
|
+
See [LICENSE](LICENSE) — SPDX `LicenseRef-Ironflow-EULA`. Not an OSI-approved open source
|
|
253
|
+
licence; read it before deploying commercially.
|