pythonnative 0.40.0__cp314-cp314-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.cp314-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,773 @@
|
|
|
1
|
+
"""Typed model and loader for ``pythonnative.toml``.
|
|
2
|
+
|
|
3
|
+
Every PythonNative project is described by a single ``pythonnative.toml``
|
|
4
|
+
at its root. This module parses and validates that file into a typed
|
|
5
|
+
[`AppConfig`][pythonnative.project.config.AppConfig] tree that the rest
|
|
6
|
+
of the build system consumes. Keeping all parsing/validation here means
|
|
7
|
+
the configurators ([`ios`][pythonnative.project.ios],
|
|
8
|
+
[`android`][pythonnative.project.android]) and the
|
|
9
|
+
[`builder`][pythonnative.project.builder] can assume a fully-validated,
|
|
10
|
+
defaulted config object.
|
|
11
|
+
|
|
12
|
+
The canonical schema:
|
|
13
|
+
|
|
14
|
+
```toml
|
|
15
|
+
[app]
|
|
16
|
+
id = "com.example.myapp" # reverse-DNS app/bundle id (required)
|
|
17
|
+
name = "myapp" # short project name (required)
|
|
18
|
+
display_name = "My App" # home-screen label (defaults to name)
|
|
19
|
+
version = "1.0.0" # marketing version
|
|
20
|
+
build = 1 # integer build number
|
|
21
|
+
python_version = "3.13" # embedded CPython version (3.13 or 3.14)
|
|
22
|
+
orientation = "portrait" # portrait | landscape | all
|
|
23
|
+
entry_point = "app/main.py" # module whose `App` is mounted
|
|
24
|
+
url_schemes = ["myapp"] # custom deep-link URL schemes
|
|
25
|
+
|
|
26
|
+
[permissions] # see pythonnative.project.permissions
|
|
27
|
+
camera = "Scan receipts."
|
|
28
|
+
notifications = true
|
|
29
|
+
|
|
30
|
+
[assets]
|
|
31
|
+
icon = "assets/icon.png" # 1024x1024 source icon
|
|
32
|
+
splash = "assets/splash.png" # splash/launch image
|
|
33
|
+
|
|
34
|
+
[requirements]
|
|
35
|
+
packages = ["humanize", "httpx"]
|
|
36
|
+
# Extra package indexes searched after PyPI and the platform wheel
|
|
37
|
+
# repositories (BeeWare for iOS, Chaquopy for Android).
|
|
38
|
+
extra_index_urls = ["https://wheels.example.com/simple"]
|
|
39
|
+
|
|
40
|
+
[plugins]
|
|
41
|
+
paths = ["native/my_plugin"] # local native plugins (pn_plugin.json + ios/ + android/)
|
|
42
|
+
|
|
43
|
+
[ios]
|
|
44
|
+
deployment_target = "13.0"
|
|
45
|
+
development_team = "ABCDE12345"
|
|
46
|
+
bundle_id = "com.example.myapp" # optional override of app.id
|
|
47
|
+
|
|
48
|
+
[ios.signing]
|
|
49
|
+
export_method = "app-store" # development | ad-hoc | app-store | enterprise
|
|
50
|
+
provisioning_profile = "My App Distribution"
|
|
51
|
+
|
|
52
|
+
[android]
|
|
53
|
+
min_sdk = 24
|
|
54
|
+
target_sdk = 34
|
|
55
|
+
abi_filters = ["arm64-v8a", "x86_64"] # the default and the full set (64-bit only)
|
|
56
|
+
|
|
57
|
+
[android.signing]
|
|
58
|
+
keystore = "release.keystore"
|
|
59
|
+
key_alias = "myapp"
|
|
60
|
+
```
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
from __future__ import annotations
|
|
64
|
+
|
|
65
|
+
import re
|
|
66
|
+
import tomllib as _toml
|
|
67
|
+
from dataclasses import dataclass, field
|
|
68
|
+
from pathlib import Path
|
|
69
|
+
from typing import Any, Dict, List, Mapping, Optional
|
|
70
|
+
|
|
71
|
+
from . import permissions as _permissions
|
|
72
|
+
|
|
73
|
+
CONFIG_FILENAME = "pythonnative.toml"
|
|
74
|
+
"""The fixed config filename looked up at the project root."""
|
|
75
|
+
|
|
76
|
+
SUPPORTED_PYTHON_VERSIONS = ("3.13", "3.14")
|
|
77
|
+
"""CPython versions accepted in ``app.python_version``.
|
|
78
|
+
|
|
79
|
+
Every listed version has a pinned, checksum-verified iOS runtime (see
|
|
80
|
+
``runtime_assets.PINNED_ASSETS``) and is supported by Chaquopy on
|
|
81
|
+
Android. The floor is 3.13 because that is the first CPython with
|
|
82
|
+
official iOS (PEP 730) and Android (PEP 738) platform support, which is
|
|
83
|
+
what lets binary wheels from PyPI install on both platforms.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
DEFAULT_PYTHON_VERSION = "3.13"
|
|
87
|
+
"""The ``app.python_version`` used when the config leaves it out.
|
|
88
|
+
|
|
89
|
+
3.13 rather than 3.14 because binary wheel coverage (BeeWare's iOS
|
|
90
|
+
index, Chaquopy's Android index, PyPI itself) is far wider for 3.13.
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
_URL_RE = re.compile(r"^https?://\S+$")
|
|
94
|
+
|
|
95
|
+
VALID_ORIENTATIONS = ("portrait", "landscape", "all")
|
|
96
|
+
"""Accepted values for ``app.orientation``."""
|
|
97
|
+
|
|
98
|
+
MIN_IOS_DEPLOYMENT_TARGET = "13.0"
|
|
99
|
+
"""Lowest ``[ios].deployment_target`` accepted.
|
|
100
|
+
|
|
101
|
+
BeeWare's CPython builds and every iOS wheel on PyPI target iOS 13.0
|
|
102
|
+
(the ``ios_13_0_*`` platform tags), so lower values can't be satisfied.
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
MIN_ANDROID_SDK = 24
|
|
106
|
+
"""Lowest ``[android].min_sdk`` accepted (Chaquopy 17 requirement)."""
|
|
107
|
+
|
|
108
|
+
VALID_ANDROID_ABIS = ("arm64-v8a", "x86_64")
|
|
109
|
+
"""Android ABIs the embedded runtime supports.
|
|
110
|
+
|
|
111
|
+
CPython 3.12+ on Chaquopy ships 64-bit only, and PEP 738 Android wheels
|
|
112
|
+
on PyPI are published for these two ABIs.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
_IOS_VERSION_RE = re.compile(r"^\d+(\.\d+){1,2}$")
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _version_tuple(version: str) -> tuple:
|
|
119
|
+
return tuple(int(part) for part in version.split("."))
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
VALID_IOS_EXPORT_METHODS = ("development", "ad-hoc", "app-store", "enterprise")
|
|
123
|
+
"""Accepted values for ``[ios.signing].export_method``."""
|
|
124
|
+
|
|
125
|
+
# Reserved Java keywords can't appear as Android package segments.
|
|
126
|
+
_JAVA_KEYWORDS = frozenset("""
|
|
127
|
+
abstract assert boolean break byte case catch char class const continue default do double else enum
|
|
128
|
+
extends final finally float for goto if implements import instanceof int interface long native new
|
|
129
|
+
package private protected public return short static strictfp super switch synchronized this throw
|
|
130
|
+
throws transient try void volatile while true false null
|
|
131
|
+
""".split())
|
|
132
|
+
|
|
133
|
+
_APP_ID_SEGMENT = re.compile(r"^[a-z][a-z0-9_]*$")
|
|
134
|
+
_VERSION_RE = re.compile(r"^\d+(\.\d+){0,3}$")
|
|
135
|
+
_URL_SCHEME_RE = re.compile(r"^[a-zA-Z][a-zA-Z0-9+.-]*$")
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class ConfigError(Exception):
|
|
139
|
+
"""Raised when ``pythonnative.toml`` is missing, malformed, or invalid.
|
|
140
|
+
|
|
141
|
+
The message is intended to be shown directly to the user by the CLI,
|
|
142
|
+
so it should be specific and actionable.
|
|
143
|
+
"""
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
# ======================================================================
|
|
147
|
+
# Sub-models
|
|
148
|
+
# ======================================================================
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@dataclass
|
|
152
|
+
class IOSSigning:
|
|
153
|
+
"""iOS code-signing / export configuration (``[ios.signing]``).
|
|
154
|
+
|
|
155
|
+
Attributes:
|
|
156
|
+
export_method: How the archive is exported, one of
|
|
157
|
+
``development``, ``ad-hoc``, ``app-store``, ``enterprise``.
|
|
158
|
+
provisioning_profile: Optional provisioning profile name or UUID
|
|
159
|
+
for manual signing.
|
|
160
|
+
"""
|
|
161
|
+
|
|
162
|
+
export_method: str = "development"
|
|
163
|
+
provisioning_profile: Optional[str] = None
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@dataclass
|
|
167
|
+
class IOSConfig:
|
|
168
|
+
"""iOS-specific settings (``[ios]``).
|
|
169
|
+
|
|
170
|
+
Attributes:
|
|
171
|
+
deployment_target: Minimum iOS version (e.g., ``"13.0"``).
|
|
172
|
+
development_team: Apple Developer Team ID used for signing.
|
|
173
|
+
bundle_id: Optional override of ``app.id`` for the iOS bundle
|
|
174
|
+
identifier.
|
|
175
|
+
extra_info_plist: Arbitrary additional ``Info.plist`` key/values
|
|
176
|
+
merged verbatim into the generated plist.
|
|
177
|
+
signing: Nested [`IOSSigning`][pythonnative.project.config.IOSSigning].
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
deployment_target: str = "13.0"
|
|
181
|
+
development_team: Optional[str] = None
|
|
182
|
+
bundle_id: Optional[str] = None
|
|
183
|
+
extra_info_plist: Dict[str, Any] = field(default_factory=dict)
|
|
184
|
+
signing: IOSSigning = field(default_factory=IOSSigning)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@dataclass
|
|
188
|
+
class AndroidSigning:
|
|
189
|
+
"""Android release signing configuration (``[android.signing]``).
|
|
190
|
+
|
|
191
|
+
Passwords are never stored in the config; they're read from the
|
|
192
|
+
environment at build time (see ``store_password_env`` /
|
|
193
|
+
``key_password_env``).
|
|
194
|
+
|
|
195
|
+
Attributes:
|
|
196
|
+
keystore: Path to the release keystore, relative to the project
|
|
197
|
+
root.
|
|
198
|
+
key_alias: Key alias within the keystore.
|
|
199
|
+
store_password_env: Environment variable holding the keystore
|
|
200
|
+
password.
|
|
201
|
+
key_password_env: Environment variable holding the key password.
|
|
202
|
+
"""
|
|
203
|
+
|
|
204
|
+
keystore: Optional[str] = None
|
|
205
|
+
key_alias: Optional[str] = None
|
|
206
|
+
store_password_env: str = "PN_ANDROID_KEYSTORE_PASSWORD"
|
|
207
|
+
key_password_env: str = "PN_ANDROID_KEY_PASSWORD"
|
|
208
|
+
|
|
209
|
+
@property
|
|
210
|
+
def is_configured(self) -> bool:
|
|
211
|
+
"""Whether both a keystore path and key alias are present."""
|
|
212
|
+
return bool(self.keystore and self.key_alias)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
@dataclass
|
|
216
|
+
class AndroidConfig:
|
|
217
|
+
"""Android-specific settings (``[android]``).
|
|
218
|
+
|
|
219
|
+
Attributes:
|
|
220
|
+
min_sdk: Minimum supported Android API level.
|
|
221
|
+
target_sdk: Target Android API level.
|
|
222
|
+
compile_sdk: SDK level the project compiles against.
|
|
223
|
+
application_id: Optional override of ``app.id`` for the Android
|
|
224
|
+
application id (and package).
|
|
225
|
+
abi_filters: Native ABIs to include (e.g., ``"arm64-v8a"``).
|
|
226
|
+
permissions: Extra raw Android permission strings appended to the
|
|
227
|
+
ones derived from ``[permissions]``.
|
|
228
|
+
signing: Nested
|
|
229
|
+
[`AndroidSigning`][pythonnative.project.config.AndroidSigning].
|
|
230
|
+
"""
|
|
231
|
+
|
|
232
|
+
min_sdk: int = 24
|
|
233
|
+
target_sdk: int = 34
|
|
234
|
+
compile_sdk: int = 34
|
|
235
|
+
application_id: Optional[str] = None
|
|
236
|
+
# arm64 devices plus x86_64 emulators: the only ABIs CPython 3.12+
|
|
237
|
+
# on Chaquopy (and PEP 738 wheels on PyPI) are built for.
|
|
238
|
+
abi_filters: List[str] = field(default_factory=lambda: list(VALID_ANDROID_ABIS))
|
|
239
|
+
permissions: List[str] = field(default_factory=list)
|
|
240
|
+
signing: AndroidSigning = field(default_factory=AndroidSigning)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
# ======================================================================
|
|
244
|
+
# Root model
|
|
245
|
+
# ======================================================================
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
@dataclass
|
|
249
|
+
class AppConfig:
|
|
250
|
+
"""A fully-parsed, validated ``pythonnative.toml``.
|
|
251
|
+
|
|
252
|
+
Use [`load`][pythonnative.project.config.AppConfig.load] to read it
|
|
253
|
+
from a project directory, or
|
|
254
|
+
[`from_dict`][pythonnative.project.config.AppConfig.from_dict] to
|
|
255
|
+
build one from an already-parsed mapping (e.g., in tests).
|
|
256
|
+
|
|
257
|
+
Attributes:
|
|
258
|
+
app_id: Reverse-DNS identifier (``app.id``); the default bundle
|
|
259
|
+
id / application id for both platforms.
|
|
260
|
+
name: Short project name (``app.name``).
|
|
261
|
+
display_name: Home-screen label (``app.display_name``).
|
|
262
|
+
version: Marketing version string (``app.version``).
|
|
263
|
+
build: Integer build number (``app.build``).
|
|
264
|
+
python_version: Embedded CPython version (``app.python_version``).
|
|
265
|
+
orientation: ``"portrait"``, ``"landscape"``, or ``"all"``.
|
|
266
|
+
entry_point: Path to the entry module (``app.entry_point``).
|
|
267
|
+
url_schemes: Custom deep-link URL schemes the app handles
|
|
268
|
+
(``app.url_schemes``); wired into ``CFBundleURLTypes`` on
|
|
269
|
+
iOS and a ``VIEW`` intent filter on Android.
|
|
270
|
+
permissions: The declared capability map (``[permissions]``).
|
|
271
|
+
icon: Optional source icon path (``[assets].icon``).
|
|
272
|
+
splash: Optional splash image path (``[assets].splash``).
|
|
273
|
+
requirements: Third-party pip packages (``[requirements].packages``).
|
|
274
|
+
extra_index_urls: Additional package indexes
|
|
275
|
+
(``[requirements].extra_index_urls``) searched after PyPI and
|
|
276
|
+
the platform wheel repositories.
|
|
277
|
+
plugin_paths: Project-local native plugin directories
|
|
278
|
+
(``[plugins].paths``); installed packages contribute plugins
|
|
279
|
+
through the ``pythonnative.plugins`` entry point group instead.
|
|
280
|
+
ios: Nested [`IOSConfig`][pythonnative.project.config.IOSConfig].
|
|
281
|
+
android: Nested
|
|
282
|
+
[`AndroidConfig`][pythonnative.project.config.AndroidConfig].
|
|
283
|
+
project_root: Absolute path to the directory containing the config.
|
|
284
|
+
"""
|
|
285
|
+
|
|
286
|
+
app_id: str
|
|
287
|
+
name: str
|
|
288
|
+
display_name: str
|
|
289
|
+
version: str = "1.0.0"
|
|
290
|
+
build: int = 1
|
|
291
|
+
python_version: str = DEFAULT_PYTHON_VERSION
|
|
292
|
+
orientation: str = "portrait"
|
|
293
|
+
entry_point: str = "app/main.py"
|
|
294
|
+
url_schemes: List[str] = field(default_factory=list)
|
|
295
|
+
permissions: Dict[str, _permissions.PermissionValue] = field(default_factory=dict)
|
|
296
|
+
icon: Optional[str] = None
|
|
297
|
+
splash: Optional[str] = None
|
|
298
|
+
requirements: List[str] = field(default_factory=list)
|
|
299
|
+
extra_index_urls: List[str] = field(default_factory=list)
|
|
300
|
+
plugin_paths: List[str] = field(default_factory=list)
|
|
301
|
+
ios: IOSConfig = field(default_factory=IOSConfig)
|
|
302
|
+
android: AndroidConfig = field(default_factory=AndroidConfig)
|
|
303
|
+
project_root: Path = field(default_factory=Path.cwd)
|
|
304
|
+
|
|
305
|
+
# -- Derived values -------------------------------------------------
|
|
306
|
+
|
|
307
|
+
@property
|
|
308
|
+
def application_id(self) -> str:
|
|
309
|
+
"""The Android application id (``[android].application_id`` or ``app.id``)."""
|
|
310
|
+
return self.android.application_id or self.app_id
|
|
311
|
+
|
|
312
|
+
@property
|
|
313
|
+
def bundle_id(self) -> str:
|
|
314
|
+
"""The iOS bundle identifier (``[ios].bundle_id`` or ``app.id``)."""
|
|
315
|
+
return self.ios.bundle_id or self.app_id
|
|
316
|
+
|
|
317
|
+
@property
|
|
318
|
+
def entry_module(self) -> str:
|
|
319
|
+
"""The dotted import path for ``entry_point`` (e.g., ``"app.main"``)."""
|
|
320
|
+
return entrypoint_to_module(self.entry_point)
|
|
321
|
+
|
|
322
|
+
@property
|
|
323
|
+
def android_package_path(self) -> str:
|
|
324
|
+
"""The Android source directory path for ``application_id``.
|
|
325
|
+
|
|
326
|
+
``"com.example.myapp"`` → ``"com/example/myapp"``.
|
|
327
|
+
"""
|
|
328
|
+
return self.application_id.replace(".", "/")
|
|
329
|
+
|
|
330
|
+
def resolve_path(self, relative: str) -> Path:
|
|
331
|
+
"""Resolve a config-relative path against the project root.
|
|
332
|
+
|
|
333
|
+
Args:
|
|
334
|
+
relative: A path string from the config (e.g., an icon path).
|
|
335
|
+
|
|
336
|
+
Returns:
|
|
337
|
+
An absolute [`Path`][pathlib.Path].
|
|
338
|
+
"""
|
|
339
|
+
candidate = Path(relative)
|
|
340
|
+
if candidate.is_absolute():
|
|
341
|
+
return candidate
|
|
342
|
+
return (self.project_root / candidate).resolve()
|
|
343
|
+
|
|
344
|
+
def resolved_permissions(self) -> _permissions.ResolvedPermissions:
|
|
345
|
+
"""Resolve declared capabilities into native permission artifacts.
|
|
346
|
+
|
|
347
|
+
Returns:
|
|
348
|
+
A
|
|
349
|
+
[`ResolvedPermissions`][pythonnative.project.permissions.ResolvedPermissions]
|
|
350
|
+
combining ``[permissions]`` with any extra
|
|
351
|
+
``[android].permissions``.
|
|
352
|
+
"""
|
|
353
|
+
return _permissions.resolve_permissions(
|
|
354
|
+
self.permissions,
|
|
355
|
+
extra_android_permissions=self.android.permissions,
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
# -- Construction ---------------------------------------------------
|
|
359
|
+
|
|
360
|
+
@classmethod
|
|
361
|
+
def load(cls, project_root: Optional[Path] = None) -> "AppConfig":
|
|
362
|
+
"""Load and validate ``pythonnative.toml`` from a project directory.
|
|
363
|
+
|
|
364
|
+
Args:
|
|
365
|
+
project_root: Directory containing the config. Defaults to the
|
|
366
|
+
current working directory.
|
|
367
|
+
|
|
368
|
+
Returns:
|
|
369
|
+
A validated [`AppConfig`][pythonnative.project.config.AppConfig].
|
|
370
|
+
|
|
371
|
+
Raises:
|
|
372
|
+
ConfigError: If the file is missing, isn't valid TOML, or
|
|
373
|
+
fails validation.
|
|
374
|
+
"""
|
|
375
|
+
root = Path(project_root).resolve() if project_root is not None else Path.cwd()
|
|
376
|
+
config_path = root / CONFIG_FILENAME
|
|
377
|
+
if not config_path.is_file():
|
|
378
|
+
raise ConfigError(f"No {CONFIG_FILENAME} found in {root}.")
|
|
379
|
+
try:
|
|
380
|
+
with open(config_path, "rb") as handle:
|
|
381
|
+
data = _toml.load(handle)
|
|
382
|
+
except _toml.TOMLDecodeError as exc:
|
|
383
|
+
raise ConfigError(f"Could not parse {CONFIG_FILENAME}: {exc}") from exc
|
|
384
|
+
return cls.from_dict(data, project_root=root)
|
|
385
|
+
|
|
386
|
+
@classmethod
|
|
387
|
+
def from_dict(cls, data: Mapping[str, Any], *, project_root: Optional[Path] = None) -> "AppConfig":
|
|
388
|
+
"""Build an [`AppConfig`][pythonnative.project.config.AppConfig] from a mapping.
|
|
389
|
+
|
|
390
|
+
Args:
|
|
391
|
+
data: A parsed TOML mapping (top-level tables: ``app``,
|
|
392
|
+
``permissions``, ``assets``, ``requirements``, ``plugins``,
|
|
393
|
+
``ios``, ``android``).
|
|
394
|
+
project_root: Directory the config came from, used to resolve
|
|
395
|
+
relative paths.
|
|
396
|
+
|
|
397
|
+
Returns:
|
|
398
|
+
A validated config.
|
|
399
|
+
|
|
400
|
+
Raises:
|
|
401
|
+
ConfigError: On any structural or value validation failure.
|
|
402
|
+
"""
|
|
403
|
+
root = Path(project_root).resolve() if project_root is not None else Path.cwd()
|
|
404
|
+
app = _expect_table(data, "app")
|
|
405
|
+
|
|
406
|
+
config = cls(
|
|
407
|
+
app_id=_require_str(app, "id", "app"),
|
|
408
|
+
name=_require_str(app, "name", "app"),
|
|
409
|
+
display_name=_opt_str(app, "display_name") or _require_str(app, "name", "app"),
|
|
410
|
+
version=_opt_str(app, "version") or "1.0.0",
|
|
411
|
+
build=_opt_int(app, "build", default=1),
|
|
412
|
+
python_version=_opt_str(app, "python_version") or DEFAULT_PYTHON_VERSION,
|
|
413
|
+
orientation=(_opt_str(app, "orientation") or "portrait").lower(),
|
|
414
|
+
entry_point=_opt_str(app, "entry_point") or "app/main.py",
|
|
415
|
+
url_schemes=_opt_str_list(app, "url_schemes"),
|
|
416
|
+
permissions=dict(_expect_table(data, "permissions", optional=True)),
|
|
417
|
+
ios=_parse_ios(_expect_table(data, "ios", optional=True)),
|
|
418
|
+
android=_parse_android(_expect_table(data, "android", optional=True)),
|
|
419
|
+
project_root=root,
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
assets = _expect_table(data, "assets", optional=True)
|
|
423
|
+
config.icon = _opt_str(assets, "icon")
|
|
424
|
+
config.splash = _opt_str(assets, "splash")
|
|
425
|
+
|
|
426
|
+
requirements = _expect_table(data, "requirements", optional=True)
|
|
427
|
+
config.requirements = _opt_str_list(requirements, "packages")
|
|
428
|
+
config.extra_index_urls = _opt_str_list(requirements, "extra_index_urls")
|
|
429
|
+
|
|
430
|
+
plugins = _expect_table(data, "plugins", optional=True)
|
|
431
|
+
config.plugin_paths = _opt_str_list(plugins, "paths")
|
|
432
|
+
|
|
433
|
+
config.validate()
|
|
434
|
+
return config
|
|
435
|
+
|
|
436
|
+
# -- Validation -----------------------------------------------------
|
|
437
|
+
|
|
438
|
+
def validate(self) -> None:
|
|
439
|
+
"""Validate all fields, raising [`ConfigError`][pythonnative.project.config.ConfigError] on first problem."""
|
|
440
|
+
_validate_app_id(self.app_id)
|
|
441
|
+
if self.android.application_id:
|
|
442
|
+
_validate_app_id(self.android.application_id)
|
|
443
|
+
if self.ios.bundle_id:
|
|
444
|
+
_validate_app_id(self.ios.bundle_id, allow_hyphen=True)
|
|
445
|
+
|
|
446
|
+
if not self.name.strip():
|
|
447
|
+
raise ConfigError("app.name must not be empty.")
|
|
448
|
+
|
|
449
|
+
if not _VERSION_RE.match(self.version):
|
|
450
|
+
raise ConfigError(f"app.version {self.version!r} must be a dotted number like '1.0.0'.")
|
|
451
|
+
|
|
452
|
+
if self.build < 1:
|
|
453
|
+
raise ConfigError("app.build must be a positive integer.")
|
|
454
|
+
|
|
455
|
+
if self.python_version not in SUPPORTED_PYTHON_VERSIONS:
|
|
456
|
+
supported = ", ".join(SUPPORTED_PYTHON_VERSIONS)
|
|
457
|
+
raise ConfigError(
|
|
458
|
+
f"app.python_version {self.python_version!r} is unsupported (choose one of: {supported})."
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
if self.orientation not in VALID_ORIENTATIONS:
|
|
462
|
+
valid = ", ".join(VALID_ORIENTATIONS)
|
|
463
|
+
raise ConfigError(f"app.orientation {self.orientation!r} is invalid (choose one of: {valid}).")
|
|
464
|
+
|
|
465
|
+
if not self.entry_point.strip():
|
|
466
|
+
raise ConfigError("app.entry_point must not be empty.")
|
|
467
|
+
|
|
468
|
+
for scheme in self.url_schemes:
|
|
469
|
+
if not _URL_SCHEME_RE.match(scheme):
|
|
470
|
+
raise ConfigError(
|
|
471
|
+
f"app.url_schemes entry {scheme!r} is invalid; schemes must start with a letter "
|
|
472
|
+
"and contain only letters, digits, '+', '-', and '.'."
|
|
473
|
+
)
|
|
474
|
+
|
|
475
|
+
unknown = _permissions.unknown_capabilities(self.permissions.keys())
|
|
476
|
+
if unknown:
|
|
477
|
+
known = ", ".join(sorted(_permissions.CAPABILITIES))
|
|
478
|
+
raise ConfigError("Unknown permission(s): " + ", ".join(unknown) + f".\nValid capabilities: {known}")
|
|
479
|
+
|
|
480
|
+
_validate_requirements(self.requirements)
|
|
481
|
+
for url in self.extra_index_urls:
|
|
482
|
+
if not _URL_RE.match(url):
|
|
483
|
+
raise ConfigError(
|
|
484
|
+
f"[requirements].extra_index_urls entry {url!r} is invalid; use a full http(s) URL "
|
|
485
|
+
"to a PEP 503 simple index (e.g. 'https://wheels.example.com/simple')."
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
if self.ios.signing.export_method not in VALID_IOS_EXPORT_METHODS:
|
|
489
|
+
valid = ", ".join(VALID_IOS_EXPORT_METHODS)
|
|
490
|
+
raise ConfigError(
|
|
491
|
+
f"[ios.signing].export_method {self.ios.signing.export_method!r} is invalid (choose one of: {valid})."
|
|
492
|
+
)
|
|
493
|
+
if not _IOS_VERSION_RE.match(self.ios.deployment_target):
|
|
494
|
+
raise ConfigError(
|
|
495
|
+
f"[ios].deployment_target {self.ios.deployment_target!r} must look like '13.0' or '16.4'."
|
|
496
|
+
)
|
|
497
|
+
if _version_tuple(self.ios.deployment_target) < _version_tuple(MIN_IOS_DEPLOYMENT_TARGET):
|
|
498
|
+
raise ConfigError(
|
|
499
|
+
f"[ios].deployment_target must be at least {MIN_IOS_DEPLOYMENT_TARGET} "
|
|
500
|
+
"(the embedded CPython runtime and iOS binary wheels target that floor)."
|
|
501
|
+
)
|
|
502
|
+
|
|
503
|
+
if self.android.min_sdk < MIN_ANDROID_SDK:
|
|
504
|
+
raise ConfigError(f"[android].min_sdk must be at least {MIN_ANDROID_SDK} (Chaquopy requirement).")
|
|
505
|
+
if self.android.target_sdk < self.android.min_sdk:
|
|
506
|
+
raise ConfigError("[android].target_sdk must be >= min_sdk.")
|
|
507
|
+
for abi in self.android.abi_filters:
|
|
508
|
+
if abi not in VALID_ANDROID_ABIS:
|
|
509
|
+
valid = ", ".join(VALID_ANDROID_ABIS)
|
|
510
|
+
raise ConfigError(f"[android].abi_filters entry {abi!r} is invalid (choose from: {valid}).")
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
# ======================================================================
|
|
514
|
+
# Parsing helpers
|
|
515
|
+
# ======================================================================
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
def _parse_ios(table: Mapping[str, Any]) -> IOSConfig:
|
|
519
|
+
signing_table = _expect_table(table, "signing", optional=True, parent="ios")
|
|
520
|
+
extra = table.get("extra_info_plist") or {}
|
|
521
|
+
if extra and not isinstance(extra, dict):
|
|
522
|
+
raise ConfigError("[ios].extra_info_plist must be a table.")
|
|
523
|
+
return IOSConfig(
|
|
524
|
+
deployment_target=_opt_str(table, "deployment_target") or "13.0",
|
|
525
|
+
development_team=_opt_str(table, "development_team"),
|
|
526
|
+
bundle_id=_opt_str(table, "bundle_id"),
|
|
527
|
+
extra_info_plist=dict(extra),
|
|
528
|
+
signing=IOSSigning(
|
|
529
|
+
export_method=(_opt_str(signing_table, "export_method") or "development").lower(),
|
|
530
|
+
provisioning_profile=_opt_str(signing_table, "provisioning_profile"),
|
|
531
|
+
),
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
def _parse_android(table: Mapping[str, Any]) -> AndroidConfig:
|
|
536
|
+
signing_table = _expect_table(table, "signing", optional=True, parent="android")
|
|
537
|
+
cfg = AndroidConfig(
|
|
538
|
+
min_sdk=_opt_int(table, "min_sdk", default=24),
|
|
539
|
+
target_sdk=_opt_int(table, "target_sdk", default=34),
|
|
540
|
+
compile_sdk=_opt_int(table, "compile_sdk", default=34),
|
|
541
|
+
application_id=_opt_str(table, "application_id"),
|
|
542
|
+
permissions=_opt_str_list(table, "permissions"),
|
|
543
|
+
signing=AndroidSigning(
|
|
544
|
+
keystore=_opt_str(signing_table, "keystore"),
|
|
545
|
+
key_alias=_opt_str(signing_table, "key_alias"),
|
|
546
|
+
store_password_env=_opt_str(signing_table, "store_password_env") or "PN_ANDROID_KEYSTORE_PASSWORD",
|
|
547
|
+
key_password_env=_opt_str(signing_table, "key_password_env") or "PN_ANDROID_KEY_PASSWORD",
|
|
548
|
+
),
|
|
549
|
+
)
|
|
550
|
+
abis = _opt_str_list(table, "abi_filters")
|
|
551
|
+
if abis:
|
|
552
|
+
cfg.abi_filters = abis
|
|
553
|
+
return cfg
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
def _expect_table(
|
|
557
|
+
data: Mapping[str, Any], key: str, *, optional: bool = False, parent: Optional[str] = None
|
|
558
|
+
) -> Mapping[str, Any]:
|
|
559
|
+
label = f"[{parent}.{key}]" if parent else f"[{key}]"
|
|
560
|
+
if key not in data:
|
|
561
|
+
if optional:
|
|
562
|
+
return {}
|
|
563
|
+
raise ConfigError(f"Missing required {label} table in {CONFIG_FILENAME}.")
|
|
564
|
+
value = data[key]
|
|
565
|
+
if not isinstance(value, Mapping):
|
|
566
|
+
raise ConfigError(f"{label} must be a table.")
|
|
567
|
+
return value
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
def _require_str(table: Mapping[str, Any], key: str, parent: str) -> str:
|
|
571
|
+
if key not in table:
|
|
572
|
+
raise ConfigError(f"Missing required '{key}' in [{parent}] ({CONFIG_FILENAME}).")
|
|
573
|
+
value = table[key]
|
|
574
|
+
if not isinstance(value, str) or not value.strip():
|
|
575
|
+
raise ConfigError(f"[{parent}].{key} must be a non-empty string.")
|
|
576
|
+
return value.strip()
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
def _opt_str(table: Mapping[str, Any], key: str) -> Optional[str]:
|
|
580
|
+
if key not in table:
|
|
581
|
+
return None
|
|
582
|
+
value = table[key]
|
|
583
|
+
if value is None:
|
|
584
|
+
return None
|
|
585
|
+
if not isinstance(value, str):
|
|
586
|
+
raise ConfigError(f"'{key}' must be a string (got {type(value).__name__}).")
|
|
587
|
+
return value.strip() or None
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
def _opt_int(table: Mapping[str, Any], key: str, *, default: int) -> int:
|
|
591
|
+
if key not in table:
|
|
592
|
+
return default
|
|
593
|
+
value = table[key]
|
|
594
|
+
if isinstance(value, bool) or not isinstance(value, int):
|
|
595
|
+
raise ConfigError(f"'{key}' must be an integer (got {type(value).__name__}).")
|
|
596
|
+
return value
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
def _opt_str_list(table: Mapping[str, Any], key: str) -> List[str]:
|
|
600
|
+
if key not in table:
|
|
601
|
+
return []
|
|
602
|
+
value = table[key]
|
|
603
|
+
if not isinstance(value, list) or not all(isinstance(item, str) for item in value):
|
|
604
|
+
raise ConfigError(f"'{key}' must be a list of strings.")
|
|
605
|
+
return [item.strip() for item in value if item.strip()]
|
|
606
|
+
|
|
607
|
+
|
|
608
|
+
def _validate_app_id(app_id: str, *, allow_hyphen: bool = False) -> None:
|
|
609
|
+
segments = app_id.split(".")
|
|
610
|
+
if len(segments) < 2:
|
|
611
|
+
raise ConfigError(f"app id {app_id!r} must be reverse-DNS with at least two segments (e.g. 'com.example.app').")
|
|
612
|
+
for segment in segments:
|
|
613
|
+
normalized = segment.replace("-", "_") if allow_hyphen else segment
|
|
614
|
+
if not _APP_ID_SEGMENT.match(normalized):
|
|
615
|
+
raise ConfigError(
|
|
616
|
+
f"app id segment {segment!r} is invalid; use lowercase letters, digits and underscores, "
|
|
617
|
+
"and start each segment with a letter."
|
|
618
|
+
)
|
|
619
|
+
if normalized in _JAVA_KEYWORDS:
|
|
620
|
+
raise ConfigError(f"app id segment {segment!r} is a reserved word and cannot be used.")
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
def _validate_requirements(requirements: List[str]) -> None:
|
|
624
|
+
for spec in requirements:
|
|
625
|
+
pkg = re.split(r"[\[><=!;~ ]", spec, maxsplit=1)[0].strip()
|
|
626
|
+
if pkg.lower().replace("-", "_") == "pythonnative":
|
|
627
|
+
raise ConfigError(
|
|
628
|
+
"Do not list 'pythonnative' in [requirements].packages; the pn CLI bundles it automatically."
|
|
629
|
+
)
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
# ======================================================================
|
|
633
|
+
# Misc utilities
|
|
634
|
+
# ======================================================================
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
def entrypoint_to_module(entry_point: str) -> str:
|
|
638
|
+
"""Convert an ``entry_point`` path into an importable module path.
|
|
639
|
+
|
|
640
|
+
``"app/main.py"`` → ``"app.main"``. Returns ``"app.main"`` for empty
|
|
641
|
+
or unusable input so callers always have a sane default.
|
|
642
|
+
|
|
643
|
+
Args:
|
|
644
|
+
entry_point: A path like ``"app/main.py"``.
|
|
645
|
+
|
|
646
|
+
Returns:
|
|
647
|
+
A dotted module path.
|
|
648
|
+
"""
|
|
649
|
+
normalized = (entry_point or "").strip().replace("\\", "/")
|
|
650
|
+
if normalized.endswith(".py"):
|
|
651
|
+
normalized = normalized[:-3]
|
|
652
|
+
normalized = normalized.strip("/").replace("/", ".")
|
|
653
|
+
return normalized or "app.main"
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
# TOML v1.0.0 basic strings must escape the quotation mark, the backslash,
|
|
657
|
+
# and every control character except tab: U+0000-U+0008, U+000A-U+001F, and
|
|
658
|
+
# U+007F. Tab is legal raw, and U+000B has no compact escape, so anything
|
|
659
|
+
# without one falls through to \uXXXX.
|
|
660
|
+
_TOML_COMPACT_ESCAPES = {
|
|
661
|
+
"\\": "\\\\",
|
|
662
|
+
'"': '\\"',
|
|
663
|
+
"\b": "\\b",
|
|
664
|
+
"\f": "\\f",
|
|
665
|
+
"\n": "\\n",
|
|
666
|
+
"\r": "\\r",
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
def _toml_escape(value: str) -> str:
|
|
671
|
+
"""Escape ``value`` for use inside a TOML basic string.
|
|
672
|
+
|
|
673
|
+
Applied at the render boundary rather than relying on the caller,
|
|
674
|
+
since this module is public API and reachable without ``pn init``'s
|
|
675
|
+
name validation in front of it.
|
|
676
|
+
|
|
677
|
+
Args:
|
|
678
|
+
value: The raw string to embed between double quotes.
|
|
679
|
+
|
|
680
|
+
Returns:
|
|
681
|
+
The escaped text, without the surrounding quotes.
|
|
682
|
+
"""
|
|
683
|
+
out = []
|
|
684
|
+
for char in value:
|
|
685
|
+
escaped = _TOML_COMPACT_ESCAPES.get(char)
|
|
686
|
+
if escaped is not None:
|
|
687
|
+
out.append(escaped)
|
|
688
|
+
elif char != "\t" and (char < "\x20" or char == "\x7f"):
|
|
689
|
+
out.append(f"\\u{ord(char):04X}")
|
|
690
|
+
else:
|
|
691
|
+
out.append(char)
|
|
692
|
+
return "".join(out)
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
def render_default_toml(*, name: str, app_id: str, python_version: str = DEFAULT_PYTHON_VERSION) -> str:
|
|
696
|
+
"""Render a starter ``pythonnative.toml`` for ``pn init``.
|
|
697
|
+
|
|
698
|
+
Every interpolated value is escaped for a TOML basic string, so a
|
|
699
|
+
name containing a quote, a backslash, or a control character still
|
|
700
|
+
produces a parseable file.
|
|
701
|
+
|
|
702
|
+
Args:
|
|
703
|
+
name: Project name.
|
|
704
|
+
app_id: Reverse-DNS app identifier.
|
|
705
|
+
python_version: Embedded CPython version.
|
|
706
|
+
|
|
707
|
+
Returns:
|
|
708
|
+
The TOML file contents as a string, with commented-out examples
|
|
709
|
+
for the optional tables.
|
|
710
|
+
"""
|
|
711
|
+
display = name.replace("_", " ").replace("-", " ").strip().title() or name
|
|
712
|
+
name = _toml_escape(name)
|
|
713
|
+
display = _toml_escape(display)
|
|
714
|
+
app_id = _toml_escape(app_id)
|
|
715
|
+
python_version = _toml_escape(python_version)
|
|
716
|
+
return f"""# PythonNative project configuration.
|
|
717
|
+
# Docs: https://pythonnative.com/guides/configuration/
|
|
718
|
+
|
|
719
|
+
[app]
|
|
720
|
+
id = "{app_id}"
|
|
721
|
+
name = "{name}"
|
|
722
|
+
display_name = "{display}"
|
|
723
|
+
version = "1.0.0"
|
|
724
|
+
build = 1
|
|
725
|
+
python_version = "{python_version}"
|
|
726
|
+
orientation = "portrait" # portrait | landscape | all
|
|
727
|
+
entry_point = "app/main.py"
|
|
728
|
+
# Custom URL schemes for deep links (e.g. "{name}" opens {name}://...).
|
|
729
|
+
# url_schemes = ["{name}"]
|
|
730
|
+
|
|
731
|
+
# Declare the device capabilities your app needs. A string becomes the
|
|
732
|
+
# iOS permission prompt text; `true` uses a sensible default.
|
|
733
|
+
# See: https://pythonnative.com/guides/permissions/
|
|
734
|
+
[permissions]
|
|
735
|
+
# camera = "Scan receipts with your camera."
|
|
736
|
+
# location_when_in_use = "Show nearby stores."
|
|
737
|
+
# notifications = true
|
|
738
|
+
# face_id = "Unlock the app with Face ID."
|
|
739
|
+
|
|
740
|
+
# App icon and splash. Provide a 1024x1024 PNG icon; it is resized for
|
|
741
|
+
# every density/idiom automatically.
|
|
742
|
+
[assets]
|
|
743
|
+
# icon = "assets/icon.png"
|
|
744
|
+
# splash = "assets/splash.png"
|
|
745
|
+
|
|
746
|
+
# Third-party pip packages bundled into the app. Pure-Python packages
|
|
747
|
+
# always work; packages with C extensions need a wheel for each device
|
|
748
|
+
# platform. Run `pn deps` to see what resolves before you build.
|
|
749
|
+
# Do NOT list "pythonnative". Docs: https://pythonnative.com/guides/pypi-packages/
|
|
750
|
+
[requirements]
|
|
751
|
+
packages = []
|
|
752
|
+
# extra_index_urls = ["https://wheels.example.com/simple"]
|
|
753
|
+
|
|
754
|
+
[ios]
|
|
755
|
+
deployment_target = "{MIN_IOS_DEPLOYMENT_TARGET}"
|
|
756
|
+
# development_team = "ABCDE12345"
|
|
757
|
+
# bundle_id = "{app_id}"
|
|
758
|
+
|
|
759
|
+
[ios.signing]
|
|
760
|
+
export_method = "development" # development | ad-hoc | app-store | enterprise
|
|
761
|
+
# provisioning_profile = "My App Distribution"
|
|
762
|
+
|
|
763
|
+
[android]
|
|
764
|
+
min_sdk = {MIN_ANDROID_SDK}
|
|
765
|
+
target_sdk = 34
|
|
766
|
+
# arm64 devices + x86_64 emulators. These are the only ABIs the embedded
|
|
767
|
+
# CPython (3.12+) and Android wheels on PyPI ship for.
|
|
768
|
+
# abi_filters = ["arm64-v8a", "x86_64"]
|
|
769
|
+
|
|
770
|
+
[android.signing]
|
|
771
|
+
# keystore = "release.keystore"
|
|
772
|
+
# key_alias = "{name}"
|
|
773
|
+
"""
|