pythonnative 0.29.0__tar.gz → 0.31.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.29.0/src/pythonnative.egg-info → pythonnative-0.31.0}/PKG-INFO +1 -1
  2. {pythonnative-0.29.0 → pythonnative-0.31.0}/pyproject.toml +1 -1
  3. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/__init__.py +1 -1
  4. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/cli/pn.py +96 -19
  5. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/project/devices.py +14 -1
  6. {pythonnative-0.29.0 → pythonnative-0.31.0/src/pythonnative.egg-info}/PKG-INFO +1 -1
  7. pythonnative-0.31.0/tests/test_cli.py +608 -0
  8. pythonnative-0.29.0/tests/test_cli.py +0 -280
  9. {pythonnative-0.29.0 → pythonnative-0.31.0}/LICENSE +0 -0
  10. {pythonnative-0.29.0 → pythonnative-0.31.0}/README.md +0 -0
  11. {pythonnative-0.29.0 → pythonnative-0.31.0}/setup.cfg +0 -0
  12. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/_ios_log.py +0 -0
  13. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/alerts.py +0 -0
  14. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/animated.py +0 -0
  15. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/appearance.py +0 -0
  16. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/cli/__init__.py +0 -0
  17. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/components.py +0 -0
  18. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/diagnostics.py +0 -0
  19. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/element.py +0 -0
  20. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/events.py +0 -0
  21. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/gestures.py +0 -0
  22. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/hooks.py +0 -0
  23. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/hot_reload.py +0 -0
  24. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/images.py +0 -0
  25. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/layout.py +0 -0
  26. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/mutations.py +0 -0
  27. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/native_modules/__init__.py +0 -0
  28. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/native_modules/app_state.py +0 -0
  29. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/native_modules/battery.py +0 -0
  30. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/native_modules/biometrics.py +0 -0
  31. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/native_modules/camera.py +0 -0
  32. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/native_modules/clipboard.py +0 -0
  33. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/native_modules/file_system.py +0 -0
  34. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/native_modules/haptics.py +0 -0
  35. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/native_modules/linking.py +0 -0
  36. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/native_modules/location.py +0 -0
  37. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/native_modules/net_info.py +0 -0
  38. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/native_modules/notifications.py +0 -0
  39. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/native_modules/permissions.py +0 -0
  40. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/native_modules/secure_store.py +0 -0
  41. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/native_modules/share.py +0 -0
  42. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/native_views/__init__.py +0 -0
  43. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/native_views/android.py +0 -0
  44. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/native_views/base.py +0 -0
  45. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/native_views/desktop.py +0 -0
  46. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/native_views/ios.py +0 -0
  47. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/navigation.py +0 -0
  48. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/net.py +0 -0
  49. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/platform.py +0 -0
  50. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/platform_metrics.py +0 -0
  51. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/preview.py +0 -0
  52. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/project/__init__.py +0 -0
  53. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/project/android.py +0 -0
  54. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/project/builder.py +0 -0
  55. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/project/config.py +0 -0
  56. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/project/doctor.py +0 -0
  57. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/project/icons.py +0 -0
  58. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/project/ios.py +0 -0
  59. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/project/permissions.py +0 -0
  60. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/project/runtime_assets.py +0 -0
  61. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/reconciler.py +0 -0
  62. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/runtime.py +0 -0
  63. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/screen.py +0 -0
  64. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/sdk/__init__.py +0 -0
  65. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/sdk/_components.py +0 -0
  66. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/storage.py +0 -0
  67. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/style.py +0 -0
  68. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/suspense.py +0 -0
  69. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/build.gradle +0 -0
  70. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/proguard-rules.pro +0 -0
  71. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/androidTest/java/com/pythonnative/android_template/ExampleInstrumentedTest.kt +0 -0
  72. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/AndroidManifest.xml +0 -0
  73. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt +0 -0
  74. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/Navigator.kt +0 -0
  75. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PNAccessibilityDelegate.kt +0 -0
  76. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PNBorderDrawable.kt +0 -0
  77. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PNFrameLayout.kt +0 -0
  78. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PNVirtualListView.java +0 -0
  79. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/ScreenFragment.kt +0 -0
  80. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/res/drawable/ic_launcher_background.xml +0 -0
  81. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +0 -0
  82. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/res/layout/activity_main.xml +0 -0
  83. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +0 -0
  84. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +0 -0
  85. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
  86. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp +0 -0
  87. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
  88. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp +0 -0
  89. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
  90. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp +0 -0
  91. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
  92. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp +0 -0
  93. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
  94. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp +0 -0
  95. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/res/navigation/nav_graph.xml +0 -0
  96. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/res/values/colors.xml +0 -0
  97. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/res/values/strings.xml +0 -0
  98. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/res/values/themes.xml +0 -0
  99. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/res/values-night/themes.xml +0 -0
  100. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/res/xml/backup_rules.xml +0 -0
  101. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/main/res/xml/data_extraction_rules.xml +0 -0
  102. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/app/src/test/java/com/pythonnative/android_template/ExampleUnitTest.kt +0 -0
  103. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/build.gradle +0 -0
  104. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.jar +0 -0
  105. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.properties +0 -0
  106. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/gradle.properties +0 -0
  107. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/gradlew +0 -0
  108. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/gradlew.bat +0 -0
  109. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/android_template/settings.gradle +0 -0
  110. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/ios_template/ios_template/AppDelegate.swift +0 -0
  111. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/AccentColor.colorset/Contents.json +0 -0
  112. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -0
  113. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/Contents.json +0 -0
  114. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/ios_template/ios_template/Base.lproj/LaunchScreen.storyboard +0 -0
  115. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/ios_template/ios_template/BridgingHeader.h +0 -0
  116. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/ios_template/ios_template/Info.plist +0 -0
  117. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/ios_template/ios_template/PythonRuntime.swift +0 -0
  118. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/ios_template/ios_template/SceneDelegate.swift +0 -0
  119. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/ios_template/ios_template/ViewController.swift +0 -0
  120. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/ios_template/ios_template.xcodeproj/project.pbxproj +0 -0
  121. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/ios_template/ios_template.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -0
  122. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/ios_template/ios_templateTests/ios_templateTests.swift +0 -0
  123. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITests.swift +0 -0
  124. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITestsLaunchTests.swift +0 -0
  125. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/utils.py +0 -0
  126. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative/virtual_rows.py +0 -0
  127. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative.egg-info/SOURCES.txt +0 -0
  128. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative.egg-info/dependency_links.txt +0 -0
  129. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative.egg-info/entry_points.txt +0 -0
  130. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative.egg-info/requires.txt +0 -0
  131. {pythonnative-0.29.0 → pythonnative-0.31.0}/src/pythonnative.egg-info/top_level.txt +0 -0
  132. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_alert.py +0 -0
  133. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_animated.py +0 -0
  134. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_animated_graph.py +0 -0
  135. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_appearance.py +0 -0
  136. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_async_hooks.py +0 -0
  137. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_components.py +0 -0
  138. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_core_semantics.py +0 -0
  139. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_desktop_backend.py +0 -0
  140. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_element.py +0 -0
  141. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_events.py +0 -0
  142. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_extended_components.py +0 -0
  143. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_gesture_composition.py +0 -0
  144. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_gestures.py +0 -0
  145. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_hooks.py +0 -0
  146. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_hot_reload.py +0 -0
  147. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_images.py +0 -0
  148. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_incremental_render.py +0 -0
  149. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_interaction_props.py +0 -0
  150. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_ios_log.py +0 -0
  151. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_layout.py +0 -0
  152. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_metric_hooks.py +0 -0
  153. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_mutations.py +0 -0
  154. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_native_lists.py +0 -0
  155. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_native_modules.py +0 -0
  156. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_native_views.py +0 -0
  157. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_navigation.py +0 -0
  158. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_net.py +0 -0
  159. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_new_components.py +0 -0
  160. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_platform.py +0 -0
  161. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_platform_metrics.py +0 -0
  162. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_reconciler.py +0 -0
  163. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_ref.py +0 -0
  164. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_runtime.py +0 -0
  165. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_screen.py +0 -0
  166. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_sdk.py +0 -0
  167. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_smoke.py +0 -0
  168. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_storage.py +0 -0
  169. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_style.py +0 -0
  170. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_suspense.py +0 -0
  171. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_transitions.py +0 -0
  172. {pythonnative-0.29.0 → pythonnative-0.31.0}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pythonnative
