pythonnative 0.31.0__tar.gz → 0.33.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pythonnative-0.31.0/src/pythonnative.egg-info → pythonnative-0.33.0}/PKG-INFO +1 -21
- {pythonnative-0.31.0 → pythonnative-0.33.0}/pyproject.toml +24 -15
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/__init__.py +1 -1
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/cli/pn.py +58 -4
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/net.py +4 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/platform_metrics.py +8 -5
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/project/config.py +47 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0/src/pythonnative.egg-info}/PKG-INFO +1 -21
- pythonnative-0.33.0/src/pythonnative.egg-info/requires.txt +10 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_cli.py +102 -16
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_net.py +11 -0
- pythonnative-0.31.0/src/pythonnative.egg-info/requires.txt +0 -33
- {pythonnative-0.31.0 → pythonnative-0.33.0}/LICENSE +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/README.md +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/setup.cfg +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/_ios_log.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/alerts.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/animated.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/appearance.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/cli/__init__.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/components.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/diagnostics.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/element.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/events.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/gestures.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/hooks.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/hot_reload.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/images.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/layout.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/mutations.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/__init__.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/app_state.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/battery.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/biometrics.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/camera.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/clipboard.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/file_system.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/haptics.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/linking.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/location.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/net_info.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/notifications.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/permissions.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/secure_store.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/share.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_views/__init__.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_views/android.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_views/base.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_views/desktop.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_views/ios.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/navigation.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/platform.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/preview.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/project/__init__.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/project/android.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/project/builder.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/project/devices.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/project/doctor.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/project/icons.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/project/ios.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/project/permissions.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/project/runtime_assets.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/reconciler.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/runtime.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/screen.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/sdk/__init__.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/sdk/_components.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/storage.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/style.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/suspense.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/build.gradle +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/proguard-rules.pro +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/androidTest/java/com/pythonnative/android_template/ExampleInstrumentedTest.kt +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/AndroidManifest.xml +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/Navigator.kt +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PNAccessibilityDelegate.kt +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PNBorderDrawable.kt +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PNFrameLayout.kt +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PNVirtualListView.java +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/ScreenFragment.kt +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/drawable/ic_launcher_background.xml +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/layout/activity_main.xml +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/navigation/nav_graph.xml +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/values/colors.xml +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/values/strings.xml +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/values/themes.xml +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/values-night/themes.xml +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/xml/backup_rules.xml +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/xml/data_extraction_rules.xml +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/test/java/com/pythonnative/android_template/ExampleUnitTest.kt +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/build.gradle +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.jar +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.properties +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/gradle.properties +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/gradlew +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/gradlew.bat +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/settings.gradle +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_template/AppDelegate.swift +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/AccentColor.colorset/Contents.json +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/Contents.json +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_template/Base.lproj/LaunchScreen.storyboard +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_template/BridgingHeader.h +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_template/Info.plist +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_template/PythonRuntime.swift +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_template/SceneDelegate.swift +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_template/ViewController.swift +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_template.xcodeproj/project.pbxproj +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_template.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_templateTests/ios_templateTests.swift +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITests.swift +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITestsLaunchTests.swift +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/utils.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/virtual_rows.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative.egg-info/SOURCES.txt +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative.egg-info/dependency_links.txt +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative.egg-info/entry_points.txt +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative.egg-info/top_level.txt +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_alert.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_animated.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_animated_graph.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_appearance.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_async_hooks.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_components.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_core_semantics.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_desktop_backend.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_element.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_events.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_extended_components.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_gesture_composition.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_gestures.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_hooks.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_hot_reload.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_images.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_incremental_render.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_interaction_props.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_ios_log.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_layout.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_metric_hooks.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_mutations.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_native_lists.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_native_modules.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_native_views.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_navigation.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_new_components.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_platform.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_platform_metrics.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_reconciler.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_ref.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_runtime.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_screen.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_sdk.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_smoke.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_storage.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_style.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_suspense.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_transitions.py +0 -0
- {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pythonnative
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.33.0
|
|
4
4
|
Summary: Cross-platform native UI toolkit for Android and iOS
|
|
5
5
|
Author: Owen Carey
|
|
6
6
|
License: MIT License
|
|
@@ -47,26 +47,6 @@ Provides-Extra: ios
|
|
|
47
47
|
Requires-Dist: rubicon-objc<0.5.0,>=0.4.6; extra == "ios"
|
|
48
48
|
Provides-Extra: build
|
|
49
49
|
Requires-Dist: Pillow>=10.0; extra == "build"
|
|
50
|
-
Provides-Extra: docs
|
|
51
|
-
Requires-Dist: mkdocs>=1.5; extra == "docs"
|
|
52
|
-
Requires-Dist: mkdocs-material[imaging]>=9.5; extra == "docs"
|
|
53
|
-
Requires-Dist: mkdocstrings[python]>=0.24; extra == "docs"
|
|
54
|
-
Requires-Dist: mkdocs-autorefs>=1.0; extra == "docs"
|
|
55
|
-
Requires-Dist: pymdown-extensions>=10.7; extra == "docs"
|
|
56
|
-
Provides-Extra: dev
|
|
57
|
-
Requires-Dist: black>=24.0; extra == "dev"
|
|
58
|
-
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
59
|
-
Requires-Dist: mypy>=1.10; extra == "dev"
|
|
60
|
-
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
61
|
-
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
62
|
-
Requires-Dist: Pillow>=10.0; extra == "dev"
|
|
63
|
-
Provides-Extra: ci
|
|
64
|
-
Requires-Dist: black>=24.0; extra == "ci"
|
|
65
|
-
Requires-Dist: ruff>=0.5; extra == "ci"
|
|
66
|
-
Requires-Dist: mypy>=1.10; extra == "ci"
|
|
67
|
-
Requires-Dist: pytest>=8.0; extra == "ci"
|
|
68
|
-
Requires-Dist: pytest-asyncio>=0.23; extra == "ci"
|
|
69
|
-
Requires-Dist: Pillow>=10.0; extra == "ci"
|
|
70
50
|
Dynamic: license-file
|
|
71
51
|
|
|
72
52
|
<p align="center">
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pythonnative"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.33.0"
|
|
8
8
|
description = "Cross-platform native UI toolkit for Android and iOS"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name = "Owen Carey" }
|
|
@@ -35,13 +35,10 @@ ios = [
|
|
|
35
35
|
build = [
|
|
36
36
|
"Pillow>=10.0",
|
|
37
37
|
]
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"mkdocs-autorefs>=1.0",
|
|
43
|
-
"pymdown-extensions>=10.7",
|
|
44
|
-
]
|
|
38
|
+
|
|
39
|
+
# Contributor tooling, not published as package extras (PEP 735). Install
|
|
40
|
+
# with `uv sync --group dev` or `--group docs`; `uv sync` alone gets `dev`.
|
|
41
|
+
[dependency-groups]
|
|
45
42
|
dev = [
|
|
46
43
|
"black>=24.0",
|
|
47
44
|
"ruff>=0.5",
|
|
@@ -49,14 +46,17 @@ dev = [
|
|
|
49
46
|
"pytest>=8.0",
|
|
50
47
|
"pytest-asyncio>=0.23",
|
|
51
48
|
"Pillow>=10.0",
|
|
49
|
+
# `pn run ios` shells out to `sys.executable -m pip` to vendor
|
|
50
|
+
# rubicon-objc (project/builder.py), and the test suite exercises that
|
|
51
|
+
# path. `uv sync` does not seed pip the way `uv venv --seed` did.
|
|
52
|
+
"pip>=24.0",
|
|
52
53
|
]
|
|
53
|
-
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"Pillow>=10.0",
|
|
54
|
+
docs = [
|
|
55
|
+
"mkdocs>=1.5",
|
|
56
|
+
"mkdocs-material[imaging]>=9.5",
|
|
57
|
+
"mkdocstrings[python]>=0.24",
|
|
58
|
+
"mkdocs-autorefs>=1.0",
|
|
59
|
+
"pymdown-extensions>=10.7",
|
|
60
60
|
]
|
|
61
61
|
|
|
62
62
|
[project.scripts]
|
|
@@ -144,6 +144,15 @@ asyncio_mode = "auto"
|
|
|
144
144
|
# ── Semantic Release ────────────────────────────────────────────────
|
|
145
145
|
|
|
146
146
|
[tool.semantic_release]
|
|
147
|
+
# PSR stamps the new version into pyproject.toml before running this, so
|
|
148
|
+
# `--upgrade-package` re-syncs only this project's own version in the lock
|
|
149
|
+
# (never dependency upgrades), and the staged uv.lock lands in the release
|
|
150
|
+
# commit. This also replaces the workflow's separate `python -m build`.
|
|
151
|
+
build_command = """
|
|
152
|
+
uv lock --upgrade-package pythonnative
|
|
153
|
+
git add uv.lock
|
|
154
|
+
uv build
|
|
155
|
+
"""
|
|
147
156
|
version_toml = ["pyproject.toml:project.version"]
|
|
148
157
|
version_variables = ["src/pythonnative/__init__.py:__version__"]
|
|
149
158
|
commit_message = "chore(release): v{version}"
|
|
@@ -98,6 +98,35 @@ def App():
|
|
|
98
98
|
_GITIGNORE = "# PythonNative\n__pycache__/\n*.pyc\n.venv/\nbuild/\n.DS_Store\n"
|
|
99
99
|
|
|
100
100
|
|
|
101
|
+
_NAME_RE = re.compile(r"^[a-z][a-z0-9_-]*$")
|
|
102
|
+
"""Legal ``pn init`` project names, in the spirit of ``flutter create`` / ``cargo new``."""
|
|
103
|
+
|
|
104
|
+
_FALLBACK_NAME = "my_app"
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _sanitize_name(name: str) -> str:
|
|
108
|
+
"""Return a legal project name derived from ``name``.
|
|
109
|
+
|
|
110
|
+
Lowercases, collapses each run of illegal characters to one
|
|
111
|
+
underscore, trims leading and trailing ``_`` and ``-``, and prefixes
|
|
112
|
+
a name that doesn't start with a letter. The result always matches
|
|
113
|
+
``_NAME_RE``, falling back to ``_FALLBACK_NAME`` when nothing usable
|
|
114
|
+
survives.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
name: The rejected name, which may be empty.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
A name suitable for suggesting back to the user.
|
|
121
|
+
"""
|
|
122
|
+
slug = re.sub(r"[^a-z0-9_-]+", "_", name.lower()).strip("_-")
|
|
123
|
+
if not slug:
|
|
124
|
+
return _FALLBACK_NAME
|
|
125
|
+
if not slug[0].isascii() or not slug[0].isalpha():
|
|
126
|
+
slug = f"app_{slug}"
|
|
127
|
+
return slug
|
|
128
|
+
|
|
129
|
+
|
|
101
130
|
def _app_id_from_name(name: str) -> str:
|
|
102
131
|
slug = re.sub(r"[^a-z0-9_]", "", name.lower())
|
|
103
132
|
if not slug or not slug[0].isalpha():
|
|
@@ -113,9 +142,17 @@ def init_project(args: argparse.Namespace) -> None:
|
|
|
113
142
|
it. Either way it writes ``app/main.py``, ``pythonnative.toml``, and
|
|
114
143
|
``.gitignore``.
|
|
115
144
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
145
|
+
A name you pass has to match ``^[a-z][a-z0-9_-]*$``: lowercase letters,
|
|
146
|
+
digits, ``-``, and ``_``, starting with a letter. Anything else is
|
|
147
|
+
refused with a legal suggestion. That keeps the directory name and the
|
|
148
|
+
``name`` field in the generated config identical, in the same spirit as
|
|
149
|
+
``flutter create`` and ``cargo new``. The name taken from the current
|
|
150
|
+
directory when you pass none is used as-is, so an existing directory
|
|
151
|
+
with any name still works.
|
|
152
|
+
|
|
153
|
+
The name also has to be a single directory name, so the project always
|
|
154
|
+
lands inside the current directory. Anything that reads as a path, such
|
|
155
|
+
as ``a/b``, ``..``, or ``/tmp/app``, is refused, and so is a name that
|
|
119
156
|
resolves somewhere else, such as a symlink to another directory.
|
|
120
157
|
|
|
121
158
|
It won't scaffold into a target directory that already holds files, and it
|
|
@@ -137,6 +174,19 @@ def init_project(args: argparse.Namespace) -> None:
|
|
|
137
174
|
print(f"Refusing to treat a path as a project name: {name!r}. Use a single directory name like my_app.")
|
|
138
175
|
sys.exit(1)
|
|
139
176
|
|
|
177
|
+
# Charset check, still lexical, so it stays ahead of ``Path.cwd()`` below.
|
|
178
|
+
# ``is not None`` rather than truthiness: "" is invalid under the pattern,
|
|
179
|
+
# and falling through to the no-name path would silently scaffold here.
|
|
180
|
+
# ``fullmatch``, not ``match``: ``$`` also matches before a trailing
|
|
181
|
+
# newline, so ``match`` would accept "app\n" and create a directory
|
|
182
|
+
# whose name contains one.
|
|
183
|
+
if name is not None and not _NAME_RE.fullmatch(name):
|
|
184
|
+
print(
|
|
185
|
+
f"Invalid project name: {name!r}. Use lowercase letters, digits, '-', and '_', "
|
|
186
|
+
f"starting with a letter. Try: {_sanitize_name(name)}"
|
|
187
|
+
)
|
|
188
|
+
sys.exit(1)
|
|
189
|
+
|
|
140
190
|
cwd = Path.cwd()
|
|
141
191
|
target = cwd / name if name else cwd
|
|
142
192
|
project_name: str = name or cwd.name
|
|
@@ -947,7 +997,11 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
947
997
|
subparsers = parser.add_subparsers()
|
|
948
998
|
|
|
949
999
|
parser_init = subparsers.add_parser("init", help="Scaffold a new project")
|
|
950
|
-
parser_init.add_argument(
|
|
1000
|
+
parser_init.add_argument(
|
|
1001
|
+
"name",
|
|
1002
|
+
nargs="?",
|
|
1003
|
+
help="Project name, matching ^[a-z][a-z0-9_-]*$; creates ./<name>/ (default: current directory)",
|
|
1004
|
+
)
|
|
951
1005
|
parser_init.add_argument("--force", action="store_true", help="Overwrite existing files or a non-empty directory")
|
|
952
1006
|
parser_init.set_defaults(func=init_project)
|
|
953
1007
|
|
|
@@ -77,6 +77,10 @@ class Response:
|
|
|
77
77
|
"""``True`` if the status is in the 2xx range."""
|
|
78
78
|
return 200 <= self.status < 300
|
|
79
79
|
|
|
80
|
+
def __repr__(self) -> str:
|
|
81
|
+
cls = type(self).__name__
|
|
82
|
+
return f"{cls}(status={self.status}, url={self.url!r}, ok={self.ok}, content={len(self.content)} bytes)"
|
|
83
|
+
|
|
80
84
|
def text(self, encoding: Optional[str] = None) -> str:
|
|
81
85
|
"""Decode ``content`` to ``str``.
|
|
82
86
|
|
|
@@ -226,12 +226,15 @@ def reset_keyboard_height() -> None:
|
|
|
226
226
|
# that natural height. Forcing our own height threw off the pill
|
|
227
227
|
# geometry, so the Android handler defers entirely to the system.
|
|
228
228
|
|
|
229
|
-
#: UIKit HIG tab-bar content height in points. The total bar reaches
|
|
230
|
-
#: ``IOS_TAB_BAR_BASE_HEIGHT_PT + safe_area_insets.bottom`` so the
|
|
231
|
-
#: pill background can extend over the home indicator. Apple's HIG
|
|
232
|
-
#: places the tab bar flush with the screen edge and lets UIKit
|
|
233
|
-
#: render its own internal padding for the home indicator.
|
|
234
229
|
IOS_TAB_BAR_BASE_HEIGHT_PT: float = 49.0
|
|
230
|
+
"""UIKit HIG tab-bar content height in points.
|
|
231
|
+
|
|
232
|
+
The total bar reaches ``IOS_TAB_BAR_BASE_HEIGHT_PT +
|
|
233
|
+
safe_area_insets.bottom`` so the pill background can extend over the
|
|
234
|
+
home indicator. Apple's HIG places the tab bar flush with the screen
|
|
235
|
+
edge and lets UIKit render its own internal padding for the home
|
|
236
|
+
indicator.
|
|
237
|
+
"""
|
|
235
238
|
|
|
236
239
|
|
|
237
240
|
def ios_tab_bar_height() -> float:
|
|
@@ -586,9 +586,52 @@ def entrypoint_to_module(entry_point: str) -> str:
|
|
|
586
586
|
return normalized or "app.main"
|
|
587
587
|
|
|
588
588
|
|
|
589
|
+
# TOML v1.0.0 basic strings must escape the quotation mark, the backslash,
|
|
590
|
+
# and every control character except tab: U+0000-U+0008, U+000A-U+001F, and
|
|
591
|
+
# U+007F. Tab is legal raw, and U+000B has no compact escape, so anything
|
|
592
|
+
# without one falls through to \uXXXX.
|
|
593
|
+
_TOML_COMPACT_ESCAPES = {
|
|
594
|
+
"\\": "\\\\",
|
|
595
|
+
'"': '\\"',
|
|
596
|
+
"\b": "\\b",
|
|
597
|
+
"\f": "\\f",
|
|
598
|
+
"\n": "\\n",
|
|
599
|
+
"\r": "\\r",
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
def _toml_escape(value: str) -> str:
|
|
604
|
+
"""Escape ``value`` for use inside a TOML basic string.
|
|
605
|
+
|
|
606
|
+
Applied at the render boundary rather than relying on the caller,
|
|
607
|
+
since this module is public API and reachable without ``pn init``'s
|
|
608
|
+
name validation in front of it.
|
|
609
|
+
|
|
610
|
+
Args:
|
|
611
|
+
value: The raw string to embed between double quotes.
|
|
612
|
+
|
|
613
|
+
Returns:
|
|
614
|
+
The escaped text, without the surrounding quotes.
|
|
615
|
+
"""
|
|
616
|
+
out = []
|
|
617
|
+
for char in value:
|
|
618
|
+
escaped = _TOML_COMPACT_ESCAPES.get(char)
|
|
619
|
+
if escaped is not None:
|
|
620
|
+
out.append(escaped)
|
|
621
|
+
elif char != "\t" and (char < "\x20" or char == "\x7f"):
|
|
622
|
+
out.append(f"\\u{ord(char):04X}")
|
|
623
|
+
else:
|
|
624
|
+
out.append(char)
|
|
625
|
+
return "".join(out)
|
|
626
|
+
|
|
627
|
+
|
|
589
628
|
def render_default_toml(*, name: str, app_id: str, python_version: str = "3.11") -> str:
|
|
590
629
|
"""Render a starter ``pythonnative.toml`` for ``pn init``.
|
|
591
630
|
|
|
631
|
+
Every interpolated value is escaped for a TOML basic string, so a
|
|
632
|
+
name containing a quote, a backslash, or a control character still
|
|
633
|
+
produces a parseable file.
|
|
634
|
+
|
|
592
635
|
Args:
|
|
593
636
|
name: Project name.
|
|
594
637
|
app_id: Reverse-DNS app identifier.
|
|
@@ -599,6 +642,10 @@ def render_default_toml(*, name: str, app_id: str, python_version: str = "3.11")
|
|
|
599
642
|
for the optional tables.
|
|
600
643
|
"""
|
|
601
644
|
display = name.replace("_", " ").replace("-", " ").strip().title() or name
|
|
645
|
+
name = _toml_escape(name)
|
|
646
|
+
display = _toml_escape(display)
|
|
647
|
+
app_id = _toml_escape(app_id)
|
|
648
|
+
python_version = _toml_escape(python_version)
|
|
602
649
|
return f"""# PythonNative project configuration.
|
|
603
650
|
# Docs: https://pythonnative.com/guides/configuration/
|
|
604
651
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pythonnative
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.33.0
|
|
4
4
|
Summary: Cross-platform native UI toolkit for Android and iOS
|
|
5
5
|
Author: Owen Carey
|
|
6
6
|
License: MIT License
|
|
@@ -47,26 +47,6 @@ Provides-Extra: ios
|
|
|
47
47
|
Requires-Dist: rubicon-objc<0.5.0,>=0.4.6; extra == "ios"
|
|
48
48
|
Provides-Extra: build
|
|
49
49
|
Requires-Dist: Pillow>=10.0; extra == "build"
|
|
50
|
-
Provides-Extra: docs
|
|
51
|
-
Requires-Dist: mkdocs>=1.5; extra == "docs"
|
|
52
|
-
Requires-Dist: mkdocs-material[imaging]>=9.5; extra == "docs"
|
|
53
|
-
Requires-Dist: mkdocstrings[python]>=0.24; extra == "docs"
|
|
54
|
-
Requires-Dist: mkdocs-autorefs>=1.0; extra == "docs"
|
|
55
|
-
Requires-Dist: pymdown-extensions>=10.7; extra == "docs"
|
|
56
|
-
Provides-Extra: dev
|
|
57
|
-
Requires-Dist: black>=24.0; extra == "dev"
|
|
58
|
-
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
59
|
-
Requires-Dist: mypy>=1.10; extra == "dev"
|
|
60
|
-
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
61
|
-
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
62
|
-
Requires-Dist: Pillow>=10.0; extra == "dev"
|
|
63
|
-
Provides-Extra: ci
|
|
64
|
-
Requires-Dist: black>=24.0; extra == "ci"
|
|
65
|
-
Requires-Dist: ruff>=0.5; extra == "ci"
|
|
66
|
-
Requires-Dist: mypy>=1.10; extra == "ci"
|
|
67
|
-
Requires-Dist: pytest>=8.0; extra == "ci"
|
|
68
|
-
Requires-Dist: pytest-asyncio>=0.23; extra == "ci"
|
|
69
|
-
Requires-Dist: Pillow>=10.0; extra == "ci"
|
|
70
50
|
Dynamic: license-file
|
|
71
51
|
|
|
72
52
|
<p align="center">
|
|
@@ -37,9 +37,9 @@ def test_cli_short_version_flag(tmp_path: Path) -> None:
|
|
|
37
37
|
def test_cli_init_and_clean() -> None:
|
|
38
38
|
tmpdir = tempfile.mkdtemp(prefix="pn_cli_test_")
|
|
39
39
|
try:
|
|
40
|
-
result = run_pn(["init", "
|
|
40
|
+
result = run_pn(["init", "my_app"], tmpdir)
|
|
41
41
|
assert result.returncode == 0, result.stderr
|
|
42
|
-
project_dir = os.path.join(tmpdir, "
|
|
42
|
+
project_dir = os.path.join(tmpdir, "my_app")
|
|
43
43
|
assert os.path.isdir(os.path.join(project_dir, "app"))
|
|
44
44
|
|
|
45
45
|
main_path = os.path.join(project_dir, "app", "main.py")
|
|
@@ -51,7 +51,7 @@ def test_cli_init_and_clean() -> None:
|
|
|
51
51
|
config_path = os.path.join(project_dir, "pythonnative.toml")
|
|
52
52
|
assert os.path.isfile(config_path)
|
|
53
53
|
toml_text = Path(config_path).read_text(encoding="utf-8")
|
|
54
|
-
assert 'id = "com.example.
|
|
54
|
+
assert 'id = "com.example.my_app"' in toml_text
|
|
55
55
|
assert os.path.isfile(os.path.join(project_dir, ".gitignore"))
|
|
56
56
|
# The legacy JSON config and requirements.txt are no longer scaffolded.
|
|
57
57
|
assert not os.path.exists(os.path.join(project_dir, "pythonnative.json"))
|
|
@@ -71,11 +71,11 @@ def test_cli_init_and_clean() -> None:
|
|
|
71
71
|
def test_cli_init_refuses_overwrite() -> None:
|
|
72
72
|
tmpdir = tempfile.mkdtemp(prefix="pn_cli_test_")
|
|
73
73
|
try:
|
|
74
|
-
assert run_pn(["init", "
|
|
75
|
-
result = run_pn(["init", "
|
|
74
|
+
assert run_pn(["init", "my_app"], tmpdir).returncode == 0
|
|
75
|
+
result = run_pn(["init", "my_app"], tmpdir)
|
|
76
76
|
assert result.returncode != 0
|
|
77
77
|
assert "Refusing to overwrite" in result.stdout
|
|
78
|
-
assert run_pn(["init", "
|
|
78
|
+
assert run_pn(["init", "my_app", "--force"], tmpdir).returncode == 0
|
|
79
79
|
finally:
|
|
80
80
|
shutil.rmtree(tmpdir, ignore_errors=True)
|
|
81
81
|
|
|
@@ -210,6 +210,92 @@ def test_cli_init_rejects_symlinked_target(tmp_path: Path, populated: bool, extr
|
|
|
210
210
|
assert os.listdir(str(work_dir)) == ["link"]
|
|
211
211
|
|
|
212
212
|
|
|
213
|
+
_ILLEGAL_NAMES = [
|
|
214
|
+
pytest.param("MyApp", id="uppercase"),
|
|
215
|
+
pytest.param("my app", id="space"),
|
|
216
|
+
pytest.param('bad"name', id="quote"),
|
|
217
|
+
pytest.param("9lives", id="leading-digit"),
|
|
218
|
+
pytest.param("_leading", id="leading-underscore"),
|
|
219
|
+
pytest.param("café", id="non-ascii"),
|
|
220
|
+
pytest.param("", id="empty"),
|
|
221
|
+
# `$` matches before a trailing newline, so `match` would let this through.
|
|
222
|
+
pytest.param("app\n", id="trailing-newline"),
|
|
223
|
+
]
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
@pytest.mark.parametrize("name", _ILLEGAL_NAMES)
|
|
227
|
+
def test_cli_init_rejects_illegal_names(tmp_path: Path, name: str) -> None:
|
|
228
|
+
result = run_pn(["init", name], str(tmp_path))
|
|
229
|
+
|
|
230
|
+
assert result.returncode != 0
|
|
231
|
+
assert "Invalid project name" in result.stdout
|
|
232
|
+
assert "Try: " in result.stdout
|
|
233
|
+
# Nothing is written, not even into the current directory.
|
|
234
|
+
assert os.listdir(str(tmp_path)) == []
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
@pytest.mark.parametrize("name", _ILLEGAL_NAMES)
|
|
238
|
+
def test_cli_init_force_does_not_lift_name_validation(tmp_path: Path, name: str) -> None:
|
|
239
|
+
result = run_pn(["init", name, "--force"], str(tmp_path))
|
|
240
|
+
|
|
241
|
+
assert result.returncode != 0
|
|
242
|
+
assert "Invalid project name" in result.stdout
|
|
243
|
+
assert os.listdir(str(tmp_path)) == []
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def test_cli_init_suggestion_is_itself_a_legal_name() -> None:
|
|
247
|
+
# The suggestion is only useful if the user can paste it straight back.
|
|
248
|
+
awkward = [
|
|
249
|
+
"MyApp",
|
|
250
|
+
"my app",
|
|
251
|
+
'bad"name',
|
|
252
|
+
"9lives",
|
|
253
|
+
"_leading",
|
|
254
|
+
"café",
|
|
255
|
+
"",
|
|
256
|
+
"___",
|
|
257
|
+
"---",
|
|
258
|
+
"123",
|
|
259
|
+
"..",
|
|
260
|
+
"a/b",
|
|
261
|
+
"ÄÖÜ",
|
|
262
|
+
"\t",
|
|
263
|
+
"app\n",
|
|
264
|
+
"app\n\n",
|
|
265
|
+
"\napp",
|
|
266
|
+
"\n",
|
|
267
|
+
"\x00",
|
|
268
|
+
"\x7f",
|
|
269
|
+
"sp ace",
|
|
270
|
+
"trailing_",
|
|
271
|
+
"-lead",
|
|
272
|
+
"mixed_-CASE-99",
|
|
273
|
+
"🙂",
|
|
274
|
+
"a" * 200,
|
|
275
|
+
]
|
|
276
|
+
for name in awkward:
|
|
277
|
+
assert pn_cli._NAME_RE.fullmatch(pn_cli._sanitize_name(name)), name
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def test_cli_init_suggestion_is_stable_for_legal_names() -> None:
|
|
281
|
+
for name in ["my_app", "my-app", "a", "a1", "hello-world_2"]:
|
|
282
|
+
assert pn_cli._sanitize_name(name) == name
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def test_cli_init_without_name_accepts_a_cwd_that_fails_the_pattern(tmp_path: Path) -> None:
|
|
286
|
+
# Validation covers the typed name only. A directory named MyProject is
|
|
287
|
+
# extremely ordinary, and `pn init` there must keep working.
|
|
288
|
+
project_dir = tmp_path / "MyProject"
|
|
289
|
+
project_dir.mkdir()
|
|
290
|
+
|
|
291
|
+
result = run_pn(["init"], str(project_dir))
|
|
292
|
+
|
|
293
|
+
assert result.returncode == 0, result.stdout
|
|
294
|
+
toml_text = (project_dir / "pythonnative.toml").read_text(encoding="utf-8")
|
|
295
|
+
assert 'name = "MyProject"' in toml_text
|
|
296
|
+
assert 'id = "com.example.myproject"' in toml_text
|
|
297
|
+
|
|
298
|
+
|
|
213
299
|
def test_cli_run_help_lists_flags() -> None:
|
|
214
300
|
tmpdir = tempfile.mkdtemp(prefix="pn_cli_test_")
|
|
215
301
|
try:
|
|
@@ -252,17 +338,17 @@ def test_cli_run_without_config_errors() -> None:
|
|
|
252
338
|
|
|
253
339
|
|
|
254
340
|
def test_cli_app_id_resolves(tmp_path: Path) -> None:
|
|
255
|
-
assert run_pn(["init", "
|
|
256
|
-
project_dir = str(tmp_path / "
|
|
341
|
+
assert run_pn(["init", "my_app"], str(tmp_path)).returncode == 0
|
|
342
|
+
project_dir = str(tmp_path / "my_app")
|
|
257
343
|
result = run_pn(["app-id", "android"], project_dir)
|
|
258
344
|
assert result.returncode == 0, result.stderr
|
|
259
|
-
assert result.stdout.strip() == "com.example.
|
|
260
|
-
assert run_pn(["app-id", "ios"], project_dir).stdout.strip() == "com.example.
|
|
345
|
+
assert result.stdout.strip() == "com.example.my_app"
|
|
346
|
+
assert run_pn(["app-id", "ios"], project_dir).stdout.strip() == "com.example.my_app"
|
|
261
347
|
|
|
262
348
|
|
|
263
349
|
def test_cli_doctor_runs(tmp_path: Path) -> None:
|
|
264
|
-
assert run_pn(["init", "
|
|
265
|
-
result = run_pn(["doctor", "android"], str(tmp_path / "
|
|
350
|
+
assert run_pn(["init", "my_app"], str(tmp_path)).returncode == 0
|
|
351
|
+
result = run_pn(["doctor", "android"], str(tmp_path / "my_app"))
|
|
266
352
|
assert "PythonNative doctor" in result.stdout
|
|
267
353
|
# android-only doctor on a CI box without adb still produces warnings, not errors.
|
|
268
354
|
assert result.returncode in (0, 1)
|
|
@@ -271,8 +357,8 @@ def test_cli_doctor_runs(tmp_path: Path) -> None:
|
|
|
271
357
|
def test_cli_run_prepare_only_android_and_ios() -> None:
|
|
272
358
|
tmpdir = tempfile.mkdtemp(prefix="pn_cli_test_")
|
|
273
359
|
try:
|
|
274
|
-
assert run_pn(["init", "
|
|
275
|
-
project_dir = os.path.join(tmpdir, "
|
|
360
|
+
assert run_pn(["init", "my_app"], tmpdir).returncode == 0
|
|
361
|
+
project_dir = os.path.join(tmpdir, "my_app")
|
|
276
362
|
|
|
277
363
|
result = run_pn(["run", "android", "--prepare-only", "--no-logs"], project_dir)
|
|
278
364
|
assert result.returncode == 0, result.stderr
|
|
@@ -280,7 +366,7 @@ def test_cli_run_prepare_only_android_and_ios() -> None:
|
|
|
280
366
|
assert os.path.isdir(android_root)
|
|
281
367
|
# Package relocated to the configured application id.
|
|
282
368
|
relocated = os.path.join(
|
|
283
|
-
android_root, "app", "src", "main", "java", "com", "example", "
|
|
369
|
+
android_root, "app", "src", "main", "java", "com", "example", "my_app", "ScreenFragment.kt"
|
|
284
370
|
)
|
|
285
371
|
assert os.path.isfile(relocated)
|
|
286
372
|
assert not os.path.exists(
|
|
@@ -288,7 +374,7 @@ def test_cli_run_prepare_only_android_and_ios() -> None:
|
|
|
288
374
|
)
|
|
289
375
|
# App identity written into the Gradle config.
|
|
290
376
|
gradle = Path(os.path.join(android_root, "app", "build.gradle")).read_text(encoding="utf-8")
|
|
291
|
-
assert "com.example.
|
|
377
|
+
assert "com.example.my_app" in gradle
|
|
292
378
|
|
|
293
379
|
result = run_pn(["run", "ios", "--prepare-only", "--no-logs"], project_dir)
|
|
294
380
|
assert result.returncode == 0, result.stderr
|
|
@@ -118,6 +118,17 @@ def test_get_json(echo_server: str) -> None:
|
|
|
118
118
|
assert resp.json() == {"ok": True}
|
|
119
119
|
|
|
120
120
|
|
|
121
|
+
def test_repr_summarizes_body(echo_server: str) -> None:
|
|
122
|
+
async def run() -> Response:
|
|
123
|
+
return await fetch(echo_server + "/text")
|
|
124
|
+
|
|
125
|
+
resp = asyncio.run(run())
|
|
126
|
+
text = repr(resp)
|
|
127
|
+
assert "status=200" in text
|
|
128
|
+
assert f"{len(resp.content)} bytes" in text
|
|
129
|
+
assert resp.text() not in text
|
|
130
|
+
|
|
131
|
+
|
|
121
132
|
def test_non_2xx_does_not_raise_but_keeps_body(echo_server: str) -> None:
|
|
122
133
|
async def run() -> Response:
|
|
123
134
|
return await fetch(echo_server + "/status/418")
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
requests>=2.31.0
|
|
2
|
-
|
|
3
|
-
[:python_version < "3.11"]
|
|
4
|
-
tomli>=2.0
|
|
5
|
-
|
|
6
|
-
[build]
|
|
7
|
-
Pillow>=10.0
|
|
8
|
-
|
|
9
|
-
[ci]
|
|
10
|
-
black>=24.0
|
|
11
|
-
ruff>=0.5
|
|
12
|
-
mypy>=1.10
|
|
13
|
-
pytest>=8.0
|
|
14
|
-
pytest-asyncio>=0.23
|
|
15
|
-
Pillow>=10.0
|
|
16
|
-
|
|
17
|
-
[dev]
|
|
18
|
-
black>=24.0
|
|
19
|
-
ruff>=0.5
|
|
20
|
-
mypy>=1.10
|
|
21
|
-
pytest>=8.0
|
|
22
|
-
pytest-asyncio>=0.23
|
|
23
|
-
Pillow>=10.0
|
|
24
|
-
|
|
25
|
-
[docs]
|
|
26
|
-
mkdocs>=1.5
|
|
27
|
-
mkdocs-material[imaging]>=9.5
|
|
28
|
-
mkdocstrings[python]>=0.24
|
|
29
|
-
mkdocs-autorefs>=1.0
|
|
30
|
-
pymdown-extensions>=10.7
|
|
31
|
-
|
|
32
|
-
[ios]
|
|
33
|
-
rubicon-objc<0.5.0,>=0.4.6
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/notifications.py
RENAMED
|
File without changes
|
|
File without changes
|