@voxgig/sdkgen 1.2.0 → 1.3.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.
- package/bin/voxgig-sdkgen +2 -2
- package/dist/action/action.d.ts +2 -1
- package/dist/action/action.js +15 -9
- package/dist/action/action.js.map +1 -1
- package/dist/action/feature.js +6 -3
- package/dist/action/feature.js.map +1 -1
- package/dist/action/target.d.ts +7 -1
- package/dist/action/target.js +13 -6
- package/dist/action/target.js.map +1 -1
- package/dist/cmp/Changelog.d.ts +2 -0
- package/dist/cmp/Changelog.js +30 -0
- package/dist/cmp/Changelog.js.map +1 -0
- package/dist/cmp/Deploy.d.ts +2 -0
- package/dist/cmp/Deploy.js +227 -0
- package/dist/cmp/Deploy.js.map +1 -0
- package/dist/cmp/FeatureHook.js +5 -6
- package/dist/cmp/FeatureHook.js.map +1 -1
- package/dist/cmp/License.d.ts +2 -0
- package/dist/cmp/License.js +44 -0
- package/dist/cmp/License.js.map +1 -0
- package/dist/cmp/Readme.js +2 -0
- package/dist/cmp/Readme.js.map +1 -1
- package/dist/cmp/ReadmeErrors.d.ts +2 -0
- package/dist/cmp/ReadmeErrors.js +210 -0
- package/dist/cmp/ReadmeErrors.js.map +1 -0
- package/dist/cmp/ReadmeExplanation.js +213 -223
- package/dist/cmp/ReadmeExplanation.js.map +1 -1
- package/dist/cmp/ReadmeModel.js +6 -1
- package/dist/cmp/ReadmeModel.js.map +1 -1
- package/dist/cmp/ReadmeTop.js +389 -82
- package/dist/cmp/ReadmeTop.js.map +1 -1
- package/dist/cmp/Security.d.ts +2 -0
- package/dist/cmp/Security.js +36 -0
- package/dist/cmp/Security.js.map +1 -0
- package/dist/helpers/canonType.d.ts +7 -0
- package/dist/helpers/canonType.js +74 -0
- package/dist/helpers/canonType.js.map +1 -0
- package/dist/helpers/collectDeps.d.ts +3 -2
- package/dist/helpers/collectDeps.js.map +1 -1
- package/dist/helpers/naming.d.ts +3 -0
- package/dist/helpers/naming.js +51 -0
- package/dist/helpers/naming.js.map +1 -0
- package/dist/helpers/opExample.d.ts +13 -0
- package/dist/helpers/opExample.js +149 -0
- package/dist/helpers/opExample.js.map +1 -0
- package/dist/helpers/opShape.d.ts +18 -0
- package/dist/helpers/opShape.js +193 -0
- package/dist/helpers/opShape.js.map +1 -0
- package/dist/helpers/packageMeta.d.ts +27 -0
- package/dist/helpers/packageMeta.js +224 -0
- package/dist/helpers/packageMeta.js.map +1 -0
- package/dist/sdkgen.d.ts +11 -1
- package/dist/sdkgen.js +57 -18
- package/dist/sdkgen.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +65 -2
- package/dist/types.js.map +1 -1
- package/dist/utility.js +20 -11
- package/dist/utility.js.map +1 -1
- package/model/sdkgen.aontu +137 -0
- package/package.json +13 -6
- package/project/.sdk/model/target/go-cli.jsonic +47 -0
- package/project/.sdk/model/target/go-mcp.jsonic +41 -0
- package/project/.sdk/model/target/go.jsonic +9 -3
- package/project/.sdk/model/target/js.jsonic +13 -0
- package/project/.sdk/model/target/lua.jsonic +13 -0
- package/project/.sdk/model/target/php.jsonic +13 -0
- package/project/.sdk/model/target/py.jsonic +13 -0
- package/project/.sdk/model/target/rb.jsonic +13 -0
- package/project/.sdk/model/target/ts.jsonic +13 -0
- package/project/.sdk/src/cmp/go/EntityOperation_go.ts +1 -0
- package/project/.sdk/src/cmp/go/EntityTypes_go.ts +197 -0
- package/project/.sdk/src/cmp/go/Entity_go.ts +2 -1
- package/project/.sdk/src/cmp/go/MainEntity_go.ts +6 -0
- package/project/.sdk/src/cmp/go/Main_go.ts +23 -3
- package/project/.sdk/src/cmp/go/Package_go.ts +1 -1
- package/project/.sdk/src/cmp/go/ReadmeEntity_go.ts +18 -6
- package/project/.sdk/src/cmp/go/ReadmeExamplesTest_go.ts +684 -0
- package/project/.sdk/src/cmp/go/ReadmeExplanation_go.ts +1 -1
- package/project/.sdk/src/cmp/go/ReadmeHowto_go.ts +48 -8
- package/project/.sdk/src/cmp/go/ReadmeInstall_go.ts +11 -6
- package/project/.sdk/src/cmp/go/ReadmeIntro_go.ts +40 -1
- package/project/.sdk/src/cmp/go/ReadmeModel_go.ts +63 -17
- package/project/.sdk/src/cmp/go/ReadmeOptions_go.ts +4 -3
- package/project/.sdk/src/cmp/go/ReadmeQuick_go.ts +124 -115
- package/project/.sdk/src/cmp/go/ReadmeRef_go.ts +28 -15
- package/project/.sdk/src/cmp/go/ReadmeTopQuick_go.ts +33 -9
- package/project/.sdk/src/cmp/go/ReadmeTopTest_go.ts +35 -5
- package/project/.sdk/src/cmp/go/TestEntity_go.ts +1 -1
- package/project/.sdk/src/cmp/go/Test_go.ts +5 -1
- package/project/.sdk/src/cmp/go/fragment/Entity.fragment.go +21 -0
- package/project/.sdk/src/cmp/go/fragment/EntityCreateOp.fragment.go +11 -0
- package/project/.sdk/src/cmp/go/fragment/EntityListOp.fragment.go +11 -0
- package/project/.sdk/src/cmp/go/fragment/EntityLoadOp.fragment.go +11 -0
- package/project/.sdk/src/cmp/go/fragment/EntityRemoveOp.fragment.go +11 -0
- package/project/.sdk/src/cmp/go/fragment/EntityUpdateOp.fragment.go +11 -0
- package/project/.sdk/src/cmp/go-cli/Main_go-cli.ts +195 -0
- package/project/.sdk/src/cmp/go-cli/fragment/main.fragment.go +106 -0
- package/project/.sdk/src/cmp/go-cli/fragment/words.fragment.go +132 -0
- package/project/.sdk/src/cmp/go-mcp/Main_go-mcp.ts +184 -0
- package/project/.sdk/src/cmp/go-mcp/fragment/main.fragment.go +68 -0
- package/project/.sdk/src/cmp/go-mcp/fragment/tools.fragment.go +114 -0
- package/project/.sdk/src/cmp/js/EntityTypes_js.ts +111 -0
- package/project/.sdk/src/cmp/js/MainEntity_js.ts +1 -0
- package/project/.sdk/src/cmp/js/Main_js.ts +3 -0
- package/project/.sdk/src/cmp/js/Package_js.ts +16 -7
- package/project/.sdk/src/cmp/js/ReadmeEntity_js.ts +13 -7
- package/project/.sdk/src/cmp/js/ReadmeExplanation_js.ts +2 -2
- package/project/.sdk/src/cmp/js/ReadmeHowto_js.ts +52 -11
- package/project/.sdk/src/cmp/js/ReadmeIntro_js.ts +39 -1
- package/project/.sdk/src/cmp/js/ReadmeModel_js.ts +50 -18
- package/project/.sdk/src/cmp/js/ReadmeOptions_js.ts +4 -3
- package/project/.sdk/src/cmp/js/ReadmeQuick_js.ts +45 -19
- package/project/.sdk/src/cmp/js/ReadmeRef_js.ts +24 -13
- package/project/.sdk/src/cmp/js/ReadmeTopHowto_js.ts +3 -0
- package/project/.sdk/src/cmp/js/ReadmeTopQuick_js.ts +28 -12
- package/project/.sdk/src/cmp/js/ReadmeTopTest_js.ts +23 -3
- package/project/.sdk/src/cmp/js/fragment/EntityCreateOp.fragment.js +5 -0
- package/project/.sdk/src/cmp/js/fragment/EntityListOp.fragment.js +5 -0
- package/project/.sdk/src/cmp/js/fragment/EntityLoadOp.fragment.js +5 -0
- package/project/.sdk/src/cmp/js/fragment/EntityRemoveOp.fragment.js +5 -0
- package/project/.sdk/src/cmp/js/fragment/EntityUpdateOp.fragment.js +5 -0
- package/project/.sdk/src/cmp/js/utility_js.ts +57 -0
- package/project/.sdk/src/cmp/lua/EntityTypes_lua.ts +113 -0
- package/project/.sdk/src/cmp/lua/MainEntity_lua.ts +8 -0
- package/project/.sdk/src/cmp/lua/Main_lua.ts +4 -0
- package/project/.sdk/src/cmp/lua/Package_lua.ts +23 -4
- package/project/.sdk/src/cmp/lua/ReadmeEntity_lua.ts +12 -10
- package/project/.sdk/src/cmp/lua/ReadmeExamplesTest_lua.ts +293 -0
- package/project/.sdk/src/cmp/lua/ReadmeHowto_lua.ts +46 -8
- package/project/.sdk/src/cmp/lua/ReadmeInstall_lua.ts +19 -3
- package/project/.sdk/src/cmp/lua/ReadmeIntro_lua.ts +38 -1
- package/project/.sdk/src/cmp/lua/ReadmeModel_lua.ts +51 -16
- package/project/.sdk/src/cmp/lua/ReadmeOptions_lua.ts +4 -3
- package/project/.sdk/src/cmp/lua/ReadmeQuick_lua.ts +79 -18
- package/project/.sdk/src/cmp/lua/ReadmeRef_lua.ts +25 -20
- package/project/.sdk/src/cmp/lua/ReadmeTopQuick_lua.ts +17 -9
- package/project/.sdk/src/cmp/lua/ReadmeTopTest_lua.ts +32 -5
- package/project/.sdk/src/cmp/lua/Test_lua.ts +4 -0
- package/project/.sdk/src/cmp/lua/fragment/EntityCreateOp.fragment.lua +4 -0
- package/project/.sdk/src/cmp/lua/fragment/EntityListOp.fragment.lua +4 -0
- package/project/.sdk/src/cmp/lua/fragment/EntityLoadOp.fragment.lua +4 -0
- package/project/.sdk/src/cmp/lua/fragment/EntityRemoveOp.fragment.lua +4 -0
- package/project/.sdk/src/cmp/lua/fragment/EntityUpdateOp.fragment.lua +4 -0
- package/project/.sdk/src/cmp/php/EntityTypes_php.ts +142 -0
- package/project/.sdk/src/cmp/php/MainEntity_php.ts +18 -1
- package/project/.sdk/src/cmp/php/Main_php.ts +4 -0
- package/project/.sdk/src/cmp/php/Package_php.ts +18 -7
- package/project/.sdk/src/cmp/php/ReadmeEntity_php.ts +15 -11
- package/project/.sdk/src/cmp/php/ReadmeExamplesTest_php.ts +401 -0
- package/project/.sdk/src/cmp/php/ReadmeHowto_php.ts +56 -13
- package/project/.sdk/src/cmp/php/ReadmeInstall_php.ts +15 -3
- package/project/.sdk/src/cmp/php/ReadmeIntro_php.ts +40 -1
- package/project/.sdk/src/cmp/php/ReadmeModel_php.ts +26 -8
- package/project/.sdk/src/cmp/php/ReadmeOptions_php.ts +4 -3
- package/project/.sdk/src/cmp/php/ReadmeQuick_php.ts +80 -22
- package/project/.sdk/src/cmp/php/ReadmeRef_php.ts +51 -42
- package/project/.sdk/src/cmp/php/ReadmeTopHowto_php.ts +1 -1
- package/project/.sdk/src/cmp/php/ReadmeTopQuick_php.ts +19 -11
- package/project/.sdk/src/cmp/php/ReadmeTopTest_php.ts +41 -11
- package/project/.sdk/src/cmp/php/TestDirect_php.ts +9 -9
- package/project/.sdk/src/cmp/php/TestEntity_php.ts +6 -12
- package/project/.sdk/src/cmp/php/Test_php.ts +4 -0
- package/project/.sdk/src/cmp/php/fragment/Entity.fragment.php +13 -1
- package/project/.sdk/src/cmp/php/fragment/EntityCreateOp.fragment.php +10 -1
- package/project/.sdk/src/cmp/php/fragment/EntityListOp.fragment.php +10 -1
- package/project/.sdk/src/cmp/php/fragment/EntityLoadOp.fragment.php +10 -1
- package/project/.sdk/src/cmp/php/fragment/EntityRemoveOp.fragment.php +10 -1
- package/project/.sdk/src/cmp/php/fragment/EntityUpdateOp.fragment.php +10 -1
- package/project/.sdk/src/cmp/php/fragment/Main.fragment.php +22 -14
- package/project/.sdk/src/cmp/py/EntityTypes_py.ts +184 -0
- package/project/.sdk/src/cmp/py/Entity_py.ts +27 -0
- package/project/.sdk/src/cmp/py/MainEntity_py.ts +2 -1
- package/project/.sdk/src/cmp/py/Main_py.ts +35 -0
- package/project/.sdk/src/cmp/py/Package_py.ts +24 -3
- package/project/.sdk/src/cmp/py/ReadmeEntity_py.ts +27 -11
- package/project/.sdk/src/cmp/py/ReadmeExamplesTest_py.ts +626 -0
- package/project/.sdk/src/cmp/py/ReadmeHowto_py.ts +53 -14
- package/project/.sdk/src/cmp/py/ReadmeInstall_py.ts +19 -3
- package/project/.sdk/src/cmp/py/ReadmeIntro_py.ts +48 -1
- package/project/.sdk/src/cmp/py/ReadmeModel_py.ts +28 -10
- package/project/.sdk/src/cmp/py/ReadmeOptions_py.ts +4 -3
- package/project/.sdk/src/cmp/py/ReadmeQuick_py.ts +73 -24
- package/project/.sdk/src/cmp/py/ReadmeRef_py.ts +57 -37
- package/project/.sdk/src/cmp/py/ReadmeTopHowto_py.ts +1 -1
- package/project/.sdk/src/cmp/py/ReadmeTopQuick_py.ts +19 -11
- package/project/.sdk/src/cmp/py/ReadmeTopTest_py.ts +30 -5
- package/project/.sdk/src/cmp/py/TestDirect_py.ts +7 -9
- package/project/.sdk/src/cmp/py/TestEntity_py.ts +6 -14
- package/project/.sdk/src/cmp/py/Test_py.ts +4 -0
- package/project/.sdk/src/cmp/py/fragment/Entity.fragment.py +5 -2
- package/project/.sdk/src/cmp/py/fragment/EntityCreateOp.fragment.py +1 -1
- package/project/.sdk/src/cmp/py/fragment/EntityListOp.fragment.py +5 -1
- package/project/.sdk/src/cmp/py/fragment/EntityLoadOp.fragment.py +6 -1
- package/project/.sdk/src/cmp/py/fragment/EntityRemoveOp.fragment.py +6 -1
- package/project/.sdk/src/cmp/py/fragment/EntityUpdateOp.fragment.py +1 -1
- package/project/.sdk/src/cmp/py/fragment/Main.fragment.py +17 -10
- package/project/.sdk/src/cmp/rb/EntityTypes_rb.ts +136 -0
- package/project/.sdk/src/cmp/rb/MainEntity_rb.ts +1 -0
- package/project/.sdk/src/cmp/rb/Main_rb.ts +4 -0
- package/project/.sdk/src/cmp/rb/Package_rb.ts +34 -7
- package/project/.sdk/src/cmp/rb/ReadmeEntity_rb.ts +26 -10
- package/project/.sdk/src/cmp/rb/ReadmeExamplesTest_rb.ts +289 -0
- package/project/.sdk/src/cmp/rb/ReadmeHowto_rb.ts +72 -21
- package/project/.sdk/src/cmp/rb/ReadmeInstall_rb.ts +16 -4
- package/project/.sdk/src/cmp/rb/ReadmeIntro_rb.ts +40 -1
- package/project/.sdk/src/cmp/rb/ReadmeModel_rb.ts +29 -12
- package/project/.sdk/src/cmp/rb/ReadmeOptions_rb.ts +4 -3
- package/project/.sdk/src/cmp/rb/ReadmeQuick_rb.ts +85 -22
- package/project/.sdk/src/cmp/rb/ReadmeRef_rb.ts +56 -37
- package/project/.sdk/src/cmp/rb/ReadmeTopHowto_rb.ts +1 -1
- package/project/.sdk/src/cmp/rb/ReadmeTopQuick_rb.ts +19 -11
- package/project/.sdk/src/cmp/rb/ReadmeTopTest_rb.ts +43 -11
- package/project/.sdk/src/cmp/rb/TestDirect_rb.ts +9 -9
- package/project/.sdk/src/cmp/rb/TestEntity_rb.ts +6 -12
- package/project/.sdk/src/cmp/rb/Test_rb.ts +4 -0
- package/project/.sdk/src/cmp/rb/fragment/Entity.fragment.rb +2 -0
- package/project/.sdk/src/cmp/rb/fragment/EntityCreateOp.fragment.rb +5 -0
- package/project/.sdk/src/cmp/rb/fragment/EntityListOp.fragment.rb +20 -2
- package/project/.sdk/src/cmp/rb/fragment/EntityLoadOp.fragment.rb +8 -1
- package/project/.sdk/src/cmp/rb/fragment/EntityRemoveOp.fragment.rb +6 -1
- package/project/.sdk/src/cmp/rb/fragment/EntityUpdateOp.fragment.rb +5 -0
- package/project/.sdk/src/cmp/rb/fragment/Main.fragment.rb +24 -8
- package/project/.sdk/src/cmp/ts/EntityTypes_ts.ts +105 -0
- package/project/.sdk/src/cmp/ts/Entity_ts.ts +17 -1
- package/project/.sdk/src/cmp/ts/MainEntity_ts.ts +1 -0
- package/project/.sdk/src/cmp/ts/Main_ts.ts +6 -0
- package/project/.sdk/src/cmp/ts/Package_ts.ts +14 -7
- package/project/.sdk/src/cmp/ts/ReadmeEntity_ts.ts +13 -7
- package/project/.sdk/src/cmp/ts/ReadmeExampleTest_ts.ts +81 -0
- package/project/.sdk/src/cmp/ts/ReadmeExamplesTest_ts.ts +446 -0
- package/project/.sdk/src/cmp/ts/ReadmeExplanation_ts.ts +2 -2
- package/project/.sdk/src/cmp/ts/ReadmeHowto_ts.ts +57 -11
- package/project/.sdk/src/cmp/ts/ReadmeInstall_ts.ts +19 -6
- package/project/.sdk/src/cmp/ts/ReadmeIntro_ts.ts +39 -1
- package/project/.sdk/src/cmp/ts/ReadmeModel_ts.ts +50 -18
- package/project/.sdk/src/cmp/ts/ReadmeOptions_ts.ts +4 -3
- package/project/.sdk/src/cmp/ts/ReadmeQuick_ts.ts +95 -40
- package/project/.sdk/src/cmp/ts/ReadmeRef_ts.ts +24 -13
- package/project/.sdk/src/cmp/ts/ReadmeTopHowto_ts.ts +3 -0
- package/project/.sdk/src/cmp/ts/ReadmeTopQuick_ts.ts +44 -12
- package/project/.sdk/src/cmp/ts/ReadmeTopTest_ts.ts +23 -3
- package/project/.sdk/src/cmp/ts/TestEntity_ts.ts +2 -2
- package/project/.sdk/src/cmp/ts/Test_ts.ts +5 -0
- package/project/.sdk/src/cmp/ts/fragment/Entity.fragment.ts +3 -1
- package/project/.sdk/src/cmp/ts/fragment/EntityBase.fragment.ts +10 -5
- package/project/.sdk/src/cmp/ts/fragment/EntityCreateOp.fragment.ts +4 -2
- package/project/.sdk/src/cmp/ts/fragment/EntityListOp.fragment.ts +4 -2
- package/project/.sdk/src/cmp/ts/fragment/EntityLoadOp.fragment.ts +4 -2
- package/project/.sdk/src/cmp/ts/fragment/EntityRemoveOp.fragment.ts +4 -2
- package/project/.sdk/src/cmp/ts/fragment/EntityUpdateOp.fragment.ts +4 -2
- package/project/.sdk/src/cmp/ts/utility_ts.ts +59 -0
- package/project/.sdk/tm/go/LICENSE +1 -1
- package/project/.sdk/tm/go/Makefile +36 -1
- package/project/.sdk/tm/go/VERSION +1 -0
- package/project/.sdk/tm/go/core/context.go +12 -7
- package/project/.sdk/tm/go-cli/src/feature/README.md +6 -0
- package/project/.sdk/tm/go-cli/src/feature/base/.gitkeep +0 -0
- package/project/.sdk/tm/go-cli/src/feature/log/.gitkeep +0 -0
- package/project/.sdk/tm/go-cli/src/feature/test/.gitkeep +0 -0
- package/project/.sdk/tm/go-mcp/src/feature/README.md +6 -0
- package/project/.sdk/tm/go-mcp/src/feature/base/.gitkeep +0 -0
- package/project/.sdk/tm/go-mcp/src/feature/log/.gitkeep +0 -0
- package/project/.sdk/tm/go-mcp/src/feature/test/.gitkeep +0 -0
- package/project/.sdk/tm/js/LICENSE +1 -1
- package/project/.sdk/tm/js/Makefile +41 -0
- package/project/.sdk/tm/lua/LICENSE +1 -1
- package/project/.sdk/tm/lua/Makefile +40 -1
- package/project/.sdk/tm/lua/core/context.lua +13 -8
- package/project/.sdk/tm/php/LICENSE +1 -1
- package/project/.sdk/tm/php/Makefile +31 -1
- package/project/.sdk/tm/php/VERSION +1 -0
- package/project/.sdk/tm/php/core/Context.php +12 -6
- package/project/.sdk/tm/php/test/PrimaryUtilityTest.php +13 -4
- package/project/.sdk/tm/php/utility/Done.php +2 -2
- package/project/.sdk/tm/php/utility/MakeError.php +3 -3
- package/project/.sdk/tm/py/LICENSE +1 -1
- package/project/.sdk/tm/py/Makefile +34 -1
- package/project/.sdk/tm/py/core/context.py +12 -7
- package/project/.sdk/tm/py/test/test_primary_utility.py +15 -4
- package/project/.sdk/tm/py/utility/done.py +3 -1
- package/project/.sdk/tm/py/utility/make_error.py +2 -2
- package/project/.sdk/tm/rb/LICENSE +1 -1
- package/project/.sdk/tm/rb/Makefile +33 -1
- package/project/.sdk/tm/rb/core/context.rb +8 -3
- package/project/.sdk/tm/rb/test/primary_utility_test.rb +2 -2
- package/project/.sdk/tm/rb/utility/done.rb +3 -1
- package/project/.sdk/tm/rb/utility/make_error.rb +5 -2
- package/project/.sdk/tm/ts/LICENSE +1 -1
- package/project/.sdk/tm/ts/Makefile +42 -0
- package/project/.sdk/tm/ts/src/Context.ts +8 -3
- package/src/action/action.ts +17 -10
- package/src/action/feature.ts +8 -6
- package/src/action/target.ts +17 -8
- package/src/cmp/Changelog.ts +34 -0
- package/src/cmp/Deploy.ts +258 -0
- package/src/cmp/FeatureHook.ts +6 -8
- package/src/cmp/License.ts +49 -0
- package/src/cmp/Readme.ts +2 -0
- package/src/cmp/ReadmeErrors.ts +257 -0
- package/src/cmp/ReadmeExplanation.ts +250 -230
- package/src/cmp/ReadmeModel.ts +7 -2
- package/src/cmp/ReadmeTop.ts +411 -95
- package/src/cmp/Security.ts +41 -0
- package/src/helpers/canonType.ts +89 -0
- package/src/helpers/collectDeps.ts +5 -3
- package/src/helpers/naming.ts +59 -0
- package/src/helpers/opExample.ts +170 -0
- package/src/helpers/opShape.ts +236 -0
- package/src/helpers/packageMeta.ts +236 -0
- package/src/sdkgen.ts +87 -20
- package/src/tsconfig.json +1 -0
- package/src/types.ts +85 -2
- package/src/utility.ts +22 -11
- package/README.md +0 -2
- package/dist/action/lang.d.ts +0 -2
- package/dist/action/lang.js +0 -68
- package/dist/action/lang.js.map +0 -1
- package/dist/cmp/Hook.d.ts +0 -2
- package/dist/cmp/Hook.js +0 -10
- package/dist/cmp/Hook.js.map +0 -1
- package/dist/cmp/Top.d.ts +0 -2
- package/dist/cmp/Top.js +0 -23
- package/dist/cmp/Top.js.map +0 -1
- package/dist/prepare-openapi.d.ts +0 -2
- package/dist/prepare-openapi.js +0 -37
- package/dist/prepare-openapi.js.map +0 -1
- package/model/sdkgen.jsonic +0 -74
- package/project/.sdk/tm/go/utility/struct/go.mod +0 -3
|
@@ -0,0 +1,626 @@
|
|
|
1
|
+
|
|
2
|
+
import { cmp, each, File, Content } from '@voxgig/sdkgen'
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
KIT,
|
|
6
|
+
getModelPath,
|
|
7
|
+
nom,
|
|
8
|
+
} from '@voxgig/apidef'
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
// Emits py/test/test_readme_examples.py — a pytest COMPLETENESS GATE over
|
|
12
|
+
// every ```python fenced block in the repository ROOT README.md, in the
|
|
13
|
+
// per-language py/README.md, AND in the per-language py/REFERENCE.md.
|
|
14
|
+
//
|
|
15
|
+
// The gate GUARANTEES every documented python example is unit-tested. For each
|
|
16
|
+
// of the three docs it:
|
|
17
|
+
// 1. extracts every ```python block (tagged by source doc + index);
|
|
18
|
+
// 2. ast.parse + compile()s EVERY block -> SYNTAX gate;
|
|
19
|
+
// 3. EXECUTEs every RUNNABLE block (one that constructs the SDK, or drives a
|
|
20
|
+
// `client`/`sdk` the narrative built earlier) in a seeded, offline
|
|
21
|
+
// TEST-mode subprocess. A PROGRAMMING error
|
|
22
|
+
// (Name/Attribute/Type/Key/Index/Import/Syntax) FAILS; ONLY a
|
|
23
|
+
// not-found/404 domain error is tolerated. Any other error also FAILS;
|
|
24
|
+
// 4. asserts COMPLETENESS: it partitions every block into exactly one of
|
|
25
|
+
// {executed, compiled-nonrunnable, illustration} and asserts
|
|
26
|
+
// total == executed + compiled + illustration. "illustration" is a NARROW
|
|
27
|
+
// explicit class (a non-runnable block that is only imports / signature
|
|
28
|
+
// stubs / bare references / literal assignments — a pure signature/table
|
|
29
|
+
// snippet) and can NEVER absorb a runnable example. A block that looks
|
|
30
|
+
// runnable (references the SDK/client) but was not executed FAILS the
|
|
31
|
+
// test; a block that fails to compile FAILS the test. A per-doc summary
|
|
32
|
+
// (total/executed/compiled/illustration) is printed.
|
|
33
|
+
//
|
|
34
|
+
// If mypy is importable it also type-checks the concatenated ROOT README blocks
|
|
35
|
+
// (the SDK ships py.typed + TypedDicts) — a bonus TYPE gate over the primary
|
|
36
|
+
// EXECUTE gate.
|
|
37
|
+
//
|
|
38
|
+
// The emitted Python is written WITHOUT backticks or backslashes (chr(96) is
|
|
39
|
+
// the fence marker, chr(10) is newline) so this TS template literal stays
|
|
40
|
+
// clean — the only interpolations are the SDK module/class names and the
|
|
41
|
+
// generated entity map.
|
|
42
|
+
const ReadmeExamplesTest = cmp(function ReadmeExamplesTest(props: any) {
|
|
43
|
+
const { target, ctx$: { model } } = props
|
|
44
|
+
|
|
45
|
+
const Name = model.const.Name
|
|
46
|
+
const sdkModule = Name.toLowerCase() + '_sdk'
|
|
47
|
+
const sdkClass = Name + 'SDK'
|
|
48
|
+
|
|
49
|
+
// The API's capitalised semantic entities, mapped to their lowercase
|
|
50
|
+
// fixture key: { "Fact": "fact", "User": "user" }. Used at runtime to seed
|
|
51
|
+
// a mock record for every entity a README block references.
|
|
52
|
+
const entities = each(getModelPath(model, `main.${KIT}.entity`))
|
|
53
|
+
.filter((e: any) => e.active !== false)
|
|
54
|
+
const entitiesLiteral = 0 === entities.length
|
|
55
|
+
? '{}'
|
|
56
|
+
: '{\n' + entities
|
|
57
|
+
.map((e: any) => ` "${nom(e, 'Name')}": "${e.name}",`)
|
|
58
|
+
.join('\n') + '\n}'
|
|
59
|
+
|
|
60
|
+
File({ name: 'test_readme_examples.' + target.ext }, () => {
|
|
61
|
+
Content(`# ${Name} SDK — documentation python-examples COMPLETENESS gate.
|
|
62
|
+
#
|
|
63
|
+
# GUARANTEE: every python example in the docs is unit-tested. This module is a
|
|
64
|
+
# completeness gate over every python fenced code block in three documents:
|
|
65
|
+
# - the repository ROOT README.md (one directory above the py/ package),
|
|
66
|
+
# - the per-language py/README.md (tutorial, how-to, error-handling,
|
|
67
|
+
# testing and entity-op examples — in the package root),
|
|
68
|
+
# - the per-language py/REFERENCE.md (in the package root).
|
|
69
|
+
# It exists to keep the documented examples honest as the generator evolves:
|
|
70
|
+
# no runnable example may be silently skipped, and no block may be dropped.
|
|
71
|
+
#
|
|
72
|
+
# Checks, in order, PER DOCUMENT:
|
|
73
|
+
#
|
|
74
|
+
# 1. COMPILE: every python block is parsed with ast.parse + compile(). This
|
|
75
|
+
# catches syntax errors in the docs (e.g. a dict key with no value, or a
|
|
76
|
+
# hyphenated import module name that is not valid Python).
|
|
77
|
+
#
|
|
78
|
+
# 2. EXECUTE (the primary safety net): every RUNNABLE block is run offline in
|
|
79
|
+
# a seeded TEST-mode subprocess. A block is RUNNABLE when it constructs the
|
|
80
|
+
# SDK (mentions ${sdkClass}) OR drives a client/sdk variable the narrative
|
|
81
|
+
# built earlier ("client." / "sdk."). A constructing block is rewritten so
|
|
82
|
+
# both ${sdkClass}(...) and ${sdkClass}.test(...) become
|
|
83
|
+
# ${sdkClass}.test({"entity": {...}}) seeding one mock record (id "test01")
|
|
84
|
+
# per referenced entity; a client-driving block gets that seeded test
|
|
85
|
+
# client injected first, then runs verbatim. Any PROGRAMMING error
|
|
86
|
+
# (NameError / AttributeError / TypeError / KeyError / IndexError /
|
|
87
|
+
# ImportError / SyntaxError / IndentationError) FAILS the test. ONLY a
|
|
88
|
+
# not-found / 404 domain error is tolerated (it proves the snippet is
|
|
89
|
+
# structurally valid Python that drives the SDK against an unseeded id).
|
|
90
|
+
# Any other runtime error also FAILS. This surfaces real bugs — a snippet
|
|
91
|
+
# calling a method that does not exist raises AttributeError; indexing
|
|
92
|
+
# result["err"] on a direct() envelope with no err key raises KeyError.
|
|
93
|
+
#
|
|
94
|
+
# 3. COMPLETENESS: every block is partitioned into exactly one of
|
|
95
|
+
# {executed, compiled-nonrunnable, illustration} and the test asserts
|
|
96
|
+
# total == executed + compiled + illustration. "illustration" is a NARROW
|
|
97
|
+
# explicit class — a non-runnable block whose every top-level statement is
|
|
98
|
+
# an import, a signature stub, a bare reference, or a literal assignment (a
|
|
99
|
+
# pure signature/table snippet). It can never absorb a runnable example: a
|
|
100
|
+
# block that references the SDK/client is classified RUNNABLE first and
|
|
101
|
+
# MUST execute. If a runnable block is not executed, the test FAILS. A
|
|
102
|
+
# per-doc summary line (total/executed/compiled/illustration) is printed.
|
|
103
|
+
#
|
|
104
|
+
# 4. TYPECHECK (only if mypy is importable): the ROOT README blocks are
|
|
105
|
+
# concatenated and type-checked with mypy as a bonus gate over the primary
|
|
106
|
+
# EXECUTE gate. Only errors mypy attributes to the snippet file fail;
|
|
107
|
+
# import-resolution noise is inconclusive.
|
|
108
|
+
|
|
109
|
+
import ast
|
|
110
|
+
import os
|
|
111
|
+
import subprocess
|
|
112
|
+
import sys
|
|
113
|
+
import tempfile
|
|
114
|
+
|
|
115
|
+
import pytest
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
_TEST_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
119
|
+
_PY_ROOT = os.path.dirname(_TEST_DIR) # the py/ package root
|
|
120
|
+
_README = os.path.abspath(os.path.join(_PY_ROOT, "..", "README.md")) # repo root
|
|
121
|
+
_REFERENCE = os.path.join(_PY_ROOT, "REFERENCE.md") # per-language reference
|
|
122
|
+
_LOCAL_README = os.path.join(_PY_ROOT, "README.md") # per-language README
|
|
123
|
+
|
|
124
|
+
_FENCE = chr(96) * 3 # the triple-backtick markdown code fence
|
|
125
|
+
_NL = chr(10) # newline
|
|
126
|
+
_WS = (chr(32), chr(9), chr(13), chr(10)) # space, tab, CR, LF
|
|
127
|
+
|
|
128
|
+
_SDK_MODULE = "${sdkModule}"
|
|
129
|
+
_SDK_CLASS = "${sdkClass}"
|
|
130
|
+
|
|
131
|
+
# The variable names the generated narrative examples bind the client to. A doc
|
|
132
|
+
# reads as a sequence: an early snippet builds the client, later snippets drive
|
|
133
|
+
# it. A block that uses "client." or "sdk." without building one gets a seeded
|
|
134
|
+
# test client injected under those names so it runs standalone.
|
|
135
|
+
_CLIENT_VARS = ("client", "sdk")
|
|
136
|
+
|
|
137
|
+
# The API's capitalised semantic entities -> lowercase fixture key.
|
|
138
|
+
_ENTITIES = ${entitiesLiteral}
|
|
139
|
+
|
|
140
|
+
# The three documents held to the gate, tagged by human label.
|
|
141
|
+
_DOCS = (
|
|
142
|
+
("root README", _README),
|
|
143
|
+
("py README.md", _LOCAL_README),
|
|
144
|
+
("py REFERENCE.md", _REFERENCE),
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _read_doc(path, label):
|
|
149
|
+
if not os.path.exists(path):
|
|
150
|
+
pytest.skip(label + " not found: " + path)
|
|
151
|
+
with open(path, "r", encoding="utf-8") as fh:
|
|
152
|
+
return fh.read()
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _blocks_in(text):
|
|
156
|
+
# Split on the code fence: odd-indexed segments are the inside of a fenced
|
|
157
|
+
# block (an info string on the first line, then the code). No regex, no
|
|
158
|
+
# backslashes — keeps this robust and generator-friendly. Only fences whose
|
|
159
|
+
# info string is exactly "python" are returned, so signature/markdown
|
|
160
|
+
# tables (plain text or other fences) are skipped.
|
|
161
|
+
parts = text.split(_FENCE)
|
|
162
|
+
blocks = []
|
|
163
|
+
for i in range(1, len(parts), 2):
|
|
164
|
+
seg = parts[i]
|
|
165
|
+
lines = seg.split(_NL)
|
|
166
|
+
info = lines[0].strip()
|
|
167
|
+
if info == "python":
|
|
168
|
+
blocks.append(_NL.join(lines[1:]))
|
|
169
|
+
return blocks
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _python_blocks():
|
|
173
|
+
return _blocks_in(_read_doc(_README, "root README"))
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _reference_blocks():
|
|
177
|
+
return _blocks_in(_read_doc(_REFERENCE, "py REFERENCE.md"))
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def _local_readme_blocks():
|
|
181
|
+
return _blocks_in(_read_doc(_LOCAL_README, "py README.md"))
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# ----------------------------------------------------------------------------
|
|
185
|
+
# Presence + COMPILE (syntax) gate
|
|
186
|
+
# ----------------------------------------------------------------------------
|
|
187
|
+
|
|
188
|
+
def test_readme_has_python_blocks():
|
|
189
|
+
assert len(_python_blocks()) > 0, "expected at least one python block in the root README"
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def test_reference_has_python_blocks():
|
|
193
|
+
assert len(_reference_blocks()) > 0, "expected at least one python block in py/REFERENCE.md"
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def test_local_readme_has_python_blocks():
|
|
197
|
+
assert len(_local_readme_blocks()) > 0, "expected at least one python block in py/README.md"
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _assert_blocks_compile(blocks, label):
|
|
201
|
+
# Syntax gate: every documented python block must parse and compile. Catches
|
|
202
|
+
# a bad constructor import such as a hyphenated module name
|
|
203
|
+
# ("from my-slug_sdk import ...") which is a Python SyntaxError.
|
|
204
|
+
for i, block in enumerate(blocks):
|
|
205
|
+
try:
|
|
206
|
+
ast.parse(block)
|
|
207
|
+
compile(block, "<" + label + "-block-" + str(i) + ">", "exec")
|
|
208
|
+
except SyntaxError as err:
|
|
209
|
+
pytest.fail(
|
|
210
|
+
label + " python block #" + str(i)
|
|
211
|
+
+ " is not valid Python: " + str(err) + _NL + _NL + block
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def test_readme_python_blocks_compile():
|
|
216
|
+
_assert_blocks_compile(_python_blocks(), "root README")
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def test_reference_python_blocks_compile():
|
|
220
|
+
_assert_blocks_compile(_reference_blocks(), "py REFERENCE.md")
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def test_local_readme_python_blocks_compile():
|
|
224
|
+
_assert_blocks_compile(_local_readme_blocks(), "py README.md")
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
# ----------------------------------------------------------------------------
|
|
228
|
+
# TYPECHECK (bonus, root README only, if mypy is importable)
|
|
229
|
+
# ----------------------------------------------------------------------------
|
|
230
|
+
|
|
231
|
+
def _mypy_available():
|
|
232
|
+
try:
|
|
233
|
+
import mypy # noqa: F401
|
|
234
|
+
return True
|
|
235
|
+
except Exception:
|
|
236
|
+
return False
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def test_readme_python_blocks_typecheck():
|
|
240
|
+
# Type gate: concatenate the blocks and run mypy over them. Only fails on
|
|
241
|
+
# errors mypy attributes to our snippet file; environmental import noise is
|
|
242
|
+
# inconclusive (skipped). When mypy is unavailable the execute gate below
|
|
243
|
+
# is the safety net.
|
|
244
|
+
if not _mypy_available():
|
|
245
|
+
pytest.skip("mypy not importable — covered by the execute gate")
|
|
246
|
+
|
|
247
|
+
source = (_NL + _NL).join(_python_blocks()) + _NL
|
|
248
|
+
|
|
249
|
+
with tempfile.TemporaryDirectory() as td:
|
|
250
|
+
snippet_path = os.path.join(td, "readme_snippets.py")
|
|
251
|
+
with open(snippet_path, "w", encoding="utf-8") as fh:
|
|
252
|
+
fh.write(source)
|
|
253
|
+
|
|
254
|
+
env = dict(os.environ)
|
|
255
|
+
env["PYTHONDONTWRITEBYTECODE"] = "1"
|
|
256
|
+
# Resolve "from _SDK_MODULE import _SDK_CLASS" against the package root.
|
|
257
|
+
env["MYPYPATH"] = _PY_ROOT + os.pathsep + env.get("MYPYPATH", "")
|
|
258
|
+
|
|
259
|
+
proc = subprocess.run(
|
|
260
|
+
[
|
|
261
|
+
sys.executable, "-m", "mypy",
|
|
262
|
+
"--no-error-summary",
|
|
263
|
+
"--no-color-output",
|
|
264
|
+
"--follow-imports=silent", # use SDK types, silence SDK-internal errors
|
|
265
|
+
"--ignore-missing-imports",
|
|
266
|
+
snippet_path,
|
|
267
|
+
],
|
|
268
|
+
cwd=_PY_ROOT,
|
|
269
|
+
env=env,
|
|
270
|
+
capture_output=True,
|
|
271
|
+
text=True,
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
base = os.path.basename(snippet_path)
|
|
275
|
+
output = (proc.stdout or "") + (proc.stderr or "")
|
|
276
|
+
errors = []
|
|
277
|
+
for line in output.split(_NL):
|
|
278
|
+
if base in line and ": error:" in line:
|
|
279
|
+
if "Cannot find implementation or library stub" in line:
|
|
280
|
+
continue
|
|
281
|
+
if "Skipping analyzing" in line:
|
|
282
|
+
continue
|
|
283
|
+
errors.append(line)
|
|
284
|
+
|
|
285
|
+
if errors:
|
|
286
|
+
pytest.fail(
|
|
287
|
+
"mypy found type errors in the root README python snippets:"
|
|
288
|
+
+ _NL + _NL.join(errors) + _NL + _NL + "--- snippets ---" + _NL + source
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
# ----------------------------------------------------------------------------
|
|
293
|
+
# Classification: runnable / illustration / compiled-nonrunnable
|
|
294
|
+
# ----------------------------------------------------------------------------
|
|
295
|
+
|
|
296
|
+
def _uses_var(block, var):
|
|
297
|
+
# True if the block reads an attribute off the whole-word variable "var"
|
|
298
|
+
# (i.e. contains "var." with a non-identifier char, or nothing, before it).
|
|
299
|
+
# This distinguishes a client-driving "sdk." from the "_sdk." tail of an
|
|
300
|
+
# import module name.
|
|
301
|
+
needle = var + "."
|
|
302
|
+
start = 0
|
|
303
|
+
while True:
|
|
304
|
+
j = block.find(needle, start)
|
|
305
|
+
if j < 0:
|
|
306
|
+
return False
|
|
307
|
+
ok_before = True
|
|
308
|
+
if j > 0:
|
|
309
|
+
ch = block[j - 1]
|
|
310
|
+
if ch.isalnum() or ch == "_":
|
|
311
|
+
ok_before = False
|
|
312
|
+
if ok_before:
|
|
313
|
+
return True
|
|
314
|
+
start = j + 1
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def _is_runnable(block):
|
|
318
|
+
# A block is RUNNABLE — and therefore MUST be executed — when it constructs
|
|
319
|
+
# the SDK (mentions the class) or drives a client/sdk the narrative built.
|
|
320
|
+
if _SDK_CLASS in block:
|
|
321
|
+
return True
|
|
322
|
+
for var in _CLIENT_VARS:
|
|
323
|
+
if _uses_var(block, var):
|
|
324
|
+
return True
|
|
325
|
+
return False
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def _is_literalish(value):
|
|
329
|
+
# A right-hand side that performs no function call (a literal, name, or
|
|
330
|
+
# collection display). Keeps the "illustration" class narrow.
|
|
331
|
+
for sub in ast.walk(value):
|
|
332
|
+
if isinstance(sub, ast.Call):
|
|
333
|
+
return False
|
|
334
|
+
return True
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def _is_illustration_node(node):
|
|
338
|
+
# A single "shape only" statement: an import, a bare reference/literal
|
|
339
|
+
# (not a call), a signature stub (def/class whose body is only pass /
|
|
340
|
+
# docstring / ...), or a literal assignment.
|
|
341
|
+
if isinstance(node, (ast.Import, ast.ImportFrom)):
|
|
342
|
+
return True
|
|
343
|
+
if isinstance(node, ast.Expr):
|
|
344
|
+
return not isinstance(node.value, ast.Call)
|
|
345
|
+
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
|
|
346
|
+
for b in node.body:
|
|
347
|
+
if isinstance(b, ast.Pass):
|
|
348
|
+
continue
|
|
349
|
+
if isinstance(b, ast.Expr) and isinstance(b.value, ast.Constant):
|
|
350
|
+
continue
|
|
351
|
+
return False
|
|
352
|
+
return True
|
|
353
|
+
if isinstance(node, ast.AnnAssign):
|
|
354
|
+
return node.value is None or _is_literalish(node.value)
|
|
355
|
+
if isinstance(node, ast.Assign):
|
|
356
|
+
return _is_literalish(node.value)
|
|
357
|
+
return False
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def _is_illustration(block):
|
|
361
|
+
# NARROW, explicit: a non-runnable block whose every top-level statement is
|
|
362
|
+
# a benign "shape" node — an import, a bare reference/literal, a signature
|
|
363
|
+
# stub, or a literal assignment (a pure signature/table snippet). Anything
|
|
364
|
+
# that does work (a call, a loop, a with/try) is NOT an illustration; it
|
|
365
|
+
# falls to the compiled-nonrunnable bucket. A runnable block (references the
|
|
366
|
+
# SDK/client) is never an illustration, so this class can never silently
|
|
367
|
+
# absorb a runnable example.
|
|
368
|
+
if _is_runnable(block):
|
|
369
|
+
return False
|
|
370
|
+
try:
|
|
371
|
+
tree = ast.parse(block)
|
|
372
|
+
except SyntaxError:
|
|
373
|
+
return False
|
|
374
|
+
if not tree.body:
|
|
375
|
+
return True
|
|
376
|
+
for node in tree.body:
|
|
377
|
+
if not _is_illustration_node(node):
|
|
378
|
+
return False
|
|
379
|
+
return True
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def _classify(block):
|
|
383
|
+
if _is_runnable(block):
|
|
384
|
+
return "runnable"
|
|
385
|
+
if _is_illustration(block):
|
|
386
|
+
return "illustration"
|
|
387
|
+
return "compiled"
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
# ----------------------------------------------------------------------------
|
|
391
|
+
# EXECUTE gate
|
|
392
|
+
# ----------------------------------------------------------------------------
|
|
393
|
+
|
|
394
|
+
# Exception TYPE names that signal a programming (not domain) error. Matched
|
|
395
|
+
# against the last line of a failed subprocess's traceback.
|
|
396
|
+
_PROGRAMMING_ERROR_NAMES = frozenset([
|
|
397
|
+
"SyntaxError", "IndentationError", "NameError", "AttributeError",
|
|
398
|
+
"TypeError", "KeyError", "IndexError", "ImportError", "ModuleNotFoundError",
|
|
399
|
+
])
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def _seed_literal(block):
|
|
403
|
+
# Build a test() fixture seeding one mock record (id "test01") for every
|
|
404
|
+
# entity the block references, so list()/load() resolve offline. Detection
|
|
405
|
+
# is by the "client.<Entity>(" factory call the generated examples use.
|
|
406
|
+
seeded = {}
|
|
407
|
+
for cap, key in _ENTITIES.items():
|
|
408
|
+
if ("." + cap + "(") in block:
|
|
409
|
+
seeded[key] = {"test01": {"id": "test01"}}
|
|
410
|
+
return {"entity": seeded}
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def _rewrite_to_test_mode(block):
|
|
414
|
+
# Force every client construction into offline test mode with seeded
|
|
415
|
+
# fixtures: both _SDK_CLASS(...) (live) and _SDK_CLASS.test(...) become
|
|
416
|
+
# _SDK_CLASS.test({"entity": {...}}). Balanced-paren aware, so a
|
|
417
|
+
# multi-line constructor argument is consumed whole. A bare mention of the
|
|
418
|
+
# class name (e.g. in an import) is left untouched.
|
|
419
|
+
seed = repr(_seed_literal(block))
|
|
420
|
+
replacement = _SDK_CLASS + ".test(" + seed + ")"
|
|
421
|
+
out = []
|
|
422
|
+
i = 0
|
|
423
|
+
n = len(block)
|
|
424
|
+
while True:
|
|
425
|
+
j = block.find(_SDK_CLASS, i)
|
|
426
|
+
if j < 0:
|
|
427
|
+
out.append(block[i:])
|
|
428
|
+
break
|
|
429
|
+
out.append(block[i:j])
|
|
430
|
+
k = j + len(_SDK_CLASS)
|
|
431
|
+
if block[k:k + 5] == ".test":
|
|
432
|
+
k += 5
|
|
433
|
+
while k < n and block[k] in _WS:
|
|
434
|
+
k += 1
|
|
435
|
+
if k < n and block[k] == "(":
|
|
436
|
+
depth = 0
|
|
437
|
+
m = k
|
|
438
|
+
while m < n:
|
|
439
|
+
c = block[m]
|
|
440
|
+
if c == "(":
|
|
441
|
+
depth += 1
|
|
442
|
+
elif c == ")":
|
|
443
|
+
depth -= 1
|
|
444
|
+
if depth == 0:
|
|
445
|
+
m += 1
|
|
446
|
+
break
|
|
447
|
+
m += 1
|
|
448
|
+
out.append(replacement)
|
|
449
|
+
i = m
|
|
450
|
+
else:
|
|
451
|
+
# Not a construction (e.g. "from ..._sdk import _SDK_CLASS") —
|
|
452
|
+
# keep the original text verbatim.
|
|
453
|
+
out.append(block[j:k])
|
|
454
|
+
i = k
|
|
455
|
+
return "".join(out)
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def _build_exec_source(block):
|
|
459
|
+
# Return runnable python source for a RUNNABLE block, else None. A block
|
|
460
|
+
# that constructs the SDK is rewritten into seeded test mode; a block that
|
|
461
|
+
# only drives a client/sdk gets a seeded test client injected under those
|
|
462
|
+
# names first, then runs verbatim. The set of blocks for which this returns
|
|
463
|
+
# a source is exactly the set _is_runnable() flags — the completeness gate
|
|
464
|
+
# asserts that, so no runnable block is ever silently skipped.
|
|
465
|
+
preamble = "import os" + _NL + "from " + _SDK_MODULE + " import " + _SDK_CLASS + _NL
|
|
466
|
+
if _SDK_CLASS in block:
|
|
467
|
+
return preamble + _rewrite_to_test_mode(block)
|
|
468
|
+
inject = ""
|
|
469
|
+
used = False
|
|
470
|
+
seed = repr(_seed_literal(block))
|
|
471
|
+
for var in _CLIENT_VARS:
|
|
472
|
+
if _uses_var(block, var):
|
|
473
|
+
inject += var + " = " + _SDK_CLASS + ".test(" + seed + ")" + _NL
|
|
474
|
+
used = True
|
|
475
|
+
if used:
|
|
476
|
+
return preamble + inject + block
|
|
477
|
+
return None
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
def _is_not_found_error(last):
|
|
481
|
+
# The ONLY tolerated domain error: a not-found / 404 raised because a
|
|
482
|
+
# referenced id was not seeded. Detected by the SDK error message text.
|
|
483
|
+
low = last.lower()
|
|
484
|
+
return ("404" in last) or ("not found" in low) or ("notfound" in low)
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
def _run_source(source, label, index):
|
|
488
|
+
# Run one rewritten block in a subprocess. Returns None when it exits 0 or
|
|
489
|
+
# fails with the single tolerated not-found/404 domain error; otherwise
|
|
490
|
+
# returns a failure message (a programming error, or any other runtime
|
|
491
|
+
# error — both fail the gate).
|
|
492
|
+
env = dict(os.environ)
|
|
493
|
+
env["PYTHONDONTWRITEBYTECODE"] = "1"
|
|
494
|
+
env["PYTHONPATH"] = _PY_ROOT + os.pathsep + env.get("PYTHONPATH", "")
|
|
495
|
+
|
|
496
|
+
proc = subprocess.run(
|
|
497
|
+
[sys.executable, "-c", source],
|
|
498
|
+
cwd=_PY_ROOT,
|
|
499
|
+
env=env,
|
|
500
|
+
capture_output=True,
|
|
501
|
+
text=True,
|
|
502
|
+
)
|
|
503
|
+
if proc.returncode == 0:
|
|
504
|
+
return None
|
|
505
|
+
|
|
506
|
+
stderr = proc.stderr or ""
|
|
507
|
+
errlines = [ln for ln in stderr.split(_NL) if ln.strip()]
|
|
508
|
+
last = errlines[-1] if errlines else ""
|
|
509
|
+
# Traceback's final line is "ExceptionType: message"; the type may be
|
|
510
|
+
# dotted (module-qualified) so compare the short name.
|
|
511
|
+
exc_type = last.split(":", 1)[0].strip().split(".")[-1]
|
|
512
|
+
|
|
513
|
+
detail = (
|
|
514
|
+
label + " python block #" + str(index) + ": " + last + _NL + _NL
|
|
515
|
+
+ "--- executed source ---" + _NL + source
|
|
516
|
+
+ _NL + _NL + "--- stderr ---" + _NL + stderr
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
if exc_type in _PROGRAMMING_ERROR_NAMES:
|
|
520
|
+
return "PROGRAMMING ERROR in " + detail
|
|
521
|
+
if _is_not_found_error(last):
|
|
522
|
+
return None # tolerated domain error
|
|
523
|
+
return "UNEXPECTED (non not-found/404) ERROR in " + detail
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
# ----------------------------------------------------------------------------
|
|
527
|
+
# COMPLETENESS gate (the guarantee)
|
|
528
|
+
# ----------------------------------------------------------------------------
|
|
529
|
+
|
|
530
|
+
def _completeness_gate(label, blocks):
|
|
531
|
+
# Partition every block, execute every runnable one, and assert the
|
|
532
|
+
# partition is complete. Returns the per-doc stats dict.
|
|
533
|
+
total = len(blocks)
|
|
534
|
+
executed = 0
|
|
535
|
+
runnable = 0
|
|
536
|
+
illustration = 0
|
|
537
|
+
compiled = 0
|
|
538
|
+
failures = []
|
|
539
|
+
|
|
540
|
+
for i, block in enumerate(blocks):
|
|
541
|
+
kind = _classify(block)
|
|
542
|
+
if kind == "runnable":
|
|
543
|
+
runnable += 1
|
|
544
|
+
source = _build_exec_source(block)
|
|
545
|
+
if source is None:
|
|
546
|
+
# Should be impossible: _is_runnable and _build_exec_source key
|
|
547
|
+
# off the same markers. If it ever happens a runnable example
|
|
548
|
+
# would be silently skipped — fail loudly.
|
|
549
|
+
failures.append(
|
|
550
|
+
label + " python block #" + str(i) + " is runnable-looking "
|
|
551
|
+
"(references the SDK/client) but produced no executable "
|
|
552
|
+
"source; it would be silently skipped:" + _NL + _NL + block
|
|
553
|
+
)
|
|
554
|
+
continue
|
|
555
|
+
executed += 1
|
|
556
|
+
msg = _run_source(source, label, i)
|
|
557
|
+
if msg is not None:
|
|
558
|
+
failures.append(msg)
|
|
559
|
+
elif kind == "illustration":
|
|
560
|
+
illustration += 1
|
|
561
|
+
else:
|
|
562
|
+
compiled += 1
|
|
563
|
+
|
|
564
|
+
print(
|
|
565
|
+
_NL + "[readme-examples] " + label + " python blocks: total="
|
|
566
|
+
+ str(total) + " executed=" + str(executed) + " compiled="
|
|
567
|
+
+ str(compiled) + " illustration=" + str(illustration)
|
|
568
|
+
)
|
|
569
|
+
|
|
570
|
+
if failures:
|
|
571
|
+
pytest.fail(
|
|
572
|
+
label + ": " + str(len(failures))
|
|
573
|
+
+ " documented python example(s) failed the completeness gate:"
|
|
574
|
+
+ _NL + _NL + (_NL + _NL).join(failures)
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
# Every runnable (SDK/client-referencing) block MUST have executed.
|
|
578
|
+
assert executed == runnable, (
|
|
579
|
+
label + ": " + str(runnable - executed) + " runnable python block(s) "
|
|
580
|
+
"were not executed — a documented example that drives the SDK/client "
|
|
581
|
+
"must run, never be silently skipped (executed=" + str(executed)
|
|
582
|
+
+ ", runnable=" + str(runnable) + ")"
|
|
583
|
+
)
|
|
584
|
+
# Every block is accounted for by exactly one bucket.
|
|
585
|
+
assert total == executed + compiled + illustration, (
|
|
586
|
+
label + ": python-block accounting does not add up — total=" + str(total)
|
|
587
|
+
+ " but executed+compiled+illustration="
|
|
588
|
+
+ str(executed + compiled + illustration)
|
|
589
|
+
+ ". Every block must be executed, compiled-nonrunnable, or a narrow "
|
|
590
|
+
"illustration; none may be dropped."
|
|
591
|
+
)
|
|
592
|
+
return {
|
|
593
|
+
"total": total,
|
|
594
|
+
"executed": executed,
|
|
595
|
+
"compiled": compiled,
|
|
596
|
+
"illustration": illustration,
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
def test_readme_completeness_gate():
|
|
601
|
+
stats = _completeness_gate("root README", _python_blocks())
|
|
602
|
+
assert stats["executed"] > 0, (
|
|
603
|
+
"expected at least one runnable python block in the root README to execute"
|
|
604
|
+
)
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
def test_local_readme_completeness_gate():
|
|
608
|
+
stats = _completeness_gate("py README.md", _local_readme_blocks())
|
|
609
|
+
assert stats["executed"] > 0, (
|
|
610
|
+
"expected at least one runnable python block in py/README.md to execute"
|
|
611
|
+
)
|
|
612
|
+
|
|
613
|
+
|
|
614
|
+
def test_reference_completeness_gate():
|
|
615
|
+
stats = _completeness_gate("py REFERENCE.md", _reference_blocks())
|
|
616
|
+
assert stats["executed"] > 0, (
|
|
617
|
+
"expected at least one runnable python block in py/REFERENCE.md to execute"
|
|
618
|
+
)
|
|
619
|
+
`)
|
|
620
|
+
})
|
|
621
|
+
})
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
export {
|
|
625
|
+
ReadmeExamplesTest
|
|
626
|
+
}
|