typefold-client 0.2.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.
@@ -0,0 +1,7 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.egg-info/
4
+ dist/
5
+ .pytest_cache/
6
+ .mypy_cache/
7
+ .ruff_cache/
@@ -0,0 +1,188 @@
1
+ Metadata-Version: 2.5
2
+ Name: typefold-client
3
+ Version: 0.2.1
4
+ Summary: Python client for the Typefold API: render published reports and templates to PDF or XLSX.
5
+ Project-URL: Repository, https://github.com/berkerdemirer/typefold
6
+ Keywords: pdf,rendering,reporting,xlsx
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Typing :: Typed
9
+ Requires-Python: >=3.10
10
+ Requires-Dist: httpx<1,>=0.27
11
+ Provides-Extra: test
12
+ Requires-Dist: mypy>=1.10; extra == 'test'
13
+ Requires-Dist: pytest>=8; extra == 'test'
14
+ Requires-Dist: ruff>=0.6; extra == 'test'
15
+ Description-Content-Type: text/markdown
16
+
17
+ # typefold-client
18
+
19
+ Python client for the Typefold API. Needs Python 3.10 or later; its only dependency is
20
+ `httpx`. It has a blocking `TypefoldClient` and an asyncio `AsyncTypefoldClient` with the
21
+ same methods.
22
+
23
+ ```sh
24
+ pip install typefold-client
25
+ ```
26
+
27
+ Create one client per API key and reuse it. The key needs `render:create`, `render:read` and
28
+ `template:read`, and `render:cancel` to cancel renders.
29
+
30
+ ```python
31
+ from typefold import TypefoldClient
32
+
33
+ client = TypefoldClient("https://pdf.example.com", api_key)
34
+ ```
35
+
36
+ Close it with `client.close()` when your application stops, or use it as a context manager.
37
+
38
+ ## Render a report
39
+
40
+ `render_report` renders a published report or saved template by name or ID and returns the PDF
41
+ or XLSX bytes:
42
+
43
+ ```python
44
+ from typefold import (
45
+ Orientation,
46
+ PageSize,
47
+ PdfMetadata,
48
+ RenderReportRequest,
49
+ ReportFormat,
50
+ ReportPdfOptions,
51
+ TemplateRef,
52
+ )
53
+
54
+ pdf = client.render_report(
55
+ RenderReportRequest(
56
+ template=TemplateRef.by_name("SoldGoodsForWarehouseReportPdf"),
57
+ data={"Data": {"reportRows": report_rows, "summaryRows": summary_rows}},
58
+ pdf=ReportPdfOptions(
59
+ page_size=PageSize.A4,
60
+ orientation=Orientation.LANDSCAPE,
61
+ metadata=PdfMetadata(title="Sold goods, week 39"),
62
+ ),
63
+ )
64
+ )
65
+
66
+ xlsx = client.render_report(
67
+ RenderReportRequest(
68
+ format=ReportFormat.XLSX,
69
+ template=TemplateRef.by_name("SoldGoodsForWarehouseReportXlsx"),
70
+ data=data,
71
+ )
72
+ )
73
+ ```
74
+
75
+ `data` is sent as written: dictionary keys keep their names, and dates, decimals, UUIDs and
76
+ dataclasses are written as JSON.
77
+
78
+ Templates imported from jsreport keep their shortids, so stored shortids still work:
79
+ `TemplateRef.by_shortid("rkJTnK2ce")`. `list_reports` returns each template's `shortid`.
80
+
81
+ A retry with the same `idempotency_key` returns the render the key already created.
82
+ `get_report_data_schema` returns the JSON Schema of a report's data; `list_reports` lists
83
+ templates and the formats each renders.
84
+
85
+ ## Render notification text
86
+
87
+ `render_string` renders a template string synchronously, without a PDF, for email bodies,
88
+ subjects, SMS and push messages. The Handlebars engine runs `#if`, `#each` and your helpers:
89
+
90
+ ```python
91
+ from typefold import RenderStringRequest, StringEngine, StringFormat
92
+
93
+ body = client.render_string(
94
+ RenderStringRequest(
95
+ template="{{#if (isEqual Status 'paid')}}Paid{{else}}Due{{/if}}: {{Booking.Code}}",
96
+ data=model,
97
+ engine=StringEngine.HANDLEBARS,
98
+ helpers="function isEqual(a, b) { return a === b; }",
99
+ format=StringFormat.HTML,
100
+ )
101
+ )
102
+ ```
103
+
104
+ `StringFormat.HTML` escapes values; `StringFormat.TEXT` (the default) writes them as they are.
105
+ Without `engine`, only `{{path}}` placeholders are replaced.
106
+
107
+ ## Render HTML or a template version
108
+
109
+ `render` creates a render, waits for it and downloads the PDF:
110
+
111
+ ```python
112
+ from typefold import DirectRenderRequest, HtmlDocument, PageSize, RenderSettings, TemplateVersionRenderRequest
113
+
114
+ pdf = client.render(
115
+ DirectRenderRequest(
116
+ document=HtmlDocument(html="<h1>Invoice 42</h1>"),
117
+ settings=RenderSettings(page_size=PageSize.A4),
118
+ )
119
+ )
120
+
121
+ ticket = client.render(
122
+ TemplateVersionRenderRequest(template_version_id=version_id, data={"ticket": {"number": "TK-1"}})
123
+ )
124
+ ```
125
+
126
+ It gives up after two minutes unless you pass a `timeout` in seconds. To render in the
127
+ background, use `create_render`, then `wait_for_render`, `download` or `cancel_render`, or the
128
+ API's render webhooks.
129
+
130
+ ## Render a batch
131
+
132
+ `create_batch` creates up to 100 renders in one request; if any item is invalid, none is created.
133
+ Items are `DirectRenderRequest` or `TemplateVersionRenderRequest`; their `wait_ms` is not sent.
134
+ `wait_for_batch` polls until no item is queued or processing (10 minutes unless you pass a
135
+ `timeout`), and `download_batch` returns one PDF of every item in order:
136
+
137
+ ```python
138
+ batch = client.create_batch(
139
+ [TemplateVersionRenderRequest(template_version_id=version_id, data=ticket) for ticket in tickets],
140
+ idempotency_key="tickets-2026-10-12",
141
+ )
142
+ batch = client.wait_for_batch(batch)
143
+
144
+ if batch.counts.succeeded == batch.total:
145
+ pdf = client.download_batch(batch.id)
146
+ ```
147
+
148
+ A failed item does not stop the others; `batch.items` gives each render's `id` and `state`, to
149
+ read, download or cancel it as a single render. `get_batch` reads a batch again.
150
+
151
+ ## Async
152
+
153
+ `AsyncTypefoldClient` has the same methods as coroutines:
154
+
155
+ ```python
156
+ from typefold import AsyncTypefoldClient
157
+
158
+ async with AsyncTypefoldClient("https://pdf.example.com", api_key) as client:
159
+ pdf = await client.render_report(request)
160
+ ```
161
+
162
+ ## Errors
163
+
164
+ A request the API refuses with `429 RATE_LIMITED`, such as a burst of parallel renders, is sent
165
+ again after its `Retry-After` up to `max_rate_limit_retries` times (3 by default; pass 0 to turn
166
+ this off). Other failures raise `TypefoldError`. `error.code` is the API's code, such as
167
+ `NOT_FOUND`, `RATE_LIMITED` or `RENDER_TIMEOUT`, and `error.retryable` says whether a retry can
168
+ help. When a render was created but failed or timed out, `error.error.render_id` names it.
169
+
170
+ ## Versions
171
+
172
+ Use a client no newer than your server: older clients keep working because `/v1` only grows,
173
+ but a newer client's fields or endpoints answer `400` or `404` on an older server.
174
+
175
+ ## httpx client
176
+
177
+ To share an `httpx.Client` (or `httpx.AsyncClient`), pass it as `http_client` with a timeout
178
+ above 95 seconds, the longest a report PDF can take. The API key goes on each request, not the
179
+ client, and never to download URLs. A client you pass in is not closed with the
180
+ `TypefoldClient`.
181
+
182
+ ## Development
183
+
184
+ ```sh
185
+ cd clients/python
186
+ python -m venv .venv && .venv/bin/pip install -e '.[test]'
187
+ .venv/bin/pytest && .venv/bin/mypy src tests && .venv/bin/ruff check . && .venv/bin/ruff format --check .
188
+ ```
@@ -0,0 +1,172 @@
1
+ # typefold-client
2
+
3
+ Python client for the Typefold API. Needs Python 3.10 or later; its only dependency is
4
+ `httpx`. It has a blocking `TypefoldClient` and an asyncio `AsyncTypefoldClient` with the
5
+ same methods.
6
+
7
+ ```sh
8
+ pip install typefold-client
9
+ ```
10
+
11
+ Create one client per API key and reuse it. The key needs `render:create`, `render:read` and
12
+ `template:read`, and `render:cancel` to cancel renders.
13
+
14
+ ```python
15
+ from typefold import TypefoldClient
16
+
17
+ client = TypefoldClient("https://pdf.example.com", api_key)
18
+ ```
19
+
20
+ Close it with `client.close()` when your application stops, or use it as a context manager.
21
+
22
+ ## Render a report
23
+
24
+ `render_report` renders a published report or saved template by name or ID and returns the PDF
25
+ or XLSX bytes:
26
+
27
+ ```python
28
+ from typefold import (
29
+ Orientation,
30
+ PageSize,
31
+ PdfMetadata,
32
+ RenderReportRequest,
33
+ ReportFormat,
34
+ ReportPdfOptions,
35
+ TemplateRef,
36
+ )
37
+
38
+ pdf = client.render_report(
39
+ RenderReportRequest(
40
+ template=TemplateRef.by_name("SoldGoodsForWarehouseReportPdf"),
41
+ data={"Data": {"reportRows": report_rows, "summaryRows": summary_rows}},
42
+ pdf=ReportPdfOptions(
43
+ page_size=PageSize.A4,
44
+ orientation=Orientation.LANDSCAPE,
45
+ metadata=PdfMetadata(title="Sold goods, week 39"),
46
+ ),
47
+ )
48
+ )
49
+
50
+ xlsx = client.render_report(
51
+ RenderReportRequest(
52
+ format=ReportFormat.XLSX,
53
+ template=TemplateRef.by_name("SoldGoodsForWarehouseReportXlsx"),
54
+ data=data,
55
+ )
56
+ )
57
+ ```
58
+
59
+ `data` is sent as written: dictionary keys keep their names, and dates, decimals, UUIDs and
60
+ dataclasses are written as JSON.
61
+
62
+ Templates imported from jsreport keep their shortids, so stored shortids still work:
63
+ `TemplateRef.by_shortid("rkJTnK2ce")`. `list_reports` returns each template's `shortid`.
64
+
65
+ A retry with the same `idempotency_key` returns the render the key already created.
66
+ `get_report_data_schema` returns the JSON Schema of a report's data; `list_reports` lists
67
+ templates and the formats each renders.
68
+
69
+ ## Render notification text
70
+
71
+ `render_string` renders a template string synchronously, without a PDF, for email bodies,
72
+ subjects, SMS and push messages. The Handlebars engine runs `#if`, `#each` and your helpers:
73
+
74
+ ```python
75
+ from typefold import RenderStringRequest, StringEngine, StringFormat
76
+
77
+ body = client.render_string(
78
+ RenderStringRequest(
79
+ template="{{#if (isEqual Status 'paid')}}Paid{{else}}Due{{/if}}: {{Booking.Code}}",
80
+ data=model,
81
+ engine=StringEngine.HANDLEBARS,
82
+ helpers="function isEqual(a, b) { return a === b; }",
83
+ format=StringFormat.HTML,
84
+ )
85
+ )
86
+ ```
87
+
88
+ `StringFormat.HTML` escapes values; `StringFormat.TEXT` (the default) writes them as they are.
89
+ Without `engine`, only `{{path}}` placeholders are replaced.
90
+
91
+ ## Render HTML or a template version
92
+
93
+ `render` creates a render, waits for it and downloads the PDF:
94
+
95
+ ```python
96
+ from typefold import DirectRenderRequest, HtmlDocument, PageSize, RenderSettings, TemplateVersionRenderRequest
97
+
98
+ pdf = client.render(
99
+ DirectRenderRequest(
100
+ document=HtmlDocument(html="<h1>Invoice 42</h1>"),
101
+ settings=RenderSettings(page_size=PageSize.A4),
102
+ )
103
+ )
104
+
105
+ ticket = client.render(
106
+ TemplateVersionRenderRequest(template_version_id=version_id, data={"ticket": {"number": "TK-1"}})
107
+ )
108
+ ```
109
+
110
+ It gives up after two minutes unless you pass a `timeout` in seconds. To render in the
111
+ background, use `create_render`, then `wait_for_render`, `download` or `cancel_render`, or the
112
+ API's render webhooks.
113
+
114
+ ## Render a batch
115
+
116
+ `create_batch` creates up to 100 renders in one request; if any item is invalid, none is created.
117
+ Items are `DirectRenderRequest` or `TemplateVersionRenderRequest`; their `wait_ms` is not sent.
118
+ `wait_for_batch` polls until no item is queued or processing (10 minutes unless you pass a
119
+ `timeout`), and `download_batch` returns one PDF of every item in order:
120
+
121
+ ```python
122
+ batch = client.create_batch(
123
+ [TemplateVersionRenderRequest(template_version_id=version_id, data=ticket) for ticket in tickets],
124
+ idempotency_key="tickets-2026-10-12",
125
+ )
126
+ batch = client.wait_for_batch(batch)
127
+
128
+ if batch.counts.succeeded == batch.total:
129
+ pdf = client.download_batch(batch.id)
130
+ ```
131
+
132
+ A failed item does not stop the others; `batch.items` gives each render's `id` and `state`, to
133
+ read, download or cancel it as a single render. `get_batch` reads a batch again.
134
+
135
+ ## Async
136
+
137
+ `AsyncTypefoldClient` has the same methods as coroutines:
138
+
139
+ ```python
140
+ from typefold import AsyncTypefoldClient
141
+
142
+ async with AsyncTypefoldClient("https://pdf.example.com", api_key) as client:
143
+ pdf = await client.render_report(request)
144
+ ```
145
+
146
+ ## Errors
147
+
148
+ A request the API refuses with `429 RATE_LIMITED`, such as a burst of parallel renders, is sent
149
+ again after its `Retry-After` up to `max_rate_limit_retries` times (3 by default; pass 0 to turn
150
+ this off). Other failures raise `TypefoldError`. `error.code` is the API's code, such as
151
+ `NOT_FOUND`, `RATE_LIMITED` or `RENDER_TIMEOUT`, and `error.retryable` says whether a retry can
152
+ help. When a render was created but failed or timed out, `error.error.render_id` names it.
153
+
154
+ ## Versions
155
+
156
+ Use a client no newer than your server: older clients keep working because `/v1` only grows,
157
+ but a newer client's fields or endpoints answer `400` or `404` on an older server.
158
+
159
+ ## httpx client
160
+
161
+ To share an `httpx.Client` (or `httpx.AsyncClient`), pass it as `http_client` with a timeout
162
+ above 95 seconds, the longest a report PDF can take. The API key goes on each request, not the
163
+ client, and never to download URLs. A client you pass in is not closed with the
164
+ `TypefoldClient`.
165
+
166
+ ## Development
167
+
168
+ ```sh
169
+ cd clients/python
170
+ python -m venv .venv && .venv/bin/pip install -e '.[test]'
171
+ .venv/bin/pytest && .venv/bin/mypy src tests && .venv/bin/ruff check . && .venv/bin/ruff format --check .
172
+ ```
@@ -0,0 +1,36 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.25"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "typefold-client"
7
+ version = "0.2.1"
8
+ description = "Python client for the Typefold API: render published reports and templates to PDF or XLSX."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ dependencies = ["httpx>=0.27,<1"]
12
+ keywords = ["pdf", "xlsx", "reporting", "rendering"]
13
+ classifiers = ["Programming Language :: Python :: 3", "Typing :: Typed"]
14
+
15
+ [project.urls]
16
+ Repository = "https://github.com/berkerdemirer/typefold"
17
+
18
+ [project.optional-dependencies]
19
+ test = ["pytest>=8", "mypy>=1.10", "ruff>=0.6"]
20
+
21
+ [tool.hatch.build.targets.wheel]
22
+ packages = ["src/typefold"]
23
+
24
+ [tool.pytest.ini_options]
25
+ testpaths = ["tests"]
26
+
27
+ [tool.ruff]
28
+ line-length = 120
29
+ target-version = "py310"
30
+
31
+ [tool.ruff.lint]
32
+ select = ["E", "F", "I", "UP", "B", "SIM"]
33
+
34
+ [tool.mypy]
35
+ strict = true
36
+ python_version = "3.10"
@@ -0,0 +1,90 @@
1
+ """Python client for the Typefold API."""
2
+
3
+ from ._client import AsyncTypefoldClient, TypefoldClient
4
+ from ._errors import TypefoldError
5
+ from .models import (
6
+ ApiError,
7
+ DirectRenderRequest,
8
+ ErrorDetail,
9
+ HtmlDocument,
10
+ Margins,
11
+ Orientation,
12
+ PageSize,
13
+ PdfAttachment,
14
+ PdfConformanceResult,
15
+ PdfEncryption,
16
+ PdfMetadata,
17
+ PdfOperation,
18
+ PdfOperationType,
19
+ PdfPermissions,
20
+ PdfPrinting,
21
+ PdfSignature,
22
+ PdfSignatureField,
23
+ Render,
24
+ RenderBatch,
25
+ RenderBatchCounts,
26
+ RenderBatchItemSummary,
27
+ RenderDiagnostic,
28
+ RenderEngine,
29
+ RenderReportRequest,
30
+ RenderRequest,
31
+ RenderResult,
32
+ RenderSettings,
33
+ RenderState,
34
+ RenderStringRequest,
35
+ RenderTiming,
36
+ ReportFormat,
37
+ ReportPdfOptions,
38
+ ReportSummary,
39
+ StringEngine,
40
+ StringFormat,
41
+ TemplateRef,
42
+ TemplateVersionRenderRequest,
43
+ XmpProperty,
44
+ XmpSchema,
45
+ )
46
+
47
+ __all__ = [
48
+ "ApiError",
49
+ "AsyncTypefoldClient",
50
+ "DirectRenderRequest",
51
+ "ErrorDetail",
52
+ "HtmlDocument",
53
+ "Margins",
54
+ "Orientation",
55
+ "PageSize",
56
+ "PdfAttachment",
57
+ "PdfConformanceResult",
58
+ "PdfEncryption",
59
+ "PdfMetadata",
60
+ "PdfOperation",
61
+ "PdfOperationType",
62
+ "PdfPermissions",
63
+ "PdfPrinting",
64
+ "TypefoldClient",
65
+ "TypefoldError",
66
+ "PdfSignature",
67
+ "PdfSignatureField",
68
+ "Render",
69
+ "RenderBatch",
70
+ "RenderBatchCounts",
71
+ "RenderBatchItemSummary",
72
+ "RenderDiagnostic",
73
+ "RenderEngine",
74
+ "RenderReportRequest",
75
+ "RenderRequest",
76
+ "RenderResult",
77
+ "RenderSettings",
78
+ "RenderState",
79
+ "RenderStringRequest",
80
+ "RenderTiming",
81
+ "ReportFormat",
82
+ "ReportPdfOptions",
83
+ "ReportSummary",
84
+ "StringEngine",
85
+ "StringFormat",
86
+ "TemplateRef",
87
+ "TemplateVersionRenderRequest",
88
+ "XmpProperty",
89
+ "XmpSchema",
90
+ ]