3
- Version: 0.29.0
3
+ Version: 0.31.0
4
4
  Summary: Cross-platform native UI toolkit for Android and iOS
5
5
  Author: Owen Carey
6
6
  License: MIT License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pythonnative"
7
- version = "0.29.0"
7
+ version = "0.31.0"
8
8
  description = "Cross-platform native UI toolkit for Android and iOS"
9
9
  authors = [
10
10
  { name = "Owen Carey" }
@@ -60,7 +60,7 @@ Example:
60
60
  ```
61
61
  """
62
62
 
63
- __version__ = "0.29.0"
63
+ __version__ = "0.31.0"
64
64
 
65
65
  from . import appearance, diagnostics, gestures, images, runtime, sdk
66
66
  from .alerts import Alert
@@ -2,12 +2,14 @@
2
2
 
3
3
  The console script `pn` (declared in `pyproject.toml`) dispatches to:
4
4
 
5
- - `pn init [name]`: scaffold a new project (``pythonnative.toml`` + ``app/``).
5
+ - `pn init [name]`: scaffold a new project (``pythonnative.toml`` +
6
+ ``app/``) into ``./name/``, or into the current directory when no name
7
+ is given.
6
8
  - `pn doctor [platform]`: diagnose the local toolchain and config.
7
9
  - `pn preview [component]`: render the app in a desktop (Tkinter) window
8
10
  with Fast Refresh, the fast inner dev loop, no device required.
9
11
  - `pn devices [platform]`: list connected devices, emulators, and
10
- simulators.
12
+ simulators, as a table or as JSON with `--json`.
11
13
  - `pn run android|ios [--device D]`: stage + build + install + launch on
12
14
  a device, emulator, or simulator, with optional on-device hot reload.
13
15
  - `pn logs android|ios`: stream logs from the running app without
@@ -36,7 +38,7 @@ import sys
36
38
  import time
37
39
  from importlib.metadata import version as pkg_version
38
40
  from pathlib import Path
39
- from typing import Any, Dict, List, Optional
41
+ from typing import Any, Dict, List, Optional, TextIO
40
42
 
41
43
  from ..project import builder as builder_mod
42
44
  from ..project import devices as devices_mod
@@ -104,21 +106,62 @@ def _app_id_from_name(name: str) -> str:
104
106
 
105
107
 
106
108
  def init_project(args: argparse.Namespace) -> None:
107
- """Scaffold a new PythonNative project in the current directory.
109
+ """Scaffold a new PythonNative project.
108
110
 
109
- Creates ``app/main.py``, ``pythonnative.toml``, and ``.gitignore``.
110
- Refuses to overwrite existing files unless ``--force`` is passed.
111
+ Given a name, this creates ``./<name>/`` and scaffolds into it. Without
112
+ one, it scaffolds into the current directory and names the project after
113
+ it. Either way it writes ``app/main.py``, ``pythonnative.toml``, and
114
+ ``.gitignore``.
115
+
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
119
+ resolves somewhere else, such as a symlink to another directory.
120
+
121
+ It won't scaffold into a target directory that already holds files, and it
122
+ won't overwrite any of the three paths above; pass ``--force`` to override
123
+ both. An existing but empty target directory is fine. A plain file at
124
+ ``./<name>`` is always refused, since ``--force`` can't turn it into a
125
+ directory, and ``--force`` lifts neither of the rules above.
111
126
 
112
127
  Args:
113
128
  args: Parsed namespace with ``name`` (optional) and ``force``.
114
129
  """
115
- cwd = Path.cwd()
116
- project_name: str = getattr(args, "name", None) or cwd.name
130
+ name: Optional[str] = getattr(args, "name", None)
117
131
  force: bool = getattr(args, "force", False)
118
132
 
119
- app_dir = cwd / "app"
120
- config_path = cwd / CONFIG_FILENAME
121
- gitignore_path = cwd / ".gitignore"
133
+ # Lexical check, before anything reads the filesystem. ``Path("..").name``
134
+ # is "..", so ".." needs naming explicitly; the rest (absolute, nested,
135
+ # trailing separator, ".") fall out of the name check.
136
+ if name and (name in (os.curdir, os.pardir) or Path(name).name != name):
137
+ print(f"Refusing to treat a path as a project name: {name!r}. Use a single directory name like my_app.")
138
+ sys.exit(1)
139
+
140
+ cwd = Path.cwd()
141
+ target = cwd / name if name else cwd
142
+ project_name: str = name or cwd.name
143
+
144
+ # A lexically clean name can still resolve elsewhere, and ``exists()`` and
145
+ # ``is_dir()`` below follow symlinks. Check containment rather than just
146
+ # ``is_symlink()`` so the whole class is closed, not one spelling of it.
147
+ if name and (target.is_symlink() or target.resolve().parent != cwd.resolve()):
148
+ print(
149
+ f"Refusing to scaffold through a link or outside the current directory: {name}. "
150
+ "Use a plain directory name."
151
+ )
152
+ sys.exit(1)
153
+
154
+ app_dir = target / "app"
155
+ config_path = target / CONFIG_FILENAME
156
+ gitignore_path = target / ".gitignore"
157
+
158
+ if name and target.exists():
159
+ if not target.is_dir():
160
+ print(f"Refusing to overwrite existing file: {name}. Remove it or choose a different name.")
161
+ sys.exit(1)
162
+ if any(target.iterdir()) and not force:
163
+ print(f"Refusing to overwrite existing non-empty directory: {name}/. Use --force to overwrite.")
164
+ sys.exit(1)
122
165
 
123
166
  if not force:
124
167
  existing = [
@@ -142,8 +185,11 @@ def init_project(args: argparse.Namespace) -> None:
142
185
  if force or not gitignore_path.exists():
143
186
  gitignore_path.write_text(_GITIGNORE, encoding="utf-8")
144
187
 
145
- print(f"Initialized PythonNative project in {cwd}.")
146
- print("Next: pn preview (desktop) | pn run android | pn run ios")
188
+ print(f"Initialized PythonNative project in {target}.")
189
+ next_steps = "pn preview (desktop) | pn run android | pn run ios"
190
+ if name:
191
+ next_steps = f"cd {name} | {next_steps}"
192
+ print(f"Next: {next_steps}")
147
193
 
148
194
 
149
195
  # ======================================================================
@@ -252,18 +298,46 @@ def _preview_entry(project_dir: Path) -> str:
252
298
  # ======================================================================
253
299
 
254
300
 
301
+ def _print_no_devices_hints(stream: TextIO) -> None:
302
+ """Print the "no devices" guidance to ``stream``.
303
+
304
+ Shared by both output modes so the wording can't drift between the
305
+ table (which sends it to stdout) and ``--json`` (stderr).
306
+
307
+ Args:
308
+ stream: Where to write, ``sys.stdout`` or ``sys.stderr``.
309
+ """
310
+ print("No devices found.", file=stream)
311
+ print("Android: start an emulator or connect a device with USB debugging enabled.", file=stream)
312
+ print("iOS: open Xcode once to install Simulators, or plug in a device.", file=stream)
313
+
314
+
255
315
  def devices_command(args: argparse.Namespace) -> None:
256
316
  """List connected devices, emulators, and simulators.
257
317
 
318
+ Prints an aligned table and exits 1 when nothing is connected.
319
+
320
+ With ``--json``, stdout carries a JSON array and nothing else, one
321
+ object per device (see ``Device.to_dict``), so it stays parseable.
322
+ The "no devices" hints go to stderr instead, an empty result prints
323
+ ``[]``, and the exit status is 0 either way, since "no devices" is a
324
+ valid answer for a script rather than a failure.
325
+
258
326
  Args:
259
- args: Parsed namespace with optional ``platform``.
327
+ args: Parsed namespace with optional ``platform`` and ``json``.
260
328
  """
261
329
  platform: Optional[str] = getattr(args, "platform", None)
330
+ as_json: bool = getattr(args, "json", False)
262
331
  devices = devices_mod.list_devices(platform)
332
+
333
+ if as_json:
334
+ if not devices:
335
+ _print_no_devices_hints(sys.stderr)
336
+ print(json.dumps([device.to_dict() for device in devices], indent=2))
337
+ return
338
+
263
339
  if not devices:
264
- print("No devices found.")
265
- print("Android: start an emulator or connect a device with USB debugging enabled.")
266
- print("iOS: open Xcode once to install Simulators, or plug in a device.")
340
+ _print_no_devices_hints(sys.stdout)
267
341
  sys.exit(1)
268
342
  print(f" {'IDENTIFIER':<40} {'KIND':<10} {'STATE':<10} NAME")
269
343
  for device in devices:
@@ -873,8 +947,8 @@ def _build_parser() -> argparse.ArgumentParser:
873
947
  subparsers = parser.add_subparsers()
874
948
 
875
949
  parser_init = subparsers.add_parser("init", help="Scaffold a new project")
876
- parser_init.add_argument("name", nargs="?", help="Project name (defaults to current directory name)")
877
- parser_init.add_argument("--force", action="store_true", help="Overwrite existing files if present")
950
+ parser_init.add_argument("name", nargs="?", help="Project name; creates ./<name>/ (default: current directory)")
951
+ parser_init.add_argument("--force", action="store_true", help="Overwrite existing files or a non-empty directory")
878
952
  parser_init.set_defaults(func=init_project)
879
953
 
880
954
  parser_doctor = subparsers.add_parser("doctor", help="Diagnose the local toolchain and config")
@@ -897,6 +971,9 @@ def _build_parser() -> argparse.ArgumentParser:
897
971
 
898
972
  parser_devices = subparsers.add_parser("devices", help="List devices, emulators, and simulators")
899
973
  parser_devices.add_argument("platform", nargs="?", choices=["android", "ios"], help="Restrict to a platform")
974
+ parser_devices.add_argument(
975
+ "--json", action="store_true", help="Print a JSON array to stdout for scripting (hints go to stderr)"
976
+ )
900
977
  parser_devices.set_defaults(func=devices_command)
901
978
 
902
979
  parser_run = subparsers.add_parser("run", help="Build, install, and launch on a device/simulator")
@@ -18,7 +18,7 @@ import json
18
18
  import re
19
19
  import subprocess
20
20
  import tempfile
21
- from dataclasses import dataclass
21
+ from dataclasses import asdict, dataclass
22
22
  from pathlib import Path
23
23
  from typing import Any, Dict, List, Optional
24
24
 
@@ -64,6 +64,19 @@ class Device:
64
64
  return self.state in ("booted", "shutdown")
65
65
  return self.state in ("booted", "connected")
66
66
 
67
+ def to_dict(self) -> Dict[str, Any]:
68
+ """Return a JSON-serializable view of this target.
69
+
70
+ Derived from the dataclass fields so a field added later is
71
+ carried into the payload automatically, plus the computed
72
+ ``is_ready`` that consumers would otherwise have to re-derive
73
+ from ``kind`` and ``state``.
74
+
75
+ Returns:
76
+ The declared fields in declaration order, then ``is_ready``.
77
+ """
78
+ return {**asdict(self), "is_ready": self.is_ready}
79
+
67
80
  def format(self) -> str:
68
81
  """Return one aligned listing row for the CLI."""
69
82
  os_part = f" ({self.os_version})" if self.os_version else ""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pythonnative
3
- Version: 0.29.0
3
+ Version: 0.31.0
4
4
  Summary: Cross-platform native UI toolkit for Android and iOS
5
5
  Author: Owen Carey
6
6
  License: MIT License