pythonnative 0.40.0__cp313-cp313-win_amd64.whl
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.
- pythonnative/__init__.py +411 -0
- pythonnative/_ios_log.py +92 -0
- pythonnative/_yoga.cp313-win_amd64.pyd +0 -0
- pythonnative/alerts.py +236 -0
- pythonnative/animated.py +1687 -0
- pythonnative/animation_graph.py +78 -0
- pythonnative/appearance.py +124 -0
- pythonnative/bootstrap.py +100 -0
- pythonnative/bridge/__init__.py +302 -0
- pythonnative/bridge/android.py +78 -0
- pythonnative/bridge/codec.py +170 -0
- pythonnative/bridge/commits.py +114 -0
- pythonnative/bridge/fake.py +243 -0
- pythonnative/bridge/ios.py +138 -0
- pythonnative/bridge/web.py +489 -0
- pythonnative/cli/__init__.py +7 -0
- pythonnative/cli/pn.py +1214 -0
- pythonnative/component.py +271 -0
- pythonnative/components/__init__.py +111 -0
- pythonnative/components/_base.py +166 -0
- pythonnative/components/controls.py +521 -0
- pythonnative/components/layout.py +518 -0
- pythonnative/components/lists.py +785 -0
- pythonnative/components/media.py +204 -0
- pythonnative/components/overlays.py +107 -0
- pythonnative/components/pressable.py +252 -0
- pythonnative/components/structural.py +167 -0
- pythonnative/components/text.py +291 -0
- pythonnative/devclient.py +824 -0
- pythonnative/devserver/__init__.py +34 -0
- pythonnative/devserver/server.py +718 -0
- pythonnative/devserver/static/animation_graph.js +82 -0
- pythonnative/devserver/static/bridge.js +147 -0
- pythonnative/devserver/static/colors.js +70 -0
- pythonnative/devserver/static/host.js +552 -0
- pythonnative/devserver/static/index.html +60 -0
- pythonnative/devserver/static/layout.js +85 -0
- pythonnative/devserver/static/preview.css +415 -0
- pythonnative/devserver/static/renderer.js +1994 -0
- pythonnative/devserver/static/shell.js +345 -0
- pythonnative/devserver/static/yoga/LICENSE +21 -0
- pythonnative/devserver/static/yoga/binaries/yoga-wasm-base64-esm.js +75 -0
- pythonnative/devserver/static/yoga/src/generated/YGEnums.d.ts +189 -0
- pythonnative/devserver/static/yoga/src/generated/YGEnums.js +211 -0
- pythonnative/devserver/static/yoga/src/generated/YGEnums.js.map +1 -0
- pythonnative/devserver/static/yoga/src/index.d.ts +12 -0
- pythonnative/devserver/static/yoga/src/index.js +16 -0
- pythonnative/devserver/static/yoga/src/index.js.map +1 -0
- pythonnative/devserver/static/yoga/src/load.d.ts +11 -0
- pythonnative/devserver/static/yoga/src/load.js +17 -0
- pythonnative/devserver/static/yoga/src/load.js.map +1 -0
- pythonnative/devserver/static/yoga/src/wrapAssembly.d.ts +155 -0
- pythonnative/devserver/static/yoga/src/wrapAssembly.js +125 -0
- pythonnative/devserver/static/yoga/src/wrapAssembly.js.map +1 -0
- pythonnative/devserver/watcher.py +267 -0
- pythonnative/devserver/ws.py +421 -0
- pythonnative/diagnostics.py +241 -0
- pythonnative/element.py +159 -0
- pythonnative/equality.py +19 -0
- pythonnative/events.py +231 -0
- pythonnative/gestures.py +1333 -0
- pythonnative/hooks.py +1621 -0
- pythonnative/hosts/__init__.py +63 -0
- pythonnative/hosts/base.py +596 -0
- pythonnative/hosts/native.py +302 -0
- pythonnative/hot_reload.py +585 -0
- pythonnative/layout.py +328 -0
- pythonnative/mutations.py +142 -0
- pythonnative/native/android/build.gradle +52 -0
- pythonnative/native/android/gradle.properties +3 -0
- pythonnative/native/android/settings.gradle +12 -0
- pythonnative/native/android/src/main/AndroidManifest.xml +11 -0
- pythonnative/native/android/src/main/cpp/CMakeLists.txt +9 -0
- pythonnative/native/android/src/main/cpp/yoga_jni.cpp +52 -0
- pythonnative/native/android/src/main/java/com/pythonnative/generated/NativeModules.kt +5 -0
- pythonnative/native/android/src/main/java/com/pythonnative/generated/NativeProps.kt +3705 -0
- pythonnative/native/android/src/main/java/com/pythonnative/generated/PNContracts.kt +129 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/PNBridge.kt +261 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/PythonHost.kt +20 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/animation/AnimationGraph.kt +108 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/animation/AnimationSpecs.kt +141 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/animation/PNAnimator.kt +257 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/CommitState.kt +106 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/JsonUtil.kt +175 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/MainThread.kt +40 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/PNLog.kt +33 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/PNRegistry.kt +82 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/PNTransaction.kt +80 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/TransactionApplier.kt +117 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/bridge/ViewRegistry.kt +74 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/BuiltinComponents.kt +37 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ButtonManager.kt +32 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ComponentManager.kt +213 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ContainerManagers.kt +69 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ControlManagers.kt +186 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/DatePickerManager.kt +123 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ImageLoader.kt +141 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ImageManager.kt +162 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ModalManager.kt +134 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PNColor.kt +201 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PickerManager.kt +86 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PortalManager.kt +55 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/PressableManager.kt +122 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ScreenStackManager.kt +110 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ScrollViewManager.kt +214 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/SegmentedControlManager.kt +113 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/StatusBarManager.kt +52 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/TabBarManager.kt +131 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/TextInputManager.kt +250 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/TextManager.kt +233 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/ViewStyler.kt +355 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/VirtualListManager.kt +159 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/components/WebViewManager.kt +149 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/gestures/GestureArbiter.kt +233 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/gestures/GestureCoordinator.kt +127 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/gestures/GestureRecognizers.kt +527 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/layout/NativeLayout.kt +145 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/layout/YogaNode.kt +29 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/BuiltinModules.kt +73 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/DeviceModules.kt +316 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/HostModule.kt +61 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/MediaModules.kt +200 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/NativeModule.kt +158 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/NotificationsModule.kt +100 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/PermissionsModule.kt +99 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/StorageModules.kt +89 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/modules/SystemModules.kt +178 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/plugins/GeneratedPlugins.kt +16 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/screens/PNScreenFragment.kt +188 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/screens/ScreenRegistry.kt +80 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNAccessibilityDelegate.kt +48 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNBorderDrawable.kt +76 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNEditText.kt +18 -0
- pythonnative/native/android/src/main/java/com/pythonnative/runtime/views/PNFrameLayout.kt +49 -0
- pythonnative/native/android/src/test/java/com/pythonnative/runtime/AnimationSpecsTest.kt +75 -0
- pythonnative/native/android/src/test/java/com/pythonnative/runtime/GestureArbiterTest.kt +197 -0
- pythonnative/native/android/src/test/java/com/pythonnative/runtime/PNColorTest.kt +47 -0
- pythonnative/native/android/src/test/java/com/pythonnative/runtime/PNTransactionTest.kt +78 -0
- pythonnative/native/android/src/test/java/com/pythonnative/runtime/PromiseTest.kt +89 -0
- pythonnative/native/ios/Package.swift +23 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Animation/PNAnimationGraph.swift +199 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Animation/PNAnimator.swift +312 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNBridge.swift +255 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNCommit.swift +104 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNRegistry.swift +142 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNTransaction.swift +166 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Bridge/PNViewRegistry.swift +53 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNButtonManagers.swift +207 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNColor.swift +173 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNComponentManager.swift +183 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNContainerView.swift +56 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNControlManagers.swift +281 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNImageManager.swift +222 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNOverlayManagers.swift +243 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNPressableManager.swift +114 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNScreenStackManager.swift +107 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNScrollViewManager.swift +197 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTabBarManager.swift +106 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTextInputManager.swift +297 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTextManager.swift +230 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNTransform.swift +82 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNViewManager.swift +86 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNViewState.swift +87 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNViewStyler.swift +340 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNVirtualListManager.swift +155 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Components/PNWebViewManager.swift +124 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Generated/NativeModules.swift +1 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Generated/NativeProps.swift +3775 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Generated/PNContracts.swift +46 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Gestures/PNGestureCoordinator.swift +260 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Layout/PNLayout.swift +173 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/AlertModule.swift +92 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/HostModule.swift +80 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/LifecycleModules.swift +174 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/MediaModules.swift +177 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/PNNativeModule.swift +168 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/PermissionModules.swift +266 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Modules/SystemModules.swift +251 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Plugins/PNPluginRegistration.swift +8 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Screens/PNScreenRegistry.swift +46 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Screens/PNViewController.swift +237 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Support/PNCompat.swift +46 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Support/PNJSON.swift +173 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Support/PNLog.swift +47 -0
- pythonnative/native/ios/Sources/PythonNativeKit/Support/PNWindow.swift +51 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNAnimationGraphTests.swift +26 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNColorTests.swift +50 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNCommitTests.swift +86 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNGestureTests.swift +101 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNManagerTests.swift +126 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNModuleTests.swift +111 -0
- pythonnative/native/ios/Tests/PythonNativeKitTests/PNTransactionTests.swift +91 -0
- pythonnative/native/yoga/LICENSE +21 -0
- pythonnative/native/yoga/Package.swift +9 -0
- pythonnative/native/yoga/VENDORED.md +2 -0
- pythonnative/native/yoga/include/PNStyle.h +10 -0
- pythonnative/native/yoga/include/module.modulemap +5 -0
- pythonnative/native/yoga/include/yoga/YGConfig.h +158 -0
- pythonnative/native/yoga/include/yoga/YGEnums.h +142 -0
- pythonnative/native/yoga/include/yoga/YGMacros.h +99 -0
- pythonnative/native/yoga/include/yoga/YGNode.h +304 -0
- pythonnative/native/yoga/include/yoga/YGNodeLayout.h +35 -0
- pythonnative/native/yoga/include/yoga/YGNodeStyle.h +130 -0
- pythonnative/native/yoga/include/yoga/YGPixelGrid.h +29 -0
- pythonnative/native/yoga/include/yoga/YGValue.h +84 -0
- pythonnative/native/yoga/include/yoga/Yoga.h +21 -0
- pythonnative/native/yoga/python.cpp +16 -0
- pythonnative/native/yoga/style.cpp +272 -0
- pythonnative/native/yoga/yoga/CMakeLists.txt +41 -0
- pythonnative/native/yoga/yoga/YGConfig.cpp +92 -0
- pythonnative/native/yoga/yoga/YGConfig.h +158 -0
- pythonnative/native/yoga/yoga/YGEnums.cpp +262 -0
- pythonnative/native/yoga/yoga/YGEnums.h +142 -0
- pythonnative/native/yoga/yoga/YGMacros.h +99 -0
- pythonnative/native/yoga/yoga/YGNode.cpp +366 -0
- pythonnative/native/yoga/yoga/YGNode.h +304 -0
- pythonnative/native/yoga/yoga/YGNodeLayout.cpp +92 -0
- pythonnative/native/yoga/yoga/YGNodeLayout.h +35 -0
- pythonnative/native/yoga/yoga/YGNodeStyle.cpp +405 -0
- pythonnative/native/yoga/yoga/YGNodeStyle.h +130 -0
- pythonnative/native/yoga/yoga/YGPixelGrid.cpp +22 -0
- pythonnative/native/yoga/yoga/YGPixelGrid.h +29 -0
- pythonnative/native/yoga/yoga/YGValue.cpp +20 -0
- pythonnative/native/yoga/yoga/YGValue.h +84 -0
- pythonnative/native/yoga/yoga/Yoga.h +21 -0
- pythonnative/native/yoga/yoga/algorithm/AbsoluteLayout.cpp +563 -0
- pythonnative/native/yoga/yoga/algorithm/AbsoluteLayout.h +41 -0
- pythonnative/native/yoga/yoga/algorithm/Align.h +72 -0
- pythonnative/native/yoga/yoga/algorithm/Baseline.cpp +78 -0
- pythonnative/native/yoga/yoga/algorithm/Baseline.h +21 -0
- pythonnative/native/yoga/yoga/algorithm/BoundAxis.h +79 -0
- pythonnative/native/yoga/yoga/algorithm/Cache.cpp +121 -0
- pythonnative/native/yoga/yoga/algorithm/Cache.h +30 -0
- pythonnative/native/yoga/yoga/algorithm/CalculateLayout.cpp +2428 -0
- pythonnative/native/yoga/yoga/algorithm/CalculateLayout.h +38 -0
- pythonnative/native/yoga/yoga/algorithm/FlexDirection.h +120 -0
- pythonnative/native/yoga/yoga/algorithm/FlexLine.cpp +124 -0
- pythonnative/native/yoga/yoga/algorithm/FlexLine.h +75 -0
- pythonnative/native/yoga/yoga/algorithm/PixelGrid.cpp +132 -0
- pythonnative/native/yoga/yoga/algorithm/PixelGrid.h +29 -0
- pythonnative/native/yoga/yoga/algorithm/SizingMode.h +73 -0
- pythonnative/native/yoga/yoga/algorithm/TrailingPosition.h +44 -0
- pythonnative/native/yoga/yoga/config/Config.cpp +135 -0
- pythonnative/native/yoga/yoga/config/Config.h +92 -0
- pythonnative/native/yoga/yoga/debug/AssertFatal.cpp +53 -0
- pythonnative/native/yoga/yoga/debug/AssertFatal.h +29 -0
- pythonnative/native/yoga/yoga/debug/Log.cpp +107 -0
- pythonnative/native/yoga/yoga/debug/Log.h +34 -0
- pythonnative/native/yoga/yoga/enums/Align.h +47 -0
- pythonnative/native/yoga/yoga/enums/BoxSizing.h +40 -0
- pythonnative/native/yoga/yoga/enums/Dimension.h +40 -0
- pythonnative/native/yoga/yoga/enums/Direction.h +41 -0
- pythonnative/native/yoga/yoga/enums/Display.h +41 -0
- pythonnative/native/yoga/yoga/enums/Edge.h +47 -0
- pythonnative/native/yoga/yoga/enums/Errata.h +41 -0
- pythonnative/native/yoga/yoga/enums/ExperimentalFeature.h +39 -0
- pythonnative/native/yoga/yoga/enums/FlexDirection.h +42 -0
- pythonnative/native/yoga/yoga/enums/Gutter.h +41 -0
- pythonnative/native/yoga/yoga/enums/Justify.h +44 -0
- pythonnative/native/yoga/yoga/enums/LogLevel.h +44 -0
- pythonnative/native/yoga/yoga/enums/MeasureMode.h +41 -0
- pythonnative/native/yoga/yoga/enums/NodeType.h +40 -0
- pythonnative/native/yoga/yoga/enums/Overflow.h +41 -0
- pythonnative/native/yoga/yoga/enums/PhysicalEdge.h +21 -0
- pythonnative/native/yoga/yoga/enums/PositionType.h +41 -0
- pythonnative/native/yoga/yoga/enums/Unit.h +42 -0
- pythonnative/native/yoga/yoga/enums/Wrap.h +41 -0
- pythonnative/native/yoga/yoga/enums/YogaEnums.h +85 -0
- pythonnative/native/yoga/yoga/event/event.cpp +87 -0
- pythonnative/native/yoga/yoga/event/event.h +130 -0
- pythonnative/native/yoga/yoga/module.modulemap +21 -0
- pythonnative/native/yoga/yoga/node/CachedMeasurement.h +53 -0
- pythonnative/native/yoga/yoga/node/LayoutResults.cpp +48 -0
- pythonnative/native/yoga/yoga/node/LayoutResults.h +122 -0
- pythonnative/native/yoga/yoga/node/LayoutableChildren.h +148 -0
- pythonnative/native/yoga/yoga/node/Node.cpp +443 -0
- pythonnative/native/yoga/yoga/node/Node.h +337 -0
- pythonnative/native/yoga/yoga/numeric/Comparison.h +81 -0
- pythonnative/native/yoga/yoga/numeric/FloatOptional.h +93 -0
- pythonnative/native/yoga/yoga/style/SmallValueBuffer.h +133 -0
- pythonnative/native/yoga/yoga/style/Style.h +757 -0
- pythonnative/native/yoga/yoga/style/StyleLength.h +107 -0
- pythonnative/native/yoga/yoga/style/StyleValueHandle.h +98 -0
- pythonnative/native/yoga/yoga/style/StyleValuePool.h +127 -0
- pythonnative/native_modules/__init__.py +124 -0
- pythonnative/native_modules/app_state.py +99 -0
- pythonnative/native_modules/battery.py +75 -0
- pythonnative/native_modules/biometrics.py +42 -0
- pythonnative/native_modules/camera.py +65 -0
- pythonnative/native_modules/clipboard.py +46 -0
- pythonnative/native_modules/fallback.py +324 -0
- pythonnative/native_modules/file_system.py +145 -0
- pythonnative/native_modules/haptics.py +75 -0
- pythonnative/native_modules/linking.py +123 -0
- pythonnative/native_modules/location.py +75 -0
- pythonnative/native_modules/net_info.py +112 -0
- pythonnative/native_modules/notifications.py +129 -0
- pythonnative/native_modules/permissions.py +108 -0
- pythonnative/native_modules/registry.py +526 -0
- pythonnative/native_modules/secure_store.py +52 -0
- pythonnative/native_modules/share.py +51 -0
- pythonnative/native_views/__init__.py +375 -0
- pythonnative/native_views/base.py +316 -0
- pythonnative/native_views/bridge_backend.py +277 -0
- pythonnative/navigation/__init__.py +103 -0
- pythonnative/navigation/container.py +154 -0
- pythonnative/navigation/handle.py +540 -0
- pythonnative/navigation/hooks.py +133 -0
- pythonnative/navigation/host.py +58 -0
- pythonnative/navigation/linking.py +200 -0
- pythonnative/navigation/navigators.py +659 -0
- pythonnative/navigation/screen.py +149 -0
- pythonnative/navigation/state.py +269 -0
- pythonnative/net.py +248 -0
- pythonnative/platform.py +166 -0
- pythonnative/platform_metrics.py +252 -0
- pythonnative/preview.py +287 -0
- pythonnative/profiling.py +101 -0
- pythonnative/project/__init__.py +68 -0
- pythonnative/project/android.py +557 -0
- pythonnative/project/builder.py +817 -0
- pythonnative/project/config.py +773 -0
- pythonnative/project/deps.py +719 -0
- pythonnative/project/devices.py +304 -0
- pythonnative/project/doctor.py +283 -0
- pythonnative/project/fingerprint.py +171 -0
- pythonnative/project/icons.py +247 -0
- pythonnative/project/ios.py +325 -0
- pythonnative/project/lockfile.py +100 -0
- pythonnative/project/permissions.py +361 -0
- pythonnative/project/plugins.py +519 -0
- pythonnative/project/runtime_assets.py +183 -0
- pythonnative/py.typed +0 -0
- pythonnative/query.py +153 -0
- pythonnative/reconciler/__init__.py +29 -0
- pythonnative/reconciler/boundaries.py +375 -0
- pythonnative/reconciler/children.py +88 -0
- pythonnative/reconciler/core.py +1165 -0
- pythonnative/reconciler/layout_pass.py +393 -0
- pythonnative/reconciler/vnode.py +266 -0
- pythonnative/refresh.py +55 -0
- pythonnative/runtime.py +306 -0
- pythonnative/scheduler.py +159 -0
- pythonnative/sdk/__init__.py +153 -0
- pythonnative/sdk/_components.py +427 -0
- pythonnative/sdk/builtins.py +91 -0
- pythonnative/sdk/codegen.py +149 -0
- pythonnative/sdk/module_codegen.py +184 -0
- pythonnative/sdk/schema.py +224 -0
- pythonnative/sdk/templates/contracts.kt +46 -0
- pythonnative/sdk/templates/contracts.swift +46 -0
- pythonnative/storage.py +184 -0
- pythonnative/style.py +841 -0
- pythonnative/suspense.py +183 -0
- pythonnative/templates/android_template/app/build.gradle +75 -0
- pythonnative/templates/android_template/app/proguard-rules.pro +21 -0
- pythonnative/templates/android_template/app/src/androidTest/java/com/pythonnative/android_template/ExampleInstrumentedTest.kt +24 -0
- pythonnative/templates/android_template/app/src/debug/AndroidManifest.xml +7 -0
- pythonnative/templates/android_template/app/src/main/AndroidManifest.xml +32 -0
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt +130 -0
- pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/ScreenFragment.kt +13 -0
- pythonnative/templates/android_template/app/src/main/res/drawable/ic_launcher_background.xml +170 -0
- pythonnative/templates/android_template/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +30 -0
- pythonnative/templates/android_template/app/src/main/res/layout/activity_main.xml +10 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +6 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +6 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp +0 -0
- pythonnative/templates/android_template/app/src/main/res/navigation/nav_graph.xml +22 -0
- pythonnative/templates/android_template/app/src/main/res/values/colors.xml +5 -0
- pythonnative/templates/android_template/app/src/main/res/values/strings.xml +5 -0
- pythonnative/templates/android_template/app/src/main/res/values/themes.xml +9 -0
- pythonnative/templates/android_template/app/src/main/res/values-night/themes.xml +7 -0
- pythonnative/templates/android_template/app/src/main/res/xml/backup_rules.xml +13 -0
- pythonnative/templates/android_template/app/src/main/res/xml/data_extraction_rules.xml +19 -0
- pythonnative/templates/android_template/app/src/test/java/com/pythonnative/android_template/ExampleUnitTest.kt +17 -0
- pythonnative/templates/android_template/build.gradle +10 -0
- pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.jar +0 -0
- pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.properties +6 -0
- pythonnative/templates/android_template/gradle.properties +23 -0
- pythonnative/templates/android_template/gradlew +185 -0
- pythonnative/templates/android_template/gradlew.bat +89 -0
- pythonnative/templates/android_template/settings.gradle +17 -0
- pythonnative/templates/ios_template/ios_template/AppDelegate.swift +51 -0
- pythonnative/templates/ios_template/ios_template/Assets.xcassets/AccentColor.colorset/Contents.json +11 -0
- pythonnative/templates/ios_template/ios_template/Assets.xcassets/AppIcon.appiconset/Contents.json +13 -0
- pythonnative/templates/ios_template/ios_template/Assets.xcassets/Contents.json +6 -0
- pythonnative/templates/ios_template/ios_template/Base.lproj/LaunchScreen.storyboard +25 -0
- pythonnative/templates/ios_template/ios_template/BridgingHeader.h +14 -0
- pythonnative/templates/ios_template/ios_template/Info.plist +28 -0
- pythonnative/templates/ios_template/ios_template/PythonRuntime.swift +390 -0
- pythonnative/templates/ios_template/ios_template/SceneDelegate.swift +61 -0
- pythonnative/templates/ios_template/ios_template/ViewController.swift +23 -0
- pythonnative/templates/ios_template/ios_template.xcodeproj/project.pbxproj +745 -0
- pythonnative/templates/ios_template/ios_template.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- pythonnative/templates/ios_template/ios_templateTests/ios_templateTests.swift +36 -0
- pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITests.swift +41 -0
- pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITestsLaunchTests.swift +32 -0
- pythonnative/testing/__init__.py +53 -0
- pythonnative/testing/backend.py +276 -0
- pythonnative/testing/harness.py +369 -0
- pythonnative/utils.py +145 -0
- pythonnative-0.40.0.dist-info/DELVEWHEEL +2 -0
- pythonnative-0.40.0.dist-info/METADATA +150 -0
- pythonnative-0.40.0.dist-info/RECORD +418 -0
- pythonnative-0.40.0.dist-info/WHEEL +5 -0
- pythonnative-0.40.0.dist-info/entry_points.txt +2 -0
- pythonnative-0.40.0.dist-info/licenses/LICENSE +21 -0
- pythonnative-0.40.0.dist-info/top_level.txt +1 -0
- pythonnative.libs/msvcp140-a4c2229bdc2a2a630acdc095b4d86008.dll +0 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"""Typed module facades and native dispatch adapters from Python protocols."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from .schema import MODULES
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _types(schema: dict[str, Any]) -> tuple[str, str, str]:
|
|
11
|
+
if "anyOf" in schema:
|
|
12
|
+
variants = [item for item in schema["anyOf"] if item.get("type") != "null"]
|
|
13
|
+
if len(variants) == 1:
|
|
14
|
+
python, swift, kotlin = _types(variants[0])
|
|
15
|
+
return python + " | None", swift + "?", kotlin + "?"
|
|
16
|
+
return {
|
|
17
|
+
"string": ("str", "String", "String"),
|
|
18
|
+
"integer": ("int", "Int", "Int"),
|
|
19
|
+
"number": ("float", "Double", "Double"),
|
|
20
|
+
"boolean": ("bool", "Bool", "Boolean"),
|
|
21
|
+
"null": ("None", "Void", "Unit"),
|
|
22
|
+
}.get(schema.get("type", ""), ("Any", "Any", "Any"))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def generate_modules() -> dict[str, str]:
|
|
26
|
+
"""Generate native interfaces, checked dispatch, and typed Python calls."""
|
|
27
|
+
python = [
|
|
28
|
+
'"""Generated native module facades."""',
|
|
29
|
+
"from typing import Any",
|
|
30
|
+
"from pythonnative.native_modules.registry import native_module",
|
|
31
|
+
"",
|
|
32
|
+
]
|
|
33
|
+
swift = ["import Foundation", ""]
|
|
34
|
+
kotlin = [
|
|
35
|
+
"package com.pythonnative.generated",
|
|
36
|
+
"",
|
|
37
|
+
"import org.json.JSONObject",
|
|
38
|
+
"import com.pythonnative.runtime.modules.NativeModule",
|
|
39
|
+
"import com.pythonnative.runtime.modules.Promise",
|
|
40
|
+
"",
|
|
41
|
+
]
|
|
42
|
+
for name, module in sorted(MODULES.items()):
|
|
43
|
+
if not name.isidentifier():
|
|
44
|
+
raise ValueError(f"Invalid module name: {name!r}")
|
|
45
|
+
python.append(f"class {name}:")
|
|
46
|
+
swift.extend([f"public protocol {name}Implementation {{", " init()"])
|
|
47
|
+
kotlin.append(f"interface {name}Implementation {{")
|
|
48
|
+
method_info = []
|
|
49
|
+
for method, contract in module.methods.items():
|
|
50
|
+
if not method.isidentifier():
|
|
51
|
+
raise ValueError(f"Invalid method name: {method!r}")
|
|
52
|
+
result_py, result_swift, result_kotlin = _types(contract["result"])
|
|
53
|
+
params_py, params_swift, params_kotlin, args = [], [], [], []
|
|
54
|
+
for key, schema in contract["arguments"].items():
|
|
55
|
+
if not key.isidentifier():
|
|
56
|
+
raise ValueError(f"Invalid argument name: {key!r}")
|
|
57
|
+
pt, st, kt = _types(schema)
|
|
58
|
+
params_py.append(f"{key}: {pt}")
|
|
59
|
+
params_swift.append(f"{key}: {st}")
|
|
60
|
+
params_kotlin.append(f"{key}: {kt}")
|
|
61
|
+
args.append(key)
|
|
62
|
+
asynchronous = contract["async"]
|
|
63
|
+
python.extend(
|
|
64
|
+
[
|
|
65
|
+
" @staticmethod",
|
|
66
|
+
f" {'async ' if asynchronous else ''}def {method}("
|
|
67
|
+
+ ", ".join(params_py)
|
|
68
|
+
+ f") -> {result_py}:",
|
|
69
|
+
f' return {"await " if asynchronous else ""}native_module("{name}").'
|
|
70
|
+
f'{"call_async" if asynchronous else "call"}("{method}"'
|
|
71
|
+
+ "".join(f", {key}={key}" for key in args)
|
|
72
|
+
+ ")",
|
|
73
|
+
"",
|
|
74
|
+
]
|
|
75
|
+
)
|
|
76
|
+
if asynchronous:
|
|
77
|
+
swift.append(
|
|
78
|
+
f" func {method}("
|
|
79
|
+
+ ", ".join(params_swift + [f"completion: @escaping (Result<{result_swift}, Error>) -> Void"])
|
|
80
|
+
+ ")"
|
|
81
|
+
)
|
|
82
|
+
kotlin.append(
|
|
83
|
+
f" fun {method}("
|
|
84
|
+
+ ", ".join(params_kotlin + [f"completion: (Result<{result_kotlin}>) -> Unit"])
|
|
85
|
+
+ ")"
|
|
86
|
+
)
|
|
87
|
+
else:
|
|
88
|
+
swift.append(f" func {method}(" + ", ".join(params_swift) + f") throws -> {result_swift}")
|
|
89
|
+
kotlin.append(f" fun {method}(" + ", ".join(params_kotlin) + f"): {result_kotlin}")
|
|
90
|
+
method_info.append((method, contract, args))
|
|
91
|
+
if not method_info:
|
|
92
|
+
python.append(" pass")
|
|
93
|
+
python.append("")
|
|
94
|
+
swift.extend(
|
|
95
|
+
[
|
|
96
|
+
"}",
|
|
97
|
+
"",
|
|
98
|
+
f"public final class {name}Module<Implementation: {name}Implementation>: PNNativeModule {{",
|
|
99
|
+
f' public static var name: String {{ "{name}" }}',
|
|
100
|
+
" let implementation: Implementation",
|
|
101
|
+
" public init() { implementation = Implementation() }",
|
|
102
|
+
" public func call(_ method: String, args: [String: Any], promise: PNPromise) {",
|
|
103
|
+
" do {",
|
|
104
|
+
" switch method {",
|
|
105
|
+
]
|
|
106
|
+
)
|
|
107
|
+
kotlin.extend(
|
|
108
|
+
[
|
|
109
|
+
"}",
|
|
110
|
+
"",
|
|
111
|
+
f"class {name}Module(private val implementation: {name}Implementation): NativeModule {{",
|
|
112
|
+
f' override val name = "{name}"',
|
|
113
|
+
" override fun call(method: String, args: JSONObject, promise: Promise) {",
|
|
114
|
+
" try {",
|
|
115
|
+
" when (method) {",
|
|
116
|
+
]
|
|
117
|
+
)
|
|
118
|
+
for method, contract, args in method_info:
|
|
119
|
+
swift.append(f' case "{method}":')
|
|
120
|
+
kotlin.append(f' "{method}" -> {{')
|
|
121
|
+
for key in args:
|
|
122
|
+
_, st, kt = _types(contract["arguments"][key])
|
|
123
|
+
if st.endswith("?"):
|
|
124
|
+
swift.append(f' let {key} = args["{key}"] as? {st[:-1]}')
|
|
125
|
+
else:
|
|
126
|
+
swift.append(
|
|
127
|
+
f' guard let {key} = args["{key}"] as? {st} else {{ '
|
|
128
|
+
f'promise.reject("Invalid {key}"); return }}'
|
|
129
|
+
)
|
|
130
|
+
accessor = {"String": "getString", "Int": "getInt", "Double": "getDouble", "Boolean": "getBoolean"}.get(
|
|
131
|
+
kt.rstrip("?"), "get"
|
|
132
|
+
)
|
|
133
|
+
expression = f'args.{accessor}("{key}")'
|
|
134
|
+
if kt.endswith("?"):
|
|
135
|
+
expression = f'if (args.isNull("{key}")) null else {expression}'
|
|
136
|
+
kotlin.append(f" val {key} = {expression}")
|
|
137
|
+
sc = f"implementation.{method}(" + ", ".join(f"{key}: {key}" for key in args)
|
|
138
|
+
kc = f"implementation.{method}(" + ", ".join(args)
|
|
139
|
+
void = contract["result"].get("type") == "null"
|
|
140
|
+
if contract["async"]:
|
|
141
|
+
swift.append(f" {sc}) {{ result in")
|
|
142
|
+
swift.append(
|
|
143
|
+
" switch result { case .success(let value): promise.resolve("
|
|
144
|
+
+ ("nil" if void else "value")
|
|
145
|
+
+ "); case .failure(let error): promise.reject(error) }"
|
|
146
|
+
)
|
|
147
|
+
swift.append(" }")
|
|
148
|
+
kotlin.append(
|
|
149
|
+
f" {kc}) {{ result -> result.fold({{ value -> promise.resolve("
|
|
150
|
+
+ ("null" if void else "value")
|
|
151
|
+
+ ') }, { error -> promise.reject(error.message ?: "Native call failed") }) }'
|
|
152
|
+
)
|
|
153
|
+
elif void:
|
|
154
|
+
swift.append(f" try {sc}); promise.resolve(nil)")
|
|
155
|
+
kotlin.append(f" {kc}); promise.resolve(null)")
|
|
156
|
+
else:
|
|
157
|
+
swift.append(f" promise.resolve(try {sc}))")
|
|
158
|
+
kotlin.append(f" promise.resolve({kc}))")
|
|
159
|
+
kotlin.append(" }")
|
|
160
|
+
swift.extend(
|
|
161
|
+
[
|
|
162
|
+
' default: promise.reject("Unknown method", code: "unknown_method")',
|
|
163
|
+
" }",
|
|
164
|
+
" } catch { promise.reject(error) }",
|
|
165
|
+
" }",
|
|
166
|
+
"}",
|
|
167
|
+
"",
|
|
168
|
+
]
|
|
169
|
+
)
|
|
170
|
+
kotlin.extend(
|
|
171
|
+
[
|
|
172
|
+
" else -> promise.rejectUnknownMethod(method)",
|
|
173
|
+
" }",
|
|
174
|
+
' } catch (error: Exception) { promise.reject(error.message ?: "Native call failed") }',
|
|
175
|
+
" }",
|
|
176
|
+
"}",
|
|
177
|
+
"",
|
|
178
|
+
]
|
|
179
|
+
)
|
|
180
|
+
return {
|
|
181
|
+
"modules.py": "\n".join(python),
|
|
182
|
+
"NativeModules.swift": "\n".join(swift),
|
|
183
|
+
"NativeModules.kt": "\n".join(kotlin),
|
|
184
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"""Python definitions for native props, events, commands, and modules."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import collections.abc
|
|
6
|
+
import dataclasses
|
|
7
|
+
import enum
|
|
8
|
+
import hashlib
|
|
9
|
+
import inspect
|
|
10
|
+
import json
|
|
11
|
+
import types
|
|
12
|
+
import typing
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
from typing import Any, Mapping
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True)
|
|
18
|
+
class NativeField:
|
|
19
|
+
"""Native behavior attached to an annotated dataclass field.
|
|
20
|
+
|
|
21
|
+
Use ``Annotated[T, NativeField(...)]`` for native-specific metadata.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
invalidates_layout: bool = False
|
|
25
|
+
recreate: bool = False
|
|
26
|
+
animated: bool = False
|
|
27
|
+
platforms: tuple[str, ...] = ("ios", "android", "web")
|
|
28
|
+
description: str = ""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def type_schema(annotation: Any) -> dict[str, Any]:
|
|
32
|
+
"""Convert a Python annotation into a portable JSON type description."""
|
|
33
|
+
origin, args = typing.get_origin(annotation), typing.get_args(annotation)
|
|
34
|
+
if origin is typing.Annotated:
|
|
35
|
+
result = type_schema(args[0])
|
|
36
|
+
for metadata in args[1:]:
|
|
37
|
+
if isinstance(metadata, NativeField):
|
|
38
|
+
result["native"] = dataclasses.asdict(metadata)
|
|
39
|
+
return result
|
|
40
|
+
if origin in (typing.Union, types.UnionType):
|
|
41
|
+
return {"anyOf": [type_schema(arg) for arg in args]}
|
|
42
|
+
if origin is typing.Literal:
|
|
43
|
+
return {"enum": list(args)}
|
|
44
|
+
if annotation is type(None):
|
|
45
|
+
return {"type": "null"}
|
|
46
|
+
if annotation in (str, bool, int, float):
|
|
47
|
+
return {"type": {str: "string", bool: "boolean", int: "integer", float: "number"}[annotation]}
|
|
48
|
+
if origin in (list, tuple, set, collections.abc.Sequence):
|
|
49
|
+
return {"type": "array", "items": type_schema(args[0]) if args else {}}
|
|
50
|
+
if origin in (dict, collections.abc.Mapping) or annotation is dict:
|
|
51
|
+
return {"type": "object", "additionalProperties": type_schema(args[-1]) if args else {}}
|
|
52
|
+
if origin in (typing.Callable, collections.abc.Callable):
|
|
53
|
+
return {
|
|
54
|
+
"type": "event",
|
|
55
|
+
"arguments": [type_schema(arg) for arg in args[0]] if args and args[0] is not Ellipsis else [],
|
|
56
|
+
}
|
|
57
|
+
if inspect.isclass(annotation) and issubclass(annotation, enum.Enum):
|
|
58
|
+
return {"enum": [value.value for value in annotation]}
|
|
59
|
+
if dataclasses.is_dataclass(annotation):
|
|
60
|
+
hints = typing.get_type_hints(annotation, include_extras=True)
|
|
61
|
+
return {"type": "object", "properties": {name: type_schema(value) for name, value in hints.items()}}
|
|
62
|
+
return {}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def validate(value: Any, schema: Mapping[str, Any], path: str = "value") -> None:
|
|
66
|
+
"""Validate wire values without coercion or ambiguous boolean comparisons."""
|
|
67
|
+
if "anyOf" in schema:
|
|
68
|
+
for alternative in schema["anyOf"]:
|
|
69
|
+
try:
|
|
70
|
+
validate(value, alternative, path)
|
|
71
|
+
return
|
|
72
|
+
except TypeError:
|
|
73
|
+
pass
|
|
74
|
+
raise TypeError(f"{path} does not match its annotation")
|
|
75
|
+
if "enum" in schema:
|
|
76
|
+
candidate = value.value if isinstance(value, enum.Enum) else value
|
|
77
|
+
if candidate not in schema["enum"]:
|
|
78
|
+
raise TypeError(f"{path} must be one of {schema['enum']!r}")
|
|
79
|
+
kind = schema.get("type")
|
|
80
|
+
checks = {
|
|
81
|
+
"null": lambda: value is None,
|
|
82
|
+
"string": lambda: isinstance(value, str),
|
|
83
|
+
"boolean": lambda: type(value) is bool,
|
|
84
|
+
"integer": lambda: type(value) is int,
|
|
85
|
+
"number": lambda: type(value) in (int, float),
|
|
86
|
+
"array": lambda: isinstance(value, (list, tuple, set)),
|
|
87
|
+
"object": lambda: isinstance(value, Mapping) or dataclasses.is_dataclass(value),
|
|
88
|
+
"event": lambda: callable(value),
|
|
89
|
+
}
|
|
90
|
+
if kind in checks and not checks[kind]():
|
|
91
|
+
raise TypeError(f"{path} must be {kind}, received {type(value).__name__}")
|
|
92
|
+
if kind == "array":
|
|
93
|
+
for index, item in enumerate(value):
|
|
94
|
+
validate(item, schema.get("items", {}), f"{path}[{index}]")
|
|
95
|
+
if kind == "object":
|
|
96
|
+
values = dataclasses.asdict(value) if dataclasses.is_dataclass(value) and not isinstance(value, type) else value
|
|
97
|
+
for name, item in values.items():
|
|
98
|
+
validate(
|
|
99
|
+
item, schema.get("properties", {}).get(name, schema.get("additionalProperties", {})), f"{path}.{name}"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@dataclass(frozen=True)
|
|
104
|
+
class ComponentSchema:
|
|
105
|
+
"""The portable contract shared by every renderer and the component SDK."""
|
|
106
|
+
|
|
107
|
+
name: str
|
|
108
|
+
props: dict[str, dict[str, Any]]
|
|
109
|
+
required: tuple[str, ...] = ()
|
|
110
|
+
defaults: dict[str, Any] = field(default_factory=dict)
|
|
111
|
+
measurement: str = "intrinsic"
|
|
112
|
+
commands: dict[str, Any] = field(default_factory=dict)
|
|
113
|
+
|
|
114
|
+
@classmethod
|
|
115
|
+
def from_dataclass(cls, name: str, props: type, *, measurement: str = "intrinsic") -> ComponentSchema:
|
|
116
|
+
"""Describe a native widget with a frozen Python props dataclass."""
|
|
117
|
+
if not dataclasses.is_dataclass(props):
|
|
118
|
+
raise TypeError("Native props must be a dataclass")
|
|
119
|
+
hints = typing.get_type_hints(props, include_extras=True)
|
|
120
|
+
required, defaults = [], {}
|
|
121
|
+
for item in dataclasses.fields(props):
|
|
122
|
+
if item.default is not dataclasses.MISSING:
|
|
123
|
+
defaults[item.name] = item.default
|
|
124
|
+
elif item.default_factory is not dataclasses.MISSING:
|
|
125
|
+
defaults[item.name] = item.default_factory()
|
|
126
|
+
else:
|
|
127
|
+
required.append(item.name)
|
|
128
|
+
return cls(
|
|
129
|
+
name, {key: type_schema(value) for key, value in hints.items()}, tuple(required), defaults, measurement
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
def validate(self, props: Mapping[str, Any], *, partial: bool = False) -> None:
|
|
133
|
+
"""Validate a construction or a partial update against this contract."""
|
|
134
|
+
if not partial:
|
|
135
|
+
missing = set(self.required) - props.keys()
|
|
136
|
+
if missing:
|
|
137
|
+
raise TypeError(f"{self.name} requires {', '.join(sorted(missing))}")
|
|
138
|
+
for key, value in props.items():
|
|
139
|
+
if key not in self.props:
|
|
140
|
+
raise TypeError(f"Unknown {self.name} prop {key!r}")
|
|
141
|
+
validate(value, self.props[key], f"{self.name}.{key}")
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
@dataclass(frozen=True)
|
|
145
|
+
class ModuleSchema:
|
|
146
|
+
"""Typed native module methods, arguments, results, and async behavior."""
|
|
147
|
+
|
|
148
|
+
name: str
|
|
149
|
+
methods: dict[str, dict[str, Any]]
|
|
150
|
+
|
|
151
|
+
@classmethod
|
|
152
|
+
def from_protocol(cls, name: str, protocol: type) -> ModuleSchema:
|
|
153
|
+
"""Read annotated methods from a Python protocol or interface class."""
|
|
154
|
+
methods = {}
|
|
155
|
+
for method_name, method in inspect.getmembers(protocol, inspect.isfunction):
|
|
156
|
+
if method_name.startswith("_"):
|
|
157
|
+
continue
|
|
158
|
+
hints = typing.get_type_hints(method, include_extras=True)
|
|
159
|
+
signature = inspect.signature(method)
|
|
160
|
+
methods[method_name] = {
|
|
161
|
+
"arguments": {key: type_schema(hints.get(key, Any)) for key in signature.parameters if key != "self"},
|
|
162
|
+
"result": type_schema(hints.get("return", Any)),
|
|
163
|
+
"async": inspect.iscoroutinefunction(method),
|
|
164
|
+
}
|
|
165
|
+
return cls(name, methods)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
COMPONENTS: dict[str, ComponentSchema] = {}
|
|
169
|
+
MODULES: dict[str, ModuleSchema] = {}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def register_schema(schema: ComponentSchema | ModuleSchema) -> None:
|
|
173
|
+
"""Register the contract used to generate and validate a native extension."""
|
|
174
|
+
if isinstance(schema, ComponentSchema):
|
|
175
|
+
COMPONENTS[schema.name] = schema
|
|
176
|
+
else:
|
|
177
|
+
MODULES[schema.name] = schema
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def manifest() -> dict[str, Any]:
|
|
181
|
+
"""Return deterministic native metadata for code generation and tooling."""
|
|
182
|
+
return {
|
|
183
|
+
"protocol": 2,
|
|
184
|
+
"yoga": "3.2.1",
|
|
185
|
+
"components": {name: dataclasses.asdict(value) for name, value in sorted(COMPONENTS.items())},
|
|
186
|
+
"modules": {name: dataclasses.asdict(value) for name, value in sorted(MODULES.items())},
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def fingerprint() -> str:
|
|
191
|
+
"""Hash the native contract; incompatible dev clients require rebuilding."""
|
|
192
|
+
return hashlib.sha256(json.dumps(manifest(), sort_keys=True, default=str).encode()).hexdigest()
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def load_manifest(document: Mapping[str, Any]) -> None:
|
|
196
|
+
"""Load declarative extension contracts without importing target binaries."""
|
|
197
|
+
if document.get("protocol") != 2 or document.get("yoga") != "3.2.1":
|
|
198
|
+
raise ValueError("Native contracts require protocol 2 and Yoga 3.2.1")
|
|
199
|
+
pending = []
|
|
200
|
+
for group, constructor, registered in (
|
|
201
|
+
("components", ComponentSchema, COMPONENTS),
|
|
202
|
+
("modules", ModuleSchema, MODULES),
|
|
203
|
+
):
|
|
204
|
+
for name, value in document.get(group, {}).items():
|
|
205
|
+
if not name.isidentifier() or value.get("name") != name:
|
|
206
|
+
raise ValueError(f"Invalid native contract name: {name!r}")
|
|
207
|
+
schema = constructor(**value)
|
|
208
|
+
old = registered.get(name)
|
|
209
|
+
if old is not None and json.dumps(dataclasses.asdict(old), sort_keys=True, default=str) != json.dumps(
|
|
210
|
+
dataclasses.asdict(schema), sort_keys=True, default=str
|
|
211
|
+
):
|
|
212
|
+
raise ValueError(f"Conflicting native contract: {name}")
|
|
213
|
+
pending.append(schema)
|
|
214
|
+
for schema in pending:
|
|
215
|
+
register_schema(schema)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def load_bundled_contracts() -> None:
|
|
219
|
+
"""Install the exact contracts compiled into this embedded application."""
|
|
220
|
+
from importlib.resources import files
|
|
221
|
+
|
|
222
|
+
path = files("pythonnative").joinpath("_native_contracts.json")
|
|
223
|
+
if path.is_file():
|
|
224
|
+
load_manifest(json.loads(path.read_text(encoding="utf-8")))
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
package com.pythonnative.generated
|
|
2
|
+
|
|
3
|
+
import org.json.JSONArray
|
|
4
|
+
import org.json.JSONObject
|
|
5
|
+
|
|
6
|
+
/** Generated contract metadata shared by built-ins and extension managers. */
|
|
7
|
+
object PNContracts {
|
|
8
|
+
const val fingerprint = "{{fingerprint}}"
|
|
9
|
+
private val components = JSONObject(listOf({{specification}}).joinToString("")).getJSONObject("components")
|
|
10
|
+
|
|
11
|
+
fun validate(name: String, props: JSONObject, partial: Boolean = false): Boolean {
|
|
12
|
+
val schema = components.optJSONObject(name) ?: return true
|
|
13
|
+
val fields = schema.getJSONObject("props")
|
|
14
|
+
val required = schema.optJSONArray("required") ?: JSONArray()
|
|
15
|
+
if (!partial) for (i in 0 until required.length()) if (!props.has(required.getString(i))) return false
|
|
16
|
+
for (key in props.keys()) {
|
|
17
|
+
if (partial && props.isNull(key)) continue
|
|
18
|
+
val field = fields.optJSONObject(key) ?: continue
|
|
19
|
+
if (!matches(props.get(key), field)) return false
|
|
20
|
+
}
|
|
21
|
+
return true
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
fun invalidatesLayout(name: String, changed: JSONObject): Boolean {
|
|
25
|
+
val fields = components.optJSONObject(name)?.optJSONObject("props") ?: return true
|
|
26
|
+
return changed.keys().asSequence().any { fields.optJSONObject(it)?.optJSONObject("native")?.optBoolean("invalidates_layout", true) ?: true }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private fun matches(value: Any, schema: JSONObject): Boolean {
|
|
30
|
+
schema.optJSONArray("anyOf")?.let { alternatives ->
|
|
31
|
+
return (0 until alternatives.length()).any { matches(value, alternatives.getJSONObject(it)) }
|
|
32
|
+
}
|
|
33
|
+
schema.optJSONArray("enum")?.let { values -> return (0 until values.length()).any { values.get(it).toString() == value.toString() } }
|
|
34
|
+
return when (schema.optString("type")) {
|
|
35
|
+
"null" -> value == JSONObject.NULL
|
|
36
|
+
"string" -> value is String
|
|
37
|
+
"boolean" -> value is Boolean
|
|
38
|
+
"integer" -> value is Number && value.toDouble() == value.toLong().toDouble()
|
|
39
|
+
"number" -> value is Number && value.toDouble().isFinite()
|
|
40
|
+
"array" -> value is JSONArray && (0 until value.length()).all { matches(value.get(it), schema.optJSONObject("items") ?: JSONObject()) }
|
|
41
|
+
"object" -> value is JSONObject
|
|
42
|
+
"event" -> value is Boolean || value == JSONObject.NULL
|
|
43
|
+
else -> true
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import CoreFoundation
|
|
3
|
+
|
|
4
|
+
/// Generated contract metadata; validation is shared by built-ins and plugins.
|
|
5
|
+
public enum PNContracts {
|
|
6
|
+
public static let fingerprint = "{{fingerprint}}"
|
|
7
|
+
private static let specification = """
|
|
8
|
+
{{specification}}
|
|
9
|
+
"""
|
|
10
|
+
private static let components = (try! JSONSerialization.jsonObject(with: Data(specification.utf8)) as! [String: Any])["components"] as! [String: [String: Any]]
|
|
11
|
+
|
|
12
|
+
public static func validate(_ name: String, _ props: [String: Any], partial: Bool = false) -> Bool {
|
|
13
|
+
guard let schema = components[name], let fields = schema["props"] as? [String: [String: Any]] else { return true }
|
|
14
|
+
if !partial {
|
|
15
|
+
for key in schema["required"] as? [String] ?? [] where props[key] == nil { return false }
|
|
16
|
+
}
|
|
17
|
+
for (key, value) in props {
|
|
18
|
+
if value is NSNull && partial { continue }
|
|
19
|
+
if let field = fields[key], !matches(value, field) { return false }
|
|
20
|
+
}
|
|
21
|
+
return true
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public static func invalidatesLayout(_ name: String, _ changed: [String: Any]) -> Bool {
|
|
25
|
+
guard let fields = components[name]?["props"] as? [String: [String: Any]] else { return true }
|
|
26
|
+
return changed.keys.contains { ((fields[$0]?["native"] as? [String: Any])?["invalidates_layout"] as? Bool) ?? true }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private static func matches(_ value: Any, _ schema: [String: Any]) -> Bool {
|
|
30
|
+
if let alternatives = schema["anyOf"] as? [[String: Any]] { return alternatives.contains { matches(value, $0) } }
|
|
31
|
+
if let values = schema["enum"] as? [Any] { return values.contains { String(describing: $0) == String(describing: value) } }
|
|
32
|
+
switch schema["type"] as? String {
|
|
33
|
+
case "null": return value is NSNull
|
|
34
|
+
case "string": return value is String
|
|
35
|
+
case "boolean": return (value as? NSNumber).map { CFGetTypeID($0) == CFBooleanGetTypeID() } ?? false
|
|
36
|
+
case "integer": return (value as? NSNumber).map { CFGetTypeID($0) != CFBooleanGetTypeID() && $0.doubleValue.rounded() == $0.doubleValue } ?? false
|
|
37
|
+
case "number": return (value as? NSNumber).map { CFGetTypeID($0) != CFBooleanGetTypeID() && $0.doubleValue.isFinite } ?? false
|
|
38
|
+
case "array":
|
|
39
|
+
guard let array = value as? [Any] else { return false }
|
|
40
|
+
return array.allSatisfy { matches($0, schema["items"] as? [String: Any] ?? [:]) }
|
|
41
|
+
case "object": return value is [String: Any]
|
|
42
|
+
case "event": return value is Bool || value is NSNull
|
|
43
|
+
default: return true
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|