apcore-toolkit 0.4.2__tar.gz → 0.6.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 (68) hide show
  1. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/.gitignore +4 -0
  2. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/CHANGELOG.md +57 -0
  3. apcore_toolkit-0.6.0/LICENSE +185 -0
  4. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/PKG-INFO +9 -10
  5. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/README.md +2 -6
  6. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/pyproject.toml +5 -4
  7. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/src/apcore_toolkit/__init__.py +40 -4
  8. apcore_toolkit-0.6.0/src/apcore_toolkit/_type_mapping.py +19 -0
  9. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/src/apcore_toolkit/ai_enhancer.py +85 -40
  10. apcore_toolkit-0.6.0/src/apcore_toolkit/binding_loader.py +387 -0
  11. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/src/apcore_toolkit/convention_scanner.py +69 -33
  12. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/src/apcore_toolkit/display/resolver.py +38 -5
  13. apcore_toolkit-0.6.0/src/apcore_toolkit/formatting/__init__.py +17 -0
  14. apcore_toolkit-0.6.0/src/apcore_toolkit/formatting/surface.py +336 -0
  15. apcore_toolkit-0.6.0/src/apcore_toolkit/http_verb_map.py +165 -0
  16. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/src/apcore_toolkit/openapi.py +82 -13
  17. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/src/apcore_toolkit/output/__init__.py +13 -2
  18. apcore_toolkit-0.6.0/src/apcore_toolkit/output/errors.py +35 -0
  19. apcore_toolkit-0.6.0/src/apcore_toolkit/output/http_proxy_writer.py +274 -0
  20. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/src/apcore_toolkit/output/python_writer.py +9 -9
  21. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/src/apcore_toolkit/output/registry_writer.py +36 -4
  22. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/src/apcore_toolkit/output/types.py +1 -1
  23. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/src/apcore_toolkit/output/verifiers.py +32 -10
  24. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/src/apcore_toolkit/output/yaml_writer.py +87 -22
  25. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/src/apcore_toolkit/pydantic_utils.py +32 -24
  26. apcore_toolkit-0.6.0/src/apcore_toolkit/resolve_target.py +82 -0
  27. apcore_toolkit-0.6.0/src/apcore_toolkit/scanner.py +213 -0
  28. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/src/apcore_toolkit/serializers.py +2 -0
  29. apcore_toolkit-0.6.0/src/apcore_toolkit/types.py +83 -0
  30. apcore_toolkit-0.6.0/tests/fixtures/scanner_verb_map.json +100 -0
  31. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/tests/test_ai_enhancer.py +97 -0
  32. apcore_toolkit-0.6.0/tests/test_binding_loader.py +485 -0
  33. apcore_toolkit-0.6.0/tests/test_convention_scanner.py +319 -0
  34. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/tests/test_display_resolver.py +203 -0
  35. apcore_toolkit-0.6.0/tests/test_format_surface.py +279 -0
  36. apcore_toolkit-0.6.0/tests/test_http_proxy_writer.py +487 -0
  37. apcore_toolkit-0.6.0/tests/test_http_verb_map.py +246 -0
  38. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/tests/test_openapi.py +166 -0
  39. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/tests/test_output_factory.py +23 -0
  40. apcore_toolkit-0.6.0/tests/test_pydantic_utils.py +267 -0
  41. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/tests/test_python_writer.py +26 -0
  42. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/tests/test_registry_writer.py +29 -1
  43. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/tests/test_scanner.py +157 -5
  44. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/tests/test_serializers.py +19 -0
  45. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/tests/test_types.py +72 -2
  46. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/tests/test_verifiers.py +91 -4
  47. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/tests/test_write_error.py +14 -9
  48. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/tests/test_yaml_writer.py +134 -0
  49. apcore_toolkit-0.4.2/src/apcore_toolkit/formatting/__init__.py +0 -7
  50. apcore_toolkit-0.4.2/src/apcore_toolkit/output/errors.py +0 -17
  51. apcore_toolkit-0.4.2/src/apcore_toolkit/output/http_proxy_writer.py +0 -187
  52. apcore_toolkit-0.4.2/src/apcore_toolkit/scanner.py +0 -134
  53. apcore_toolkit-0.4.2/src/apcore_toolkit/types.py +0 -46
  54. apcore_toolkit-0.4.2/tests/test_convention_scanner.py +0 -118
  55. apcore_toolkit-0.4.2/tests/test_http_proxy_writer.py +0 -204
  56. apcore_toolkit-0.4.2/tests/test_pydantic_utils.py +0 -120
  57. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/.github/CODEOWNERS +0 -0
  58. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/.github/copilot-ignore +0 -0
  59. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/.github/workflows/ci.yml +0 -0
  60. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/.gitmessage +0 -0
  61. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/.pre-commit-config.yaml +0 -0
  62. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/src/apcore_toolkit/display/__init__.py +0 -0
  63. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/src/apcore_toolkit/formatting/markdown.py +0 -0
  64. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/src/apcore_toolkit/schema_utils.py +0 -0
  65. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/tests/conftest.py +0 -0
  66. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/tests/test_markdown.py +0 -0
  67. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/tests/test_schema_utils.py +0 -0
  68. {apcore_toolkit-0.4.2 → apcore_toolkit-0.6.0}/tests/test_write_result.py +0 -0
