pythonnative 0.28.0__tar.gz → 0.30.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.28.0/src/pythonnative.egg-info → pythonnative-0.30.0}/PKG-INFO +1 -1
- {pythonnative-0.28.0 → pythonnative-0.30.0}/pyproject.toml +1 -1
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/__init__.py +1 -1
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/cli/pn.py +59 -13
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/project/doctor.py +21 -1
- {pythonnative-0.28.0 → pythonnative-0.30.0/src/pythonnative.egg-info}/PKG-INFO +1 -1
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_cli.py +145 -12
- {pythonnative-0.28.0 → pythonnative-0.30.0}/LICENSE +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/README.md +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/setup.cfg +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/_ios_log.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/alerts.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/animated.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/appearance.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/cli/__init__.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/components.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/diagnostics.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/element.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/events.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/gestures.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/hooks.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/hot_reload.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/images.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/layout.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/mutations.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/native_modules/__init__.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/native_modules/app_state.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/native_modules/battery.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/native_modules/biometrics.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/native_modules/camera.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/native_modules/clipboard.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/native_modules/file_system.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/native_modules/haptics.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/native_modules/linking.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/native_modules/location.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/native_modules/net_info.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/native_modules/notifications.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/native_modules/permissions.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/native_modules/secure_store.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/native_modules/share.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/native_views/__init__.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/native_views/android.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/native_views/base.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/native_views/desktop.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/native_views/ios.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/navigation.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/net.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/platform.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/platform_metrics.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/preview.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/project/__init__.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/project/android.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/project/builder.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/project/config.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/project/devices.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/project/icons.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/project/ios.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/project/permissions.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/project/runtime_assets.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/reconciler.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/runtime.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/screen.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/sdk/__init__.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/sdk/_components.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/storage.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/style.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/suspense.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/build.gradle +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/proguard-rules.pro +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/androidTest/java/com/pythonnative/android_template/ExampleInstrumentedTest.kt +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/AndroidManifest.xml +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/MainActivity.kt +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/Navigator.kt +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PNAccessibilityDelegate.kt +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PNBorderDrawable.kt +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PNFrameLayout.kt +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PNVirtualListView.java +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/ScreenFragment.kt +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/res/drawable/ic_launcher_background.xml +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/res/layout/activity_main.xml +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher.webp +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher.webp +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher.webp +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/res/navigation/nav_graph.xml +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/res/values/colors.xml +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/res/values/strings.xml +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/res/values/themes.xml +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/res/values-night/themes.xml +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/res/xml/backup_rules.xml +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/main/res/xml/data_extraction_rules.xml +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/app/src/test/java/com/pythonnative/android_template/ExampleUnitTest.kt +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/build.gradle +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.jar +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.properties +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/gradle.properties +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/gradlew +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/gradlew.bat +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/settings.gradle +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/ios_template/ios_template/AppDelegate.swift +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/AccentColor.colorset/Contents.json +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/ios_template/ios_template/Assets.xcassets/Contents.json +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/ios_template/ios_template/Base.lproj/LaunchScreen.storyboard +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/ios_template/ios_template/BridgingHeader.h +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/ios_template/ios_template/Info.plist +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/ios_template/ios_template/PythonRuntime.swift +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/ios_template/ios_template/SceneDelegate.swift +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/ios_template/ios_template/ViewController.swift +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/ios_template/ios_template.xcodeproj/project.pbxproj +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/ios_template/ios_template.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/ios_template/ios_templateTests/ios_templateTests.swift +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITests.swift +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/ios_template/ios_templateUITests/ios_templateUITestsLaunchTests.swift +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/utils.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/virtual_rows.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative.egg-info/SOURCES.txt +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative.egg-info/dependency_links.txt +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative.egg-info/entry_points.txt +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative.egg-info/requires.txt +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative.egg-info/top_level.txt +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_alert.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_animated.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_animated_graph.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_appearance.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_async_hooks.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_components.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_core_semantics.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_desktop_backend.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_element.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_events.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_extended_components.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_gesture_composition.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_gestures.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_hooks.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_hot_reload.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_images.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_incremental_render.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_interaction_props.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_ios_log.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_layout.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_metric_hooks.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_mutations.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_native_lists.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_native_modules.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_native_views.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_navigation.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_net.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_new_components.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_platform.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_platform_metrics.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_reconciler.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_ref.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_runtime.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_screen.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_sdk.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_smoke.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_storage.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_style.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_suspense.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_transitions.py +0 -0
- {pythonnative-0.28.0 → pythonnative-0.30.0}/tests/test_utils.py +0 -0
|
@@ -2,7 +2,9 @@
|
|
|
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`` +
|
|
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.
|
|
@@ -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
|
|
109
|
+
"""Scaffold a new PythonNative project.
|
|
108
110
|
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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 {
|
|
146
|
-
|
|
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
|
# ======================================================================
|
|
@@ -873,8 +919,8 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
873
919
|
subparsers = parser.add_subparsers()
|
|
874
920
|
|
|
875
921
|
parser_init = subparsers.add_parser("init", help="Scaffold a new project")
|
|
876
|
-
parser_init.add_argument("name", nargs="?", help="Project name (
|
|
877
|
-
parser_init.add_argument("--force", action="store_true", help="Overwrite existing files
|
|
922
|
+
parser_init.add_argument("name", nargs="?", help="Project name; creates ./<name>/ (default: current directory)")
|
|
923
|
+
parser_init.add_argument("--force", action="store_true", help="Overwrite existing files or a non-empty directory")
|
|
878
924
|
parser_init.set_defaults(func=init_project)
|
|
879
925
|
|
|
880
926
|
parser_doctor = subparsers.add_parser("doctor", help="Diagnose the local toolchain and config")
|
|
@@ -61,8 +61,17 @@ def _which_version(tool: str, version_args: List[str]) -> Optional[str]:
|
|
|
61
61
|
return text[0] if text else path
|
|
62
62
|
|
|
63
63
|
|
|
64
|
+
def _tkinter_available() -> bool:
|
|
65
|
+
"""Return whether the host Python can import Tkinter."""
|
|
66
|
+
try:
|
|
67
|
+
import tkinter # noqa: F401
|
|
68
|
+
except ImportError:
|
|
69
|
+
return False
|
|
70
|
+
return True
|
|
71
|
+
|
|
72
|
+
|
|
64
73
|
def check_common() -> List[CheckResult]:
|
|
65
|
-
"""Run platform-agnostic checks (interpreter
|
|
74
|
+
"""Run platform-agnostic checks (interpreter and optional dependencies).
|
|
66
75
|
|
|
67
76
|
Returns:
|
|
68
77
|
Check results for the host Python and optional dependencies.
|
|
@@ -89,6 +98,17 @@ def check_common() -> List[CheckResult]:
|
|
|
89
98
|
"not installed; run: pip install 'pythonnative[build]'",
|
|
90
99
|
)
|
|
91
100
|
)
|
|
101
|
+
if _tkinter_available():
|
|
102
|
+
results.append(CheckResult("Tkinter (desktop preview)", OK))
|
|
103
|
+
else:
|
|
104
|
+
results.append(
|
|
105
|
+
CheckResult(
|
|
106
|
+
"Tkinter (desktop preview)",
|
|
107
|
+
WARN,
|
|
108
|
+
"not installed; macOS: brew install python-tk; Debian/Ubuntu: sudo apt-get install python3-tk; "
|
|
109
|
+
"Windows: reinstall Python with the 'tcl/tk' option checked",
|
|
110
|
+
)
|
|
111
|
+
)
|
|
92
112
|
return results
|
|
93
113
|
|
|
94
114
|
|
|
@@ -36,21 +36,22 @@ def test_cli_init_and_clean() -> None:
|
|
|
36
36
|
try:
|
|
37
37
|
result = run_pn(["init", "MyApp"], tmpdir)
|
|
38
38
|
assert result.returncode == 0, result.stderr
|
|
39
|
-
|
|
39
|
+
project_dir = os.path.join(tmpdir, "MyApp")
|
|
40
|
+
assert os.path.isdir(os.path.join(project_dir, "app"))
|
|
40
41
|
|
|
41
|
-
main_path = os.path.join(
|
|
42
|
+
main_path = os.path.join(project_dir, "app", "main.py")
|
|
42
43
|
assert os.path.isfile(main_path)
|
|
43
44
|
content = Path(main_path).read_text(encoding="utf-8")
|
|
44
45
|
assert "def App(" in content
|
|
45
46
|
assert "Stack.Navigator" in content
|
|
46
47
|
|
|
47
|
-
config_path = os.path.join(
|
|
48
|
+
config_path = os.path.join(project_dir, "pythonnative.toml")
|
|
48
49
|
assert os.path.isfile(config_path)
|
|
49
50
|
toml_text = Path(config_path).read_text(encoding="utf-8")
|
|
50
51
|
assert 'id = "com.example.myapp"' in toml_text
|
|
51
|
-
assert os.path.isfile(os.path.join(
|
|
52
|
+
assert os.path.isfile(os.path.join(project_dir, ".gitignore"))
|
|
52
53
|
# The legacy JSON config and requirements.txt are no longer scaffolded.
|
|
53
|
-
assert not os.path.exists(os.path.join(
|
|
54
|
+
assert not os.path.exists(os.path.join(project_dir, "pythonnative.json"))
|
|
54
55
|
|
|
55
56
|
# clean on empty build is a no-op
|
|
56
57
|
result = run_pn(["clean"], tmpdir)
|
|
@@ -76,6 +77,136 @@ def test_cli_init_refuses_overwrite() -> None:
|
|
|
76
77
|
shutil.rmtree(tmpdir, ignore_errors=True)
|
|
77
78
|
|
|
78
79
|
|
|
80
|
+
def test_cli_init_creates_named_directory(tmp_path: Path) -> None:
|
|
81
|
+
result = run_pn(["init", "my_app"], str(tmp_path))
|
|
82
|
+
assert result.returncode == 0, result.stderr
|
|
83
|
+
assert "cd my_app" in result.stdout
|
|
84
|
+
|
|
85
|
+
project_dir = os.path.join(str(tmp_path), "my_app")
|
|
86
|
+
assert os.path.isfile(os.path.join(project_dir, "app", "main.py"))
|
|
87
|
+
assert os.path.isfile(os.path.join(project_dir, "pythonnative.toml"))
|
|
88
|
+
assert os.path.isfile(os.path.join(project_dir, ".gitignore"))
|
|
89
|
+
toml_text = Path(os.path.join(project_dir, "pythonnative.toml")).read_text(encoding="utf-8")
|
|
90
|
+
assert 'id = "com.example.my_app"' in toml_text
|
|
91
|
+
# Nothing is scaffolded beside the project directory.
|
|
92
|
+
assert os.listdir(str(tmp_path)) == ["my_app"]
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_cli_init_without_name_uses_cwd(tmp_path: Path) -> None:
|
|
96
|
+
project_dir = tmp_path / "widgets"
|
|
97
|
+
project_dir.mkdir()
|
|
98
|
+
|
|
99
|
+
result = run_pn(["init"], str(project_dir))
|
|
100
|
+
assert result.returncode == 0, result.stderr
|
|
101
|
+
assert "cd " not in result.stdout
|
|
102
|
+
|
|
103
|
+
assert os.path.isfile(os.path.join(str(project_dir), "app", "main.py"))
|
|
104
|
+
assert os.path.isfile(os.path.join(str(project_dir), ".gitignore"))
|
|
105
|
+
toml_text = Path(os.path.join(str(project_dir), "pythonnative.toml")).read_text(encoding="utf-8")
|
|
106
|
+
assert 'id = "com.example.widgets"' in toml_text
|
|
107
|
+
assert 'name = "widgets"' in toml_text
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_cli_init_refuses_non_empty_directory(tmp_path: Path) -> None:
|
|
111
|
+
project_dir = tmp_path / "my_app"
|
|
112
|
+
project_dir.mkdir()
|
|
113
|
+
keeper = project_dir / "README.md"
|
|
114
|
+
keeper.write_text("keep me\n", encoding="utf-8")
|
|
115
|
+
|
|
116
|
+
result = run_pn(["init", "my_app"], str(tmp_path))
|
|
117
|
+
assert result.returncode != 0
|
|
118
|
+
assert "Refusing to overwrite" in result.stdout
|
|
119
|
+
assert "non-empty directory" in result.stdout
|
|
120
|
+
assert not os.path.exists(os.path.join(str(project_dir), "app"))
|
|
121
|
+
assert keeper.read_text(encoding="utf-8") == "keep me\n"
|
|
122
|
+
|
|
123
|
+
result = run_pn(["init", "my_app", "--force"], str(tmp_path))
|
|
124
|
+
assert result.returncode == 0, result.stderr
|
|
125
|
+
assert os.path.isfile(os.path.join(str(project_dir), "app", "main.py"))
|
|
126
|
+
# --force scaffolds over the directory; it doesn't empty it first.
|
|
127
|
+
assert keeper.read_text(encoding="utf-8") == "keep me\n"
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def test_cli_init_accepts_existing_empty_directory(tmp_path: Path) -> None:
|
|
131
|
+
project_dir = tmp_path / "my_app"
|
|
132
|
+
project_dir.mkdir()
|
|
133
|
+
|
|
134
|
+
result = run_pn(["init", "my_app"], str(tmp_path))
|
|
135
|
+
assert result.returncode == 0, result.stderr
|
|
136
|
+
assert os.path.isfile(os.path.join(str(project_dir), "app", "main.py"))
|
|
137
|
+
assert os.path.isfile(os.path.join(str(project_dir), "pythonnative.toml"))
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def test_cli_init_refuses_existing_file(tmp_path: Path) -> None:
|
|
141
|
+
blocker = tmp_path / "my_app"
|
|
142
|
+
blocker.write_text("not a project\n", encoding="utf-8")
|
|
143
|
+
|
|
144
|
+
result = run_pn(["init", "my_app"], str(tmp_path))
|
|
145
|
+
assert result.returncode != 0
|
|
146
|
+
assert "Refusing to overwrite existing file" in result.stdout
|
|
147
|
+
|
|
148
|
+
# --force can't turn a file into a directory, so it is refused too.
|
|
149
|
+
result = run_pn(["init", "my_app", "--force"], str(tmp_path))
|
|
150
|
+
assert result.returncode != 0
|
|
151
|
+
assert "Refusing to overwrite existing file" in result.stdout
|
|
152
|
+
assert blocker.read_text(encoding="utf-8") == "not a project\n"
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@pytest.mark.parametrize("name", ["{absolute}", "nested/my_app", "my_app/", ".", "..", "../", "a/.."])
|
|
156
|
+
def test_cli_init_rejects_path_like_names(tmp_path: Path, name: str) -> None:
|
|
157
|
+
work_dir = tmp_path / "work"
|
|
158
|
+
work_dir.mkdir()
|
|
159
|
+
|
|
160
|
+
result = run_pn(["init", name.format(absolute=str(tmp_path / "elsewhere"))], str(work_dir))
|
|
161
|
+
assert result.returncode != 0
|
|
162
|
+
assert "Refusing to" in result.stdout
|
|
163
|
+
assert "project name" in result.stdout
|
|
164
|
+
# Nothing was created in the working directory or anywhere above it.
|
|
165
|
+
assert os.listdir(str(work_dir)) == []
|
|
166
|
+
assert os.listdir(str(tmp_path)) == ["work"]
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def test_cli_init_force_does_not_escape_to_parent(tmp_path: Path) -> None:
|
|
170
|
+
parent_config = tmp_path / "pythonnative.toml"
|
|
171
|
+
parent_config.write_text("# hand-written\n", encoding="utf-8")
|
|
172
|
+
work_dir = tmp_path / "work"
|
|
173
|
+
work_dir.mkdir()
|
|
174
|
+
|
|
175
|
+
result = run_pn(["init", "..", "--force"], str(work_dir))
|
|
176
|
+
assert result.returncode != 0
|
|
177
|
+
assert "Refusing to" in result.stdout
|
|
178
|
+
# --force does not lift the single-name rule, so the parent is untouched.
|
|
179
|
+
assert parent_config.read_text(encoding="utf-8") == "# hand-written\n"
|
|
180
|
+
assert not os.path.exists(os.path.join(str(tmp_path), "app"))
|
|
181
|
+
assert not os.path.exists(os.path.join(str(tmp_path), ".gitignore"))
|
|
182
|
+
assert os.listdir(str(work_dir)) == []
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
@pytest.mark.parametrize("extra_args", [[], ["--force"]])
|
|
186
|
+
@pytest.mark.parametrize("populated", [True, False])
|
|
187
|
+
def test_cli_init_rejects_symlinked_target(tmp_path: Path, populated: bool, extra_args: List[str]) -> None:
|
|
188
|
+
outside = tmp_path / "outside"
|
|
189
|
+
outside.mkdir()
|
|
190
|
+
outside_config = outside / "pythonnative.toml"
|
|
191
|
+
if populated:
|
|
192
|
+
outside_config.write_text("# hand-written\n", encoding="utf-8")
|
|
193
|
+
before = sorted(os.listdir(str(outside)))
|
|
194
|
+
|
|
195
|
+
work_dir = tmp_path / "work"
|
|
196
|
+
work_dir.mkdir()
|
|
197
|
+
os.symlink(str(outside), os.path.join(str(work_dir), "link"))
|
|
198
|
+
|
|
199
|
+
result = run_pn(["init", "link"] + extra_args, str(work_dir))
|
|
200
|
+
assert result.returncode != 0
|
|
201
|
+
assert "Refusing to" in result.stdout
|
|
202
|
+
# exists() and is_dir() follow symlinks, so the destination must stay untouched:
|
|
203
|
+
# no new entries, and no rewrite of a config that was already there.
|
|
204
|
+
assert sorted(os.listdir(str(outside))) == before
|
|
205
|
+
if populated:
|
|
206
|
+
assert outside_config.read_text(encoding="utf-8") == "# hand-written\n"
|
|
207
|
+
assert os.listdir(str(work_dir)) == ["link"]
|
|
208
|
+
|
|
209
|
+
|
|
79
210
|
def test_cli_run_help_lists_flags() -> None:
|
|
80
211
|
tmpdir = tempfile.mkdtemp(prefix="pn_cli_test_")
|
|
81
212
|
try:
|
|
@@ -119,15 +250,16 @@ def test_cli_run_without_config_errors() -> None:
|
|
|
119
250
|
|
|
120
251
|
def test_cli_app_id_resolves(tmp_path: Path) -> None:
|
|
121
252
|
assert run_pn(["init", "MyApp"], str(tmp_path)).returncode == 0
|
|
122
|
-
|
|
253
|
+
project_dir = str(tmp_path / "MyApp")
|
|
254
|
+
result = run_pn(["app-id", "android"], project_dir)
|
|
123
255
|
assert result.returncode == 0, result.stderr
|
|
124
256
|
assert result.stdout.strip() == "com.example.myapp"
|
|
125
|
-
assert run_pn(["app-id", "ios"],
|
|
257
|
+
assert run_pn(["app-id", "ios"], project_dir).stdout.strip() == "com.example.myapp"
|
|
126
258
|
|
|
127
259
|
|
|
128
260
|
def test_cli_doctor_runs(tmp_path: Path) -> None:
|
|
129
261
|
assert run_pn(["init", "MyApp"], str(tmp_path)).returncode == 0
|
|
130
|
-
result = run_pn(["doctor", "android"], str(tmp_path))
|
|
262
|
+
result = run_pn(["doctor", "android"], str(tmp_path / "MyApp"))
|
|
131
263
|
assert "PythonNative doctor" in result.stdout
|
|
132
264
|
# android-only doctor on a CI box without adb still produces warnings, not errors.
|
|
133
265
|
assert result.returncode in (0, 1)
|
|
@@ -137,10 +269,11 @@ def test_cli_run_prepare_only_android_and_ios() -> None:
|
|
|
137
269
|
tmpdir = tempfile.mkdtemp(prefix="pn_cli_test_")
|
|
138
270
|
try:
|
|
139
271
|
assert run_pn(["init", "MyApp"], tmpdir).returncode == 0
|
|
272
|
+
project_dir = os.path.join(tmpdir, "MyApp")
|
|
140
273
|
|
|
141
|
-
result = run_pn(["run", "android", "--prepare-only", "--no-logs"],
|
|
274
|
+
result = run_pn(["run", "android", "--prepare-only", "--no-logs"], project_dir)
|
|
142
275
|
assert result.returncode == 0, result.stderr
|
|
143
|
-
android_root = os.path.join(
|
|
276
|
+
android_root = os.path.join(project_dir, "build", "android", "android_template")
|
|
144
277
|
assert os.path.isdir(android_root)
|
|
145
278
|
# Package relocated to the configured application id.
|
|
146
279
|
relocated = os.path.join(
|
|
@@ -154,9 +287,9 @@ def test_cli_run_prepare_only_android_and_ios() -> None:
|
|
|
154
287
|
gradle = Path(os.path.join(android_root, "app", "build.gradle")).read_text(encoding="utf-8")
|
|
155
288
|
assert "com.example.myapp" in gradle
|
|
156
289
|
|
|
157
|
-
result = run_pn(["run", "ios", "--prepare-only", "--no-logs"],
|
|
290
|
+
result = run_pn(["run", "ios", "--prepare-only", "--no-logs"], project_dir)
|
|
158
291
|
assert result.returncode == 0, result.stderr
|
|
159
|
-
ios_root = os.path.join(
|
|
292
|
+
ios_root = os.path.join(project_dir, "build", "ios", "ios_template")
|
|
160
293
|
assert os.path.isdir(ios_root)
|
|
161
294
|
info_plist = Path(os.path.join(ios_root, "ios_template", "Info.plist")).read_bytes()
|
|
162
295
|
assert b"CFBundleDisplayName" in info_plist
|
|
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.28.0 → pythonnative-0.30.0}/src/pythonnative/native_modules/notifications.py
RENAMED
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/build.gradle
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/gradlew
RENAMED
|
File without changes
|
{pythonnative-0.28.0 → pythonnative-0.30.0}/src/pythonnative/templates/android_template/gradlew.bat
RENAMED
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|