@contractkit/plugin-python 0.9.1

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 (39) hide show
  1. package/.turbo/turbo-build$colon$ci.log +35 -0
  2. package/.turbo/turbo-build.log +15 -0
  3. package/.turbo/turbo-test$colon$ci.log +59 -0
  4. package/.turbo/turbo-test.log +15 -0
  5. package/CHANGELOG.md +118 -0
  6. package/README.md +86 -0
  7. package/coverage/base.css +224 -0
  8. package/coverage/block-navigation.js +87 -0
  9. package/coverage/clover.xml +559 -0
  10. package/coverage/coverage-final.json +4 -0
  11. package/coverage/favicon.png +0 -0
  12. package/coverage/index.html +131 -0
  13. package/coverage/prettify.css +1 -0
  14. package/coverage/prettify.js +2 -0
  15. package/coverage/sort-arrow-sprite.png +0 -0
  16. package/coverage/sorter.js +210 -0
  17. package/coverage/src/codegen-client.ts.html +2071 -0
  18. package/coverage/src/codegen-models.ts.html +1360 -0
  19. package/coverage/src/index.html +131 -0
  20. package/coverage/tests/helpers.ts.html +667 -0
  21. package/coverage/tests/index.html +116 -0
  22. package/dist/codegen-client.d.ts +30 -0
  23. package/dist/codegen-client.d.ts.map +1 -0
  24. package/dist/codegen-models.d.ts +19 -0
  25. package/dist/codegen-models.d.ts.map +1 -0
  26. package/dist/index.d.ts +17 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +1046 -0
  29. package/dist/index.js.map +1 -0
  30. package/eslint.config.js +6 -0
  31. package/package.json +45 -0
  32. package/src/codegen-client.ts +662 -0
  33. package/src/codegen-models.ts +425 -0
  34. package/src/index.ts +143 -0
  35. package/tests/codegen-client.test.ts +361 -0
  36. package/tests/codegen-models.test.ts +295 -0
  37. package/tests/helpers.ts +194 -0
  38. package/tsconfig.json +9 -0
  39. package/vitest.config.ts +14 -0
@@ -0,0 +1,116 @@
1
+
2
+ <!doctype html>
3
+ <html lang="en">
4
+
5
+ <head>
6
+ <title>Code coverage report for tests</title>
7
+ <meta charset="utf-8" />
8
+ <link rel="stylesheet" href="../prettify.css" />
9
+ <link rel="stylesheet" href="../base.css" />
10
+ <link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
11
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
12
+ <style type='text/css'>
13
+ .coverage-summary .sorter {
14
+ background-image: url(../sort-arrow-sprite.png);
15
+ }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <div class='wrapper'>
21
+ <div class='pad1'>
22
+ <h1><a href="../index.html">All files</a> tests</h1>
23
+ <div class='clearfix'>
24
+
25
+ <div class='fl pad1y space-right2'>
26
+ <span class="strong">86.66% </span>
27
+ <span class="quiet">Statements</span>
28
+ <span class='fraction'>39/45</span>
29
+ </div>
30
+
31
+
32
+ <div class='fl pad1y space-right2'>
33
+ <span class="strong">74.19% </span>
34
+ <span class="quiet">Branches</span>
35
+ <span class='fraction'>23/31</span>
36
+ </div>
37
+
38
+
39
+ <div class='fl pad1y space-right2'>
40
+ <span class="strong">92% </span>
41
+ <span class="quiet">Functions</span>
42
+ <span class='fraction'>23/25</span>
43
+ </div>
44
+
45
+
46
+ <div class='fl pad1y space-right2'>
47
+ <span class="strong">92.1% </span>
48
+ <span class="quiet">Lines</span>
49
+ <span class='fraction'>35/38</span>
50
+ </div>
51
+
52
+
53
+ </div>
54
+ <p class="quiet">
55
+ Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
56
+ </p>
57
+ <template id="filterTemplate">
58
+ <div class="quiet">
59
+ Filter:
60
+ <input type="search" id="fileSearch">
61
+ </div>
62
+ </template>
63
+ </div>
64
+ <div class='status-line high'></div>
65
+ <div class="pad1">
66
+ <table class="coverage-summary">
67
+ <thead>
68
+ <tr>
69
+ <th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
70
+ <th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
71
+ <th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
72
+ <th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
73
+ <th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
74
+ <th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
75
+ <th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
76
+ <th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
77
+ <th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
78
+ <th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
79
+ </tr>
80
+ </thead>
81
+ <tbody><tr>
82
+ <td class="file high" data-value="helpers.ts"><a href="helpers.ts.html">helpers.ts</a></td>
83
+ <td data-value="86.66" class="pic high">
84
+ <div class="chart"><div class="cover-fill" style="width: 86%"></div><div class="cover-empty" style="width: 14%"></div></div>
85
+ </td>
86
+ <td data-value="86.66" class="pct high">86.66%</td>
87
+ <td data-value="45" class="abs high">39/45</td>
88
+ <td data-value="74.19" class="pct medium">74.19%</td>
89
+ <td data-value="31" class="abs medium">23/31</td>
90
+ <td data-value="92" class="pct high">92%</td>
91
+ <td data-value="25" class="abs high">23/25</td>
92
+ <td data-value="92.1" class="pct high">92.1%</td>
93
+ <td data-value="38" class="abs high">35/38</td>
94
+ </tr>
95
+
96
+ </tbody>
97
+ </table>
98
+ </div>
99
+ <div class='push'></div><!-- for sticky footer -->
100
+ </div><!-- /wrapper -->
101
+ <div class='footer quiet pad2 space-top1 center small'>
102
+ Code coverage generated by
103
+ <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
104
+ at 2026-04-29T12:51:10.031Z
105
+ </div>
106
+ <script src="../prettify.js"></script>
107
+ <script>
108
+ window.onload = function () {
109
+ prettyPrint();
110
+ };
111
+ </script>
112
+ <script src="../sorter.js"></script>
113
+ <script src="../block-navigation.js"></script>
114
+ </body>
115
+ </html>
116
+
@@ -0,0 +1,30 @@
1
+ import type { OpRootNode } from '@contractkit/core';
2
+ export interface ClientCodegenOptions {
3
+ /** Map from model name → Python module path (e.g. "._models_payments") */
4
+ modelModulePaths?: Map<string, string>;
5
+ /** Current client's module name (e.g. "_client_payments") — used to avoid self-imports */
6
+ currentModule?: string;
7
+ /** Set of model names that have Input variants */
8
+ modelsWithInput?: Set<string>;
9
+ /**
10
+ * Whether to emit client methods for operations marked `internal`. Defaults to `false` —
11
+ * internal ops are omitted so consumers don't pick them up. Set to `true` for an
12
+ * internal-use SDK that should expose them.
13
+ */
14
+ includeInternal?: boolean;
15
+ }
16
+ /**
17
+ * Returns true if the root contains at least one operation eligible for client emission.
18
+ * With `includeInternal: false` (default) that means at least one non-internal op; with
19
+ * `includeInternal: true` any op qualifies.
20
+ */
21
+ export declare function hasPublicOperations(root: OpRootNode, includeInternal?: boolean): boolean;
22
+ /**
23
+ * Generate a Pydantic/httpx async Python client class from an OpRootNode.
24
+ */
25
+ export declare function generatePythonClient(root: OpRootNode, opts?: ClientCodegenOptions): string;
26
+ export declare function deriveClientClassName(file: string): string;
27
+ export declare function deriveClientModuleName(file: string): string;
28
+ export declare function deriveClientPropertyName(file: string): string;
29
+ export declare const BASE_CLIENT_PY = "# Auto-generated by contractkit-plugin-python-sdk. Do not edit manually.\nfrom __future__ import annotations\n\nimport httpx\nfrom typing import Any\n\n\nclass SdkError(Exception):\n def __init__(self, status: int, status_text: str, body: Any):\n super().__init__(f\"{status} {status_text}\")\n self.status = status\n self.status_text = status_text\n self.body = body\n\n\nclass BaseClient:\n def __init__(self, base_url: str, headers: dict[str, str] | None = None):\n self._base_url = base_url.rstrip(\"/\")\n self._headers = headers or {}\n self._http = httpx.AsyncClient()\n\n async def _fetch(\n self,\n path: str,\n *,\n method: str,\n body: Any = None,\n params: dict | None = None,\n extra_headers: dict | None = None,\n content_type: str | None = None,\n body_kind: str = \"json\",\n response_kind: str = \"json\",\n ) -> Any:\n result, _ = await self._fetch_with_headers(\n path,\n method=method,\n body=body,\n params=params,\n extra_headers=extra_headers,\n content_type=content_type,\n body_kind=body_kind,\n response_kind=response_kind,\n )\n return result\n\n async def _fetch_with_headers(\n self,\n path: str,\n *,\n method: str,\n body: Any = None,\n params: dict | None = None,\n extra_headers: dict | None = None,\n content_type: str | None = None,\n body_kind: str = \"json\",\n response_kind: str = \"json\",\n ) -> tuple[Any, dict[str, str]]:\n headers = {**self._headers, **(extra_headers or {})}\n if body is not None:\n headers[\"Content-Type\"] = content_type or \"application/json\"\n # body_kind controls how httpx serializes the request body:\n # \"json\" \u2014 body is a JSON-serializable object, sent via httpx's json= kwarg\n # \"text\"/\"binary\" \u2014 body is a raw str/bytes payload, sent via content= unchanged\n request_kwargs: dict[str, Any] = {\"method\": method, \"url\": f\"{self._base_url}{path}\", \"params\": params, \"headers\": headers}\n if body is not None:\n if body_kind == \"json\":\n request_kwargs[\"json\"] = body\n else:\n request_kwargs[\"content\"] = body\n response = await self._http.request(**request_kwargs)\n if not response.is_success:\n try:\n error_body = response.json()\n except Exception:\n error_body = response.text\n raise SdkError(response.status_code, response.reason_phrase, error_body)\n # HTTP headers are case-insensitive \u2014 normalize to lowercase keys for stable lookup.\n response_headers = {k.lower(): v for k, v in response.headers.items()}\n if response.status_code == 204 or not response.content:\n return None, response_headers\n if response_kind == \"text\":\n return response.text, response_headers\n if response_kind == \"binary\":\n return response.content, response_headers\n return response.json(), response_headers\n";
30
+ //# sourceMappingURL=codegen-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codegen-client.d.ts","sourceRoot":"","sources":["../src/codegen-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAqF,MAAM,mBAAmB,CAAC;AAMvI,MAAM,WAAW,oBAAoB;IACjC,0EAA0E;IAC1E,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,0FAA0F;IAC1F,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kDAAkD;IAClD,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,UAAU,EAAE,eAAe,UAAQ,GAAG,OAAO,CAOtF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,GAAE,oBAAyB,GAAG,MAAM,CA0G9F;AA4XD,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQ3D;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG7D;AAmCD,eAAO,MAAM,cAAc,wuGAqF1B,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { ContractRootNode, ContractTypeNode } from '@contractkit/core';
2
+ export interface ModelCodegenOptions {
3
+ /** Map from model name → output module name (for cross-file imports) */
4
+ modelModulePaths?: Map<string, string>;
5
+ /** Current output module name (used to skip self-imports) */
6
+ currentModule?: string;
7
+ /** Set of model names that have Input variants (cross-file) */
8
+ modelsWithInput?: Set<string>;
9
+ }
10
+ /**
11
+ * Generate Pydantic v2 model classes from a ContractRootNode.
12
+ */
13
+ export declare function generatePydanticModels(root: ContractRootNode, opts?: ModelCodegenOptions): string;
14
+ export declare function renderPyType(type: ContractTypeNode, modelsWithInput?: Set<string>, forInput?: boolean): string;
15
+ /** Convert a field name to a valid Python identifier in snake_case. */
16
+ export declare function toPythonFieldName(name: string): string;
17
+ /** Derive a Python module name from a .ck file path, e.g. "ledger.categories.ck" → "_models_ledger_categories" */
18
+ export declare function deriveModelsModuleName(file: string): string;
19
+ //# sourceMappingURL=codegen-models.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codegen-models.d.ts","sourceRoot":"","sources":["../src/codegen-models.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAwB,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAKlG,MAAM,WAAW,mBAAmB;IAChC,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,6DAA6D;IAC7D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,+DAA+D;IAC/D,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACjC;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,gBAAgB,EAAE,IAAI,GAAE,mBAAwB,GAAG,MAAM,CAwDrG;AAsGD,wBAAgB,YAAY,CAAC,IAAI,EAAE,gBAAgB,EAAE,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,QAAQ,UAAQ,GAAG,MAAM,CAkC5G;AAyCD,uEAAuE;AACvE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAUtD;AA0JD,kHAAkH;AAClH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQ3D"}
@@ -0,0 +1,17 @@
1
+ import type { ContractKitPlugin } from '@contractkit/core';
2
+ export interface PythonSdkPluginConfig {
3
+ /** Output directory relative to rootDir (default: "python-sdk") */
4
+ baseDir?: string;
5
+ /** Python package name used in the aggregator class name (default: "Sdk") */
6
+ packageName?: string;
7
+ /**
8
+ * Whether to emit client methods for operations marked `internal`. Defaults to `false` —
9
+ * internal ops are omitted so consumers don't pick them up. Set to `true` for an
10
+ * internal-use SDK that should expose them.
11
+ */
12
+ includeInternal?: boolean;
13
+ }
14
+ declare const plugin: ContractKitPlugin;
15
+ export default plugin;
16
+ export declare function createPythonSdkPlugin(config: PythonSdkPluginConfig, rootDir: string): ContractKitPlugin;
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAW3D,MAAM,WAAW,qBAAqB;IAClC,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC7B;AAID,QAAA,MAAM,MAAM,EAAE,iBAOb,CAAC;AAEF,eAAe,MAAM,CAAC;AAItB,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB,EAAE,OAAO,EAAE,MAAM,GAAG,iBAAiB,CAsGvG"}