@@ -11,6 +11,10 @@ build/
11
11
  .DS_Store
12
12
  .ruff_cache/
13
13
  .pytest_cache/
14
+ .mypy_cache/
15
+ .pyright_cache/
16
+ htmlcov/
17
+ coverage.xml
14
18
  .coverage
15
19
  .venv/
16
20
  venv/
@@ -2,6 +2,63 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+
6
+ ## [0.6.0] - 2026-05-07
7
+
8
+ ### Changed
9
+
10
+ - **`apcore` minimum version bumped from 0.20.0 to 0.21.0** — `pyproject.toml` `dependencies` now requires `apcore>=0.21.0`. Toolkit only imports stable apcore surface (`ModuleAnnotations`, `DEFAULT_ANNOTATIONS`, `ModuleExample`, `Registry`, `ErrorCodes`, `parse_docstring`, `errors.ModuleError`); the 0.21.0 additions (`discoverable` field on `ModuleAnnotations`, `PreviewResult`, `Change`, `ephemeral.*` namespace) are automatically handled — `ModuleAnnotations.from_dict()` picks up `discoverable` transparently, and `dataclasses.asdict()` / `dataclasses.replace()` propagate the new field without code changes. `AIEnhancer` derives its field set from `dataclasses.fields(ModuleAnnotations)` at import time, so it also picks up `discoverable` automatically. Full pytest suite verified against apcore 0.21.0.
11
+ - **`apcore` minimum version bumped from 0.19.0 to 0.20.0** — `pyproject.toml` `dependencies` now requires `apcore>=0.20.0`. Toolkit only imports stable apcore surface (`ModuleAnnotations`, `DEFAULT_ANNOTATIONS`, `ModuleExample`, `Registry`, `ErrorCodes`, `parse_docstring`, `errors.ModuleError`); none of these were affected by 0.20.0 changes. Full pytest suite (585 passed) verified against apcore 0.20.0.
12
+
13
+ ### Added
14
+
15
+ - **Surface-aware formatters** (refs aiperceivable/apcore-toolkit#13) — `format_module`, `format_schema`, `format_modules` for rendering `ScannedModule` and JSON Schema for specific consumer surfaces. Four styles for `format_module`: `markdown` (LLM context), `skill` (drop-in `.claude/skills/<id>/SKILL.md` or `.gemini/skills/<id>/SKILL.md` body with minimal `name` + `description` frontmatter — no vendor-specific extensions), `table-row` (CLI listing), `json` (programmatic). `format_schema` styles: `prose`, `table`, `json`. `format_modules` adds optional `group_by="tag" | "prefix"`. `display=True` (default) prefers the `ScannedModule.display` overlay over raw fields. Lives in `apcore_toolkit.formatting.surface`; re-exported at the top-level package.
16
+ - **Annotation-table cross-SDK alignment** — `format_module(style="markdown" | "skill")` `## Behavior` table now emits only fields that differ from `ModuleAnnotations()` defaults, sorts rows alphabetically by snake_case key, and renders bool values as lowercase `true`/`false`. The section is omitted entirely when every annotation field matches its default. Closes the byte-equality gap with the TypeScript and Rust SDKs.
17
+
18
+ ### Changed
19
+
20
+ - **`infer_annotations_from_method` canonical mapping** (refs aiperceivable/apcore-toolkit#11) — `HEAD` and `OPTIONS` now map to `readonly=true` (without `cacheable=true`), matching the canonical mapping declared in `apcore-toolkit/docs/features/scanning.md` and aligning with the existing Rust SDK. Previously these methods returned default annotations.
21
+
22
+ ## [0.5.0] - 2026-04-21
23
+
24
+ ### Added
25
+
26
+ - **`BindingLoader`** (`apcore_toolkit.binding_loader`) — parses `.binding.yaml` files back into `ScannedModule` objects, the inverse of `YAMLWriter`. Unlike `apcore.BindingLoader`, this is pure data: no target import, no Registry mutation. Enables verification, merging, diffing, and round-trip workflows.
27
+ - `load(path, *, strict=False)` — single file or directory of `*.binding.yaml`.
28
+ - `load_data(data, *, strict=False)` — pre-parsed YAML dict.
29
+ - Loose mode (default): only `module_id + target` required; missing fields use defaults.
30
+ - Strict mode: additionally requires `input_schema + output_schema`.
31
+ - `spec_version` validated; missing/unsupported versions WARN but do not fail.
32
+ - `annotations` parsed via `ModuleAnnotations.from_dict`; malformed values degrade to `None` with WARN.
33
+ - `examples` entries validated individually; malformed ones skipped with WARN.
34
+ - `BindingLoadError` exception carries `file_path`, `module_id`, `missing_fields`, `reason`.
35
+ - **`ScannedModule.display`** — new top-level optional field (`dict | None`) holding the sparse display overlay for binding YAML persistence. Distinct from `metadata["display"]` (resolved form produced by `DisplayResolver`).
36
+ - **New feature doc**: `docs/features/binding-loader.md`; `display-overlay.md` and `output-writers.md` updated.
37
+
38
+ ### Changed
39
+
40
+ - **`YAMLWriter._build_binding`** — emits top-level `display:` key only when `ScannedModule.display is not None` (skip when None keeps output clean).
41
+ - **`serializers.module_to_dict`** — includes `display` key in output.
42
+ - **`AIEnhancer._build_prompt`** — confidence template is now built dynamically from `gaps`. When `annotations` is in gaps, the prompt requests per-field confidence for every `_ANNOTATION_FIELD_VALIDATORS` field (`annotations.readonly`, `annotations.streaming`, `annotations.cache_ttl`, ...). Previously the template hard-coded `{"description": 0.0, "documentation": 0.0}` only, causing all annotation-field confidence lookups to fall back to `0.0` and fail the threshold check — annotation enhancement silently never took effect. Fixes symmetry with `_enhance_module`'s `ann_conf.get(f"annotations.{field_name}", ...)` read path.
43
+
44
+ ### Dependencies
45
+
46
+ - **`apcore >= 0.19.0`** — picks up the expanded `ModuleAnnotations` (12 fields incl. `streaming`, `cacheable`, `cache_ttl`, `cache_key_fields`, `paginated`, `pagination_style`, `extra`). No toolkit code changes were needed for the type itself — `_build_annotation_field_validators` reflects the updated dataclass automatically.
47
+
48
+ ### Tests
49
+
50
+ - +34 new tests: 24 for `BindingLoader` (parsing, strict/loose modes, spec_version, file & directory loading, round-trip with `YAMLWriter`), 5 for the prompt confidence block, and 5 hardening tests (display deep-copy, malformed-shape warn, recursive glob, UTF-8 encoding, null-field error wording).
51
+ - Updated `test_field_count` (13 → 14) and `test_all_expected_keys` for the new `display` field.
52
+ - Total suite: 440 tests.
53
+
54
+ ### Hardening (post-review)
55
+
56
+ - **`BindingLoader`**: warns (rather than silently drops) malformed `display` values that are not a mapping; `load()` gained a `recursive: bool = False` kwarg for nested binding layouts; `read_text` now forces UTF-8 decoding so non-ASCII aliases round-trip on non-UTF-8 locales; required-field validation now rejects wrong-type scalars (e.g. `module_id: 42`, `target: true`) and empty strings in addition to absent/null, matching the Rust loader's contract — error wording is "missing or invalid required fields"; nested `input_schema`/`output_schema`/`metadata` are now deep-copied via `copy.deepcopy` so caller mutation does not leak back into the parsed YAML source graph.
57
+ - **`YAMLWriter`**: `display` is now deep-copied into the emitted binding (defensive parity with the TypeScript/Rust writers) so post-write mutation of `ScannedModule.display` cannot leak into the file. File writes are now atomic: the payload is written to `<name>.<pid>.tmp`, `fsync`ed, then `os.replace`d onto the final path (matches the TypeScript `tmp + rename` and Rust `tmp + sync_all + rename` writers). A process crash mid-write no longer leaves a partial YAML file that `BindingLoader` would fail to parse. A pre-write check refuses to overwrite a symlink at the target path (defence-in-depth against TOCTOU).
58
+ - **`BaseScanner.deduplicate_ids`**: pre-scans all input `module_id`s so generated `_N` suffixes never collide with an ID already present in the input. Input `[a, a, a_2]` now yields `[a, a_3, a_2]` instead of the previous buggy `[a, a_2, a_2]`. Matches the TypeScript and Rust implementations.
59
+ - **`resolve_target` / `RegistryWriter.write`**: new `allowed_prefixes: list[str] | None` kwarg (forwarded from `RegistryWriter.write` through `_to_function_module` to `resolve_target`). When set, `resolve_target` rejects any module path outside the listed prefixes **before** calling `importlib.import_module`, raising `PermissionError`. Mitigates arbitrary-code-execution via forged binding files (e.g. a malicious `target: "os:system"` injected into untrusted YAML). Parity with the TypeScript SDK's `allowedPrefixes` option, adapted to Python's module-name import model. Boundary-aware: `"myapp"` permits `myapp.views` but NOT `myappx.foo`. Rust does not need this because `resolve_target` is parse-only and the `HandlerFactory` is the security boundary.
60
+ - **`ScannedModule.display`**: moved to the END of the dataclass so existing positional `ScannedModule(...)` callers are not broken by the new field.
61
+
5
62
  ## [0.4.1] - 2026-03-25
6
63
 
7
64
  ### Added
@@ -0,0 +1,185 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship made available under
36
+ the License, as indicated by a copyright notice that is included in
37
+ or attached to the work (an example is provided in the Appendix below).
38
+
39
+ "Derivative Works" shall mean any work, whether in Source or Object
40
+ form, that is based on (or derived from) the Work and for which the
41
+ editorial revisions, annotations, elaborations, or other modifications
42
+ represent, as a whole, an original work of authorship. For the purposes
43
+ of this License, Derivative Works shall not include works that remain
44
+ separable from, or merely link (or bind by name) to the interfaces of,
45
+ the Work and Derivative Works thereof.
46
+
47
+ "Contribution" shall mean, as defined in Section 5, any work of authorship,
48
+ including the original version of the Work and any modifications or
49
+ additions to that Work or Derivative Works of the Work, that is
50
+ intentionally submitted to the Licensor for inclusion in the Work by the
51
+ copyright owner or by an individual or Legal Entity authorized to submit
52
+ on behalf of the copyright owner. For the purposes of this definition,
53
+ "submitted" means any form of electronic, verbal, or written communication
54
+ sent to the Licensor or its representatives, including but not limited to
55
+ communication on electronic mailing lists, source code control systems,
56
+ and issue tracking systems that are managed by, or on behalf of, the
57
+ Licensor for the purpose of discussing and improving the Work, but
58
+ excluding communication that is conspicuously marked or otherwise
59
+ designated in writing by the copyright owner as "Not a Contribution."
60
+
61
+ "Contributor" shall mean Licensor and any Legal Entity on behalf of
62
+ whom a Contribution has been received by the Licensor and subsequently
63
+ incorporated within the Work.
64
+
65
+ 2. Grant of Copyright License. Subject to the terms and conditions of
66
+ this License, each Contributor hereby grants to You a perpetual,
67
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
68
+ copyright license to reproduce, prepare Derivative Works of,
69
+ publicly display, publicly perform, sublicense, and distribute the
70
+ Work and such Derivative Works in Source or Object form.
71
+
72
+ 3. Grant of Patent License. Subject to the terms and conditions of
73
+ this License, each Contributor hereby grants to You a perpetual,
74
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
75
+ (except as stated in this section) patent license to make, have made,
76
+ use, offer to sell, sell, import, and otherwise transfer the Work,
77
+ where such license applies only to those patent claims licensable
78
+ by such Contributor that are necessarily infringed by their
79
+ Contribution(s) alone or by the combination of their Contribution(s)
80
+ with the Work to which such Contribution(s) was submitted. If You
81
+ institute patent litigation against any entity (including a cross-claim
82
+ or counterclaim in a lawsuit) alleging that the Work or any Contribution
83
+ incorporated within the Work constitutes direct or contributory patent
84
+ infringement, then any patent licenses granted to You under this License
85
+ for that Work shall terminate as of the date such litigation is filed.
86
+
87
+ 4. Redistribution. You may reproduce and distribute copies of the
88
+ Work or Derivative Works thereof in any medium, with or without
89
+ modifications, and in Source or Object form, provided that You
90
+ meet the following conditions:
91
+
92
+ (a) You must give any other recipients of the Work or Derivative
93
+ Works a copy of this License; and
94
+
95
+ (b) You must cause any modified files to carry prominent notices
96
+ stating that You changed the files; and
97
+
98
+ (c) You must retain, in the Source form of any Derivative Works
99
+ that You distribute, all copyright, patent, trademark, and
100
+ attribution notices from the Source form of the Work,
101
+ excluding those notices that do not pertain to any part of
102
+ the Derivative Works; and
103
+
104
+ (d) If the Work includes a "NOTICE" text file as part of its
105
+ distribution, You must include a readable copy of the
106
+ attribution notices contained within such NOTICE file, in
107
+ at least one of the following places: within a NOTICE text
108
+ file distributed as part of the Derivative Works; within
109
+ the Source form or documentation, if provided along with the
110
+ Derivative Works; or, within a display generated by the
111
+ Derivative Works, if and wherever such third-party notices
112
+ normally appear. The contents of the NOTICE file are for
113
+ informational purposes only and do not modify the License.
114
+ You may add Your own attribution notices within Derivative
115
+ Works that You distribute, alongside or as an addendum to
116
+ the NOTICE text from the Work, provided that such additional
117
+ attribution notices cannot be construed as modifying the License.
118
+
119
+ You may add Your own license statement for Your modifications and
120
+ may provide additional grant of rights to use, copy, modify, merge,
121
+ publish, distribute, sublicense, and/or sell copies of the Work and
122
+ Derivative Works, subject to the conditions of this License.
123
+
124
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
125
+ any Contribution intentionally submitted for inclusion in the Work
126
+ by You to the Licensor shall be under the terms and conditions of
127
+ this License, without any additional terms or conditions.
128
+ Notwithstanding the above, nothing herein shall supersede or modify
129
+ the terms of any separate license agreement you may have executed
130
+ with Licensor regarding such Contributions.
131
+
132
+ 6. Trademarks. This License does not grant permission to use the trade
133
+ names, trademarks, service marks, or product names of the Licensor,
134
+ except as required for reasonable and customary use in describing the
135
+ origin of the Work and reproducing the content of the NOTICE file.
136
+
137
+ 7. Disclaimer of Warranty. Unless required by applicable law or
138
+ agreed to in writing, Licensor provides the Work (and each
139
+ Contributor provides its Contributions) on an "AS IS" BASIS,
140
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
141
+ implied, including, without limitation, any warranties or conditions
142
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
143
+ PARTICULAR PURPOSE. You are solely responsible for determining the
144
+ appropriateness of using or reproducing the Work and assume any
145
+ risks associated with Your exercise of permissions under this License.
146
+
147
+ 8. Limitation of Liability. In no event and under no legal theory,
148
+ whether in tort (including negligence), contract, or otherwise,
149
+ unless required by applicable law (such as deliberate and grossly
150
+ negligent acts) or agreed to in writing, shall any Contributor be
151
+ liable to You for damages, including any direct, indirect, special,
152
+ incidental, or exemplary damages of any character arising as a
153
+ result of this License or out of the use or inability to use the
154
+ Work (including but not limited to damages for loss of goodwill,
155
+ work stoppage, computer failure or malfunction, or all other
156
+ commercial damages or losses), even if such Contributor has been
157
+ advised of the possibility of such damages.
158
+
159
+ 9. Accepting Warranty or Additional Liability. While redistributing
160
+ the Work or Derivative Works thereof, You may choose to offer,
161
+ and charge a fee for, acceptance of support, warranty, indemnity,
162
+ or other liability obligations and/or rights consistent with this
163
+ License. However, in accepting such obligations, You may offer
164
+ such conditions only on Your own behalf and on Your sole
165
+ responsibility, not on behalf of any other Contributor, and only
166
+ if You agree to indemnify, defend, and hold each Contributor
167
+ harmless for any liability incurred by, or claims asserted against,
168
+ such Contributor by reason of your accepting any such warranty or
169
+ additional liability.
170
+
171
+ END OF TERMS AND CONDITIONS
172
+
173
+ Copyright 2024 aiperceivable
174
+
175
+ Licensed under the Apache License, Version 2.0 (the "License");
176
+ you may not use this file except in compliance with the License.
177
+ You may obtain a copy of the License at
178
+
179
+ http://www.apache.org/licenses/LICENSE-2.0
180
+
181
+ Unless required by applicable law or agreed to in writing, software
182
+ distributed under the License is distributed on an "AS IS" BASIS,
183
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
184
+ See the License for the specific language governing permissions and
185
+ limitations under the License.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: apcore-toolkit
3
- Version: 0.4.2
3
+ Version: 0.6.0
4
4
  Summary: Shared scanner, schema extraction, and output toolkit for apcore framework adapters
5
5
  Project-URL: Homepage, https://aiperceivable.com
6
6
  Project-URL: Repository, https://github.com/aiperceivable/apcore-toolkit-python
@@ -8,9 +8,10 @@ Project-URL: Documentation, https://github.com/aiperceivable/apcore-toolkit-pyth
8
8
  Project-URL: Issues, https://github.com/aiperceivable/apcore-toolkit-python/issues
9
9
  Author-email: aiperceivable <tercel.yi@gmail.com>
10
10
  License-Expression: Apache-2.0
11
+ License-File: LICENSE
11
12
  Keywords: apcore,mcp,openapi,pydantic,scanner,schema,toolkit,yaml
12
13
  Requires-Python: >=3.11
13
- Requires-Dist: apcore>=0.18.0
14
+ Requires-Dist: apcore>=0.21.0
14
15
  Requires-Dist: pydantic>=2.0
15
16
  Requires-Dist: pyyaml>=6.0
16
17
  Provides-Extra: dev
@@ -18,10 +19,12 @@ Requires-Dist: apdev[dev]>=0.2.1; extra == 'dev'
18
19
  Requires-Dist: httpx>=0.24; extra == 'dev'
19
20
  Requires-Dist: mypy>=1.0; extra == 'dev'
20
21
  Requires-Dist: pytest-cov>=4.0; extra == 'dev'
21
- Requires-Dist: pytest>=7.0; extra == 'dev'
22
- Requires-Dist: ruff>=0.1; extra == 'dev'
22
+ Requires-Dist: pytest>=8.0; extra == 'dev'
23
+ Requires-Dist: ruff>=0.9; extra == 'dev'
23
24
  Provides-Extra: http-proxy
24
25
  Requires-Dist: httpx>=0.24; extra == 'http-proxy'
26
+ Provides-Extra: json-schema
27
+ Requires-Dist: jsonschema>=4.0; extra == 'json-schema'
25
28
  Description-Content-Type: text/markdown
26
29
 
27
30
  <div align="center">
@@ -48,6 +51,8 @@ pip install apcore-toolkit
48
51
  | `ScannedModule` | Canonical dataclass representing a scanned endpoint |
49
52
  | `BaseScanner` | Abstract base class for framework scanners with filtering and deduplication |
50
53
  | `YAMLWriter` | Generates `.binding.yaml` files for `apcore.BindingLoader` |
54
+ | `BindingLoader` | Parses `.binding.yaml` files back into `ScannedModule` objects (pure-data inverse of `YAMLWriter`, with loose/strict modes) |
55
+ | `BindingLoadError` | Exception raised when binding parsing fails; carries `file_path`, `module_id`, `missing_fields`, `reason` |
51
56
  | `PythonWriter` | Generates `@module`-decorated Python wrapper files |
52
57
  | `RegistryWriter` | Registers modules directly into an `apcore.Registry` |
53
58
  | `HTTPProxyRegistryWriter` | Registers HTTP proxy modules that forward requests to a running API |
@@ -246,12 +251,6 @@ def deploy(env: str, tag: str = "latest") -> dict:
246
251
 
247
252
  Input and output schemas are inferred from PEP 484 type annotations. Use `include` / `exclude` regex filters to control which module IDs are registered.
248
253
 
249
- ## Requirements
250
-
251
- - Python >= 3.11
252
- - apcore >= 0.14.0
253
- - pydantic >= 2.0
254
- - PyYAML >= 6.0
255
254
 
256
255
  ## Documentation
257
256
 
@@ -22,6 +22,8 @@ pip install apcore-toolkit
22
22
  | `ScannedModule` | Canonical dataclass representing a scanned endpoint |
23
23
  | `BaseScanner` | Abstract base class for framework scanners with filtering and deduplication |
24
24
  | `YAMLWriter` | Generates `.binding.yaml` files for `apcore.BindingLoader` |
25
+ | `BindingLoader` | Parses `.binding.yaml` files back into `ScannedModule` objects (pure-data inverse of `YAMLWriter`, with loose/strict modes) |
26
+ | `BindingLoadError` | Exception raised when binding parsing fails; carries `file_path`, `module_id`, `missing_fields`, `reason` |
25
27
  | `PythonWriter` | Generates `@module`-decorated Python wrapper files |
26
28
  | `RegistryWriter` | Registers modules directly into an `apcore.Registry` |
27
29
  | `HTTPProxyRegistryWriter` | Registers HTTP proxy modules that forward requests to a running API |
@@ -220,12 +222,6 @@ def deploy(env: str, tag: str = "latest") -> dict:
220
222
 
221
223
  Input and output schemas are inferred from PEP 484 type annotations. Use `include` / `exclude` regex filters to control which module IDs are registered.
222
224
 
223
- ## Requirements
224
-
225
- - Python >= 3.11
226
- - apcore >= 0.14.0
227
- - pydantic >= 2.0
228
- - PyYAML >= 6.0
229
225
 
230
226
  ## Documentation
231
227
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "apcore-toolkit"
7
- version = "0.4.2"
7
+ version = "0.6.0"
8
8
  description = "Shared scanner, schema extraction, and output toolkit for apcore framework adapters"
9
9
  requires-python = ">=3.11"
10
10
  readme = "README.md"
@@ -23,17 +23,18 @@ keywords = [
23
23
  "toolkit",
24
24
  ]
25
25
  dependencies = [
26
- "apcore>=0.18.0",
26
+ "apcore>=0.21.0",
27
27
  "pydantic>=2.0",
28
28
  "PyYAML>=6.0",
29
29
  ]
30
30
 
31
31
  [project.optional-dependencies]
32
32
  http-proxy = ["httpx>=0.24"]
33
+ json-schema = ["jsonschema>=4.0"]
33
34
  dev = [
34
- "pytest>=7.0",
35
+ "pytest>=8.0",
35
36
  "pytest-cov>=4.0",
36
- "ruff>=0.1",
37
+ "ruff>=0.9",
37
38
  "mypy>=1.0",
38
39
  "apdev[dev]>=0.2.1",
39
40
  "httpx>=0.24",
@@ -6,8 +6,14 @@ Public API re-exports for convenient access to core types and utilities.
6
6
  from importlib.metadata import PackageNotFoundError
7
7
  from importlib.metadata import version as _get_version
8
8
  from apcore_toolkit.ai_enhancer import AIEnhancer, Enhancer
9
+ from apcore_toolkit.binding_loader import BindingLoader, BindingLoadError
9
10
  from apcore_toolkit.display import DisplayResolver
10
- from apcore_toolkit.formatting import to_markdown
11
+ from apcore_toolkit.formatting import (
12
+ format_module,
13
+ format_modules,
14
+ format_schema,
15
+ to_markdown,
16
+ )
11
17
  from apcore_toolkit.openapi import (
12
18
  deep_resolve_refs,
13
19
  extract_input_schema,
@@ -16,7 +22,7 @@ from apcore_toolkit.openapi import (
16
22
  resolve_schema,
17
23
  )
18
24
  from apcore_toolkit.output import get_writer
19
- from apcore_toolkit.output.errors import WriteError
25
+ from apcore_toolkit.output.errors import InvalidFormatError, WriteError
20
26
  from apcore_toolkit.output.python_writer import PythonWriter
21
27
  from apcore_toolkit.output.http_proxy_writer import HTTPProxyRegistryWriter
22
28
  from apcore_toolkit.output.registry_writer import RegistryWriter
@@ -32,10 +38,23 @@ from apcore_toolkit.output.verifiers import (
32
38
  from apcore_toolkit.output.yaml_writer import YAMLWriter
33
39
  from apcore_toolkit.pydantic_utils import flatten_pydantic_params, resolve_target
34
40
  from apcore_toolkit.convention_scanner import ConventionScanner
35
- from apcore_toolkit.scanner import BaseScanner
41
+ from apcore_toolkit.http_verb_map import (
42
+ SCANNER_VERB_MAP,
43
+ extract_path_param_names,
44
+ generate_suggested_alias,
45
+ has_path_params,
46
+ resolve_http_verb,
47
+ substitute_path_params,
48
+ )
49
+ from apcore_toolkit.scanner import (
50
+ BaseScanner,
51
+ deduplicate_ids,
52
+ filter_modules,
53
+ infer_annotations_from_method,
54
+ )
36
55
  from apcore_toolkit.schema_utils import enrich_schema_descriptions
37
56
  from apcore_toolkit.serializers import annotations_to_dict, module_to_dict, modules_to_dicts
38
- from apcore_toolkit.types import ScannedModule
57
+ from apcore_toolkit.types import ScannedModule, clone_module, create_scanned_module
39
58
 
40
59
  try:
41
60
  __version__ = _get_version("apcore-toolkit")
@@ -44,16 +63,20 @@ except PackageNotFoundError:
44
63
 
45
64
  __all__ = [
46
65
  "AIEnhancer",
66
+ "BindingLoadError",
67
+ "BindingLoader",
47
68
  "DisplayResolver",
48
69
  "BaseScanner",
49
70
  "ConventionScanner",
50
71
  "HTTPProxyRegistryWriter",
51
72
  "Enhancer",
73
+ "InvalidFormatError",
52
74
  "JSONVerifier",
53
75
  "MagicBytesVerifier",
54
76
  "PythonWriter",
55
77
  "RegistryVerifier",
56
78
  "RegistryWriter",
79
+ "SCANNER_VERB_MAP",
57
80
  "ScannedModule",
58
81
  "SyntaxVerifier",
59
82
  "Verifier",
@@ -63,17 +86,30 @@ __all__ = [
63
86
  "YAMLVerifier",
64
87
  "YAMLWriter",
65
88
  "annotations_to_dict",
89
+ "clone_module",
90
+ "create_scanned_module",
91
+ "deduplicate_ids",
66
92
  "deep_resolve_refs",
67
93
  "enrich_schema_descriptions",
68
94
  "extract_input_schema",
69
95
  "extract_output_schema",
96
+ "extract_path_param_names",
97
+ "filter_modules",
70
98
  "flatten_pydantic_params",
99
+ "format_module",
100
+ "format_modules",
101
+ "format_schema",
102
+ "generate_suggested_alias",
71
103
  "get_writer",
104
+ "has_path_params",
105
+ "infer_annotations_from_method",
72
106
  "module_to_dict",
73
107
  "modules_to_dicts",
108
+ "resolve_http_verb",
74
109
  "resolve_ref",
75
110
  "resolve_schema",
76
111
  "resolve_target",
77
112
  "run_verifier_chain",
113
+ "substitute_path_params",
78
114
  "to_markdown",
79
115
  ]
@@ -0,0 +1,19 @@
1
+ """Shared Python ↔ JSON Schema type vocabulary.
2
+
3
+ Single source of truth for the 6-type mapping used by ConventionScanner
4
+ (Python→JSON Schema) and PythonWriter (JSON Schema→Python). Adding a new
5
+ type here propagates to both automatically.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ PYTHON_TO_JSON_SCHEMA: dict[str, str] = {
11
+ "str": "string",
12
+ "int": "integer",
13
+ "float": "number",
14
+ "bool": "boolean",
15
+ "list": "array",
16
+ "dict": "object",
17
+ }
18
+
19
+ JSON_SCHEMA_TO_PYTHON: dict[str, str] = {v: k for k, v in PYTHON_TO_JSON_SCHEMA.items()}