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.
Files changed (172) hide show
  1. {pythonnative-0.31.0/src/pythonnative.egg-info → pythonnative-0.33.0}/PKG-INFO +1 -21
  2. {pythonnative-0.31.0 → pythonnative-0.33.0}/pyproject.toml +24 -15
  3. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/__init__.py +1 -1
  4. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/cli/pn.py +58 -4
  5. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/net.py +4 -0
  6. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/platform_metrics.py +8 -5
  7. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/project/config.py +47 -0
  8. {pythonnative-0.31.0 → pythonnative-0.33.0/src/pythonnative.egg-info}/PKG-INFO +1 -21
  9. pythonnative-0.33.0/src/pythonnative.egg-info/requires.txt +10 -0
  10. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_cli.py +102 -16
  11. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_net.py +11 -0
  12. pythonnative-0.31.0/src/pythonnative.egg-info/requires.txt +0 -33
  13. {pythonnative-0.31.0 → pythonnative-0.33.0}/LICENSE +0 -0
  14. {pythonnative-0.31.0 → pythonnative-0.33.0}/README.md +0 -0
  15. {pythonnative-0.31.0 → pythonnative-0.33.0}/setup.cfg +0 -0
  16. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/_ios_log.py +0 -0
  17. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/alerts.py +0 -0
  18. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/animated.py +0 -0
  19. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/appearance.py +0 -0
  20. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/cli/__init__.py +0 -0
  21. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/components.py +0 -0
  22. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/diagnostics.py +0 -0
  23. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/element.py +0 -0
  24. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/events.py +0 -0
  25. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/gestures.py +0 -0
  26. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/hooks.py +0 -0
  27. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/hot_reload.py +0 -0
  28. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/images.py +0 -0
  29. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/layout.py +0 -0
  30. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/mutations.py +0 -0
  31. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/__init__.py +0 -0
  32. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/app_state.py +0 -0
  33. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/battery.py +0 -0
  34. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/biometrics.py +0 -0
  35. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/camera.py +0 -0
  36. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/clipboard.py +0 -0
  37. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/file_system.py +0 -0
  38. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/haptics.py +0 -0
  39. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/linking.py +0 -0
  40. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/location.py +0 -0
  41. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/net_info.py +0 -0
  42. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/notifications.py +0 -0
  43. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/permissions.py +0 -0
  44. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/secure_store.py +0 -0
  45. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_modules/share.py +0 -0
  46. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_views/__init__.py +0 -0
  47. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_views/android.py +0 -0
  48. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_views/base.py +0 -0
  49. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_views/desktop.py +0 -0
  50. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/native_views/ios.py +0 -0
  51. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/navigation.py +0 -0
  52. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/platform.py +0 -0
  53. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/preview.py +0 -0
  54. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/project/__init__.py +0 -0
  55. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/project/android.py +0 -0
  56. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/project/builder.py +0 -0
  57. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/project/devices.py +0 -0
  58. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/project/doctor.py +0 -0
  59. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/project/icons.py +0 -0
  60. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/project/ios.py +0 -0
  61. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/project/permissions.py +0 -0
  62. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/project/runtime_assets.py +0 -0
  63. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/reconciler.py +0 -0
  64. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/runtime.py +0 -0
  65. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/screen.py +0 -0
  66. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/sdk/__init__.py +0 -0
  67. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/sdk/_components.py +0 -0
  68. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/storage.py +0 -0
  69. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/style.py +0 -0
  70. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/suspense.py +0 -0
  71. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/build.gradle +0 -0
  72. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/proguard-rules.pro +0 -0
  73. {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
  74. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/AndroidManifest.xml +0 -0
  75. {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
  76. {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
  77. {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
  78. {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
  79. {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
  80. {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
  81. {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
  82. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/drawable/ic_launcher_background.xml +0 -0
  83. {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
  84. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/layout/activity_main.xml +0 -0
  85. {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
  86. {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
  87. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
  88. {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
  89. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
  90. {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
  91. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
  92. {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
  93. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
  94. {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
  95. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
  96. {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
  97. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/navigation/nav_graph.xml +0 -0
  98. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/values/colors.xml +0 -0
  99. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/values/strings.xml +0 -0
  100. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/values/themes.xml +0 -0
  101. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/values-night/themes.xml +0 -0
  102. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/xml/backup_rules.xml +0 -0
  103. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/app/src/main/res/xml/data_extraction_rules.xml +0 -0
  104. {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
  105. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/build.gradle +0 -0
  106. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.jar +0 -0
  107. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.properties +0 -0
  108. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/gradle.properties +0 -0
  109. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/gradlew +0 -0
  110. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/gradlew.bat +0 -0
  111. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/android_template/settings.gradle +0 -0
  112. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_template/AppDelegate.swift +0 -0
  113. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/AccentColor.colorset/Contents.json +0 -0
  114. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -0
  115. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/Contents.json +0 -0
  116. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_template/Base.lproj/LaunchScreen.storyboard +0 -0
  117. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_template/BridgingHeader.h +0 -0
  118. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_template/Info.plist +0 -0
  119. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_template/PythonRuntime.swift +0 -0
  120. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_template/SceneDelegate.swift +0 -0
  121. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_template/ViewController.swift +0 -0
  122. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_template.xcodeproj/project.pbxproj +0 -0
  123. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_template.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -0
  124. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_templateTests/ios_templateTests.swift +0 -0
  125. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITests.swift +0 -0
  126. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITestsLaunchTests.swift +0 -0
  127. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/utils.py +0 -0
  128. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative/virtual_rows.py +0 -0
  129. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative.egg-info/SOURCES.txt +0 -0
  130. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative.egg-info/dependency_links.txt +0 -0
  131. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative.egg-info/entry_points.txt +0 -0
  132. {pythonnative-0.31.0 → pythonnative-0.33.0}/src/pythonnative.egg-info/top_level.txt +0 -0
  133. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_alert.py +0 -0
  134. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_animated.py +0 -0
  135. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_animated_graph.py +0 -0
  136. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_appearance.py +0 -0
  137. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_async_hooks.py +0 -0
  138. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_components.py +0 -0
  139. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_core_semantics.py +0 -0
  140. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_desktop_backend.py +0 -0
  141. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_element.py +0 -0
  142. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_events.py +0 -0
  143. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_extended_components.py +0 -0
  144. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_gesture_composition.py +0 -0
  145. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_gestures.py +0 -0
  146. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_hooks.py +0 -0
  147. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_hot_reload.py +0 -0
  148. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_images.py +0 -0
  149. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_incremental_render.py +0 -0
  150. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_interaction_props.py +0 -0
  151. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_ios_log.py +0 -0
  152. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_layout.py +0 -0
  153. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_metric_hooks.py +0 -0
  154. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_mutations.py +0 -0
  155. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_native_lists.py +0 -0
  156. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_native_modules.py +0 -0
  157. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_native_views.py +0 -0
  158. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_navigation.py +0 -0
  159. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_new_components.py +0 -0
  160. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_platform.py +0 -0
  161. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_platform_metrics.py +0 -0
  162. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_reconciler.py +0 -0
  163. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_ref.py +0 -0
  164. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_runtime.py +0 -0
  165. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_screen.py +0 -0
  166. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_sdk.py +0 -0
  167. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_smoke.py +0 -0
  168. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_storage.py +0 -0
  169. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_style.py +0 -0
  170. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_suspense.py +0 -0
  171. {pythonnative-0.31.0 → pythonnative-0.33.0}/tests/test_transitions.py +0 -0
  172. {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.31.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.31.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
- docs = [
39
- "mkdocs>=1.5",
40
- "mkdocs-material[imaging]>=9.5",
41
- "mkdocstrings[python]>=0.24",
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
- ci = [
54
- "black>=24.0",
55
- "ruff>=0.5",
56
- "mypy>=1.10",
57
- "pytest>=8.0",
58
- "pytest-asyncio>=0.23",
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}"
@@ -60,7 +60,7 @@ Example:
60
60
  ```
61
61
  """
62
62
 
63
- __version__ = "0.31.0"
63
+ __version__ = "0.33.0"
64
64
 
65
65
  from . import appearance, diagnostics, gestures, images, runtime, sdk
66
66
  from .alerts import Alert
@@ -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
- The name has to be a single directory name, so the project always lands
117
- inside the current directory. Anything that reads as a path, such as
118
- ``a/b``, ``..``, or ``/tmp/app``, is refused, and so is a name that
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("name", nargs="?", help="Project name; creates ./<name>/ (default: current directory)")
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.31.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">
@@ -0,0 +1,10 @@
1
+ requests>=2.31.0
2
+
3
+ [:python_version < "3.11"]
4
+ tomli>=2.0
5
+
6
+ [build]
7
+ Pillow>=10.0
8
+
9
+ [ios]
10
+ rubicon-objc<0.5.0,>=0.4.6
@@ -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", "MyApp"], tmpdir)
40
+ result = run_pn(["init", "my_app"], tmpdir)
41
41
  assert result.returncode == 0, result.stderr
42
- project_dir = os.path.join(tmpdir, "MyApp")
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.myapp"' in toml_text
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", "MyApp"], tmpdir).returncode == 0
75
- result = run_pn(["init", "MyApp"], tmpdir)
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", "MyApp", "--force"], tmpdir).returncode == 0
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", "MyApp"], str(tmp_path)).returncode == 0
256
- project_dir = str(tmp_path / "MyApp")
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.myapp"
260
- assert run_pn(["app-id", "ios"], project_dir).stdout.strip() == "com.example.myapp"
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", "MyApp"], str(tmp_path)).returncode == 0
265
- result = run_pn(["doctor", "android"], str(tmp_path / "MyApp"))
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", "MyApp"], tmpdir).returncode == 0
275
- project_dir = os.path.join(tmpdir, "MyApp")
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", "myapp", "ScreenFragment.kt"
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.myapp" in gradle
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