pythonnative 0.37.0__py3-none-any.whl → 0.38.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. pythonnative/__init__.py +9 -1
  2. pythonnative/alerts.py +6 -14
  3. pythonnative/cli/pn.py +96 -10
  4. pythonnative/components/_base.py +31 -0
  5. pythonnative/components/controls.py +19 -16
  6. pythonnative/components/layout.py +5 -8
  7. pythonnative/components/lists.py +6 -6
  8. pythonnative/gestures.py +27 -6
  9. pythonnative/native_modules/__init__.py +19 -0
  10. pythonnative/native_modules/battery.py +13 -14
  11. pythonnative/native_modules/biometrics.py +12 -14
  12. pythonnative/native_modules/camera.py +10 -6
  13. pythonnative/native_modules/clipboard.py +9 -11
  14. pythonnative/native_modules/file_system.py +81 -172
  15. pythonnative/native_modules/haptics.py +14 -10
  16. pythonnative/native_modules/linking.py +9 -8
  17. pythonnative/native_modules/location.py +10 -12
  18. pythonnative/native_modules/net_info.py +1 -4
  19. pythonnative/native_modules/notifications.py +25 -32
  20. pythonnative/native_modules/permissions.py +70 -22
  21. pythonnative/native_modules/secure_store.py +16 -20
  22. pythonnative/native_modules/share.py +5 -5
  23. pythonnative/navigation/__init__.py +2 -1
  24. pythonnative/navigation/hooks.py +46 -7
  25. pythonnative/navigation/state.py +30 -9
  26. pythonnative/project/android.py +23 -0
  27. pythonnative/project/builder.py +64 -35
  28. pythonnative/project/config.py +84 -24
  29. pythonnative/project/deps.py +703 -0
  30. pythonnative/project/doctor.py +53 -4
  31. pythonnative/project/runtime_assets.py +13 -14
  32. pythonnative/style.py +94 -44
  33. pythonnative/templates/android_template/app/build.gradle +5 -2
  34. pythonnative/templates/android_template/build.gradle +7 -4
  35. pythonnative/templates/android_template/gradle/wrapper/gradle-wrapper.properties +1 -1
  36. pythonnative/templates/android_template/pythonnative/src/main/java/com/pythonnative/runtime/modules/PermissionsModule.kt +15 -4
  37. pythonnative/templates/ios_template/PythonNativeKit/Sources/PythonNativeKit/Modules/PermissionModules.swift +25 -10
  38. pythonnative/templates/ios_template/ios_template/PythonRuntime.swift +3 -2
  39. pythonnative/templates/ios_template/ios_template.xcodeproj/project.pbxproj +35 -9
  40. pythonnative/utils.py +22 -27
  41. pythonnative/virtual_rows.py +10 -20
  42. {pythonnative-0.37.0.dist-info → pythonnative-0.38.0.dist-info}/METADATA +8 -8
  43. {pythonnative-0.37.0.dist-info → pythonnative-0.38.0.dist-info}/RECORD +47 -46
  44. {pythonnative-0.37.0.dist-info → pythonnative-0.38.0.dist-info}/WHEEL +0 -0
  45. {pythonnative-0.37.0.dist-info → pythonnative-0.38.0.dist-info}/entry_points.txt +0 -0
  46. {pythonnative-0.37.0.dist-info → pythonnative-0.38.0.dist-info}/licenses/LICENSE +0 -0
  47. {pythonnative-0.37.0.dist-info → pythonnative-0.38.0.dist-info}/top_level.txt +0 -0
@@ -18,7 +18,7 @@ name = "myapp" # short project name (required)
18
18
  display_name = "My App" # home-screen label (defaults to name)
19
19
  version = "1.0.0" # marketing version
20
20
  build = 1 # integer build number
21
- python_version = "3.11" # embedded CPython version
21
+ python_version = "3.13" # embedded CPython version (3.13 or 3.14)
22
22
  orientation = "portrait" # portrait | landscape | all
23
23
  entry_point = "app/main.py" # module whose `App` is mounted
24
24
  url_schemes = ["myapp"] # custom deep-link URL schemes
@@ -33,6 +33,9 @@ splash = "assets/splash.png" # splash/launch image
33
33
 
34
34
  [requirements]
35
35
  packages = ["humanize", "httpx"]
36
+ # Extra package indexes searched after PyPI and the platform wheel
37
+ # repositories (BeeWare for iOS, Chaquopy for Android).
38
+ extra_index_urls = ["https://wheels.example.com/simple"]
36
39
 
37
40
  [plugins]
38
41
  paths = ["native/my_plugin"] # local native plugins (pn_plugin.json + ios/ + android/)
@@ -49,7 +52,7 @@ provisioning_profile = "My App Distribution"
49
52
  [android]
50
53
  min_sdk = 24
51
54
  target_sdk = 34
52
- abi_filters = ["arm64-v8a", "x86_64"] # the default; add armeabi-v7a/x86 if needed
55
+ abi_filters = ["arm64-v8a", "x86_64"] # the default and the full set (64-bit only)
53
56
 
54
57
  [android.signing]
55
58
  keystore = "release.keystore"
@@ -60,32 +63,62 @@ key_alias = "myapp"
60
63
  from __future__ import annotations
61
64
 
62
65
  import re
66
+ import tomllib as _toml
63
67
  from dataclasses import dataclass, field
64
68
  from pathlib import Path
65
69
  from typing import Any, Dict, List, Mapping, Optional
66
70
 
67
71
  from . import permissions as _permissions
68
72
 
69
- try: # Python 3.11+
70
- import tomllib as _toml
71
- except ModuleNotFoundError: # Python 3.10
72
- import tomli as _toml
73
-
74
-
75
73
  CONFIG_FILENAME = "pythonnative.toml"
76
74
  """The fixed config filename looked up at the project root."""
77
75
 
78
- SUPPORTED_PYTHON_VERSIONS = ("3.10", "3.11", "3.12")
76
+ SUPPORTED_PYTHON_VERSIONS = ("3.13", "3.14")
79
77
  """CPython versions accepted in ``app.python_version``.
80
78
 
81
79
  Every listed version has a pinned, checksum-verified iOS runtime (see
82
80
  ``runtime_assets.PINNED_ASSETS``) and is supported by Chaquopy on
83
- Android.
81
+ Android. The floor is 3.13 because that is the first CPython with
82
+ official iOS (PEP 730) and Android (PEP 738) platform support, which is
83
+ what lets binary wheels from PyPI install on both platforms.
84
84
  """
85
85
 
86
+ DEFAULT_PYTHON_VERSION = "3.13"
87
+ """The ``app.python_version`` used when the config leaves it out.
88
+
89
+ 3.13 rather than 3.14 because binary wheel coverage (BeeWare's iOS
90
+ index, Chaquopy's Android index, PyPI itself) is far wider for 3.13.
91
+ """
92
+
93
+ _URL_RE = re.compile(r"^https?://\S+$")
94
+
86
95
  VALID_ORIENTATIONS = ("portrait", "landscape", "all")
87
96
  """Accepted values for ``app.orientation``."""
88
97
 
98
+ MIN_IOS_DEPLOYMENT_TARGET = "13.0"
99
+ """Lowest ``[ios].deployment_target`` accepted.
100
+
101
+ BeeWare's CPython builds and every iOS wheel on PyPI target iOS 13.0
102
+ (the ``ios_13_0_*`` platform tags), so lower values can't be satisfied.
103
+ """
104
+
105
+ MIN_ANDROID_SDK = 24
106
+ """Lowest ``[android].min_sdk`` accepted (Chaquopy 17 requirement)."""
107
+
108
+ VALID_ANDROID_ABIS = ("arm64-v8a", "x86_64")
109
+ """Android ABIs the embedded runtime supports.
110
+
111
+ CPython 3.12+ on Chaquopy ships 64-bit only, and PEP 738 Android wheels
112
+ on PyPI are published for these two ABIs.
113
+ """
114
+
115
+ _IOS_VERSION_RE = re.compile(r"^\d+(\.\d+){1,2}$")
116
+
117
+
118
+ def _version_tuple(version: str) -> tuple:
119
+ return tuple(int(part) for part in version.split("."))
120
+
121
+
89
122
  VALID_IOS_EXPORT_METHODS = ("development", "ad-hoc", "app-store", "enterprise")
90
123
  """Accepted values for ``[ios.signing].export_method``."""
91
124
 
@@ -200,9 +233,9 @@ class AndroidConfig:
200
233
  target_sdk: int = 34
201
234
  compile_sdk: int = 34
202
235
  application_id: Optional[str] = None
203
- # arm64 devices plus x86_64 emulators. 32-bit ABIs (armeabi-v7a, x86)
204
- # roughly double the native payload and are opt-in.
205
- abi_filters: List[str] = field(default_factory=lambda: ["arm64-v8a", "x86_64"])
236
+ # arm64 devices plus x86_64 emulators: the only ABIs CPython 3.12+
237
+ # on Chaquopy (and PEP 738 wheels on PyPI) are built for.
238
+ abi_filters: List[str] = field(default_factory=lambda: list(VALID_ANDROID_ABIS))
206
239
  permissions: List[str] = field(default_factory=list)
207
240
  signing: AndroidSigning = field(default_factory=AndroidSigning)
208
241
 
@@ -238,6 +271,9 @@ class AppConfig:
238
271
  icon: Optional source icon path (``[assets].icon``).
239
272
  splash: Optional splash image path (``[assets].splash``).
240
273
  requirements: Third-party pip packages (``[requirements].packages``).
274
+ extra_index_urls: Additional package indexes
275
+ (``[requirements].extra_index_urls``) searched after PyPI and
276
+ the platform wheel repositories.
241
277
  plugin_paths: Project-local native plugin directories
242
278
  (``[plugins].paths``); installed packages contribute plugins
243
279
  through the ``pythonnative.plugins`` entry point group instead.
@@ -252,7 +288,7 @@ class AppConfig:
252
288
  display_name: str
253
289
  version: str = "1.0.0"
254
290
  build: int = 1
255
- python_version: str = "3.11"
291
+ python_version: str = DEFAULT_PYTHON_VERSION
256
292
  orientation: str = "portrait"
257
293
  entry_point: str = "app/main.py"
258
294
  url_schemes: List[str] = field(default_factory=list)
@@ -260,6 +296,7 @@ class AppConfig:
260
296
  icon: Optional[str] = None
261
297
  splash: Optional[str] = None
262
298
  requirements: List[str] = field(default_factory=list)
299
+ extra_index_urls: List[str] = field(default_factory=list)
263
300
  plugin_paths: List[str] = field(default_factory=list)
264
301
  ios: IOSConfig = field(default_factory=IOSConfig)
265
302
  android: AndroidConfig = field(default_factory=AndroidConfig)
@@ -372,7 +409,7 @@ class AppConfig:
372
409
  display_name=_opt_str(app, "display_name") or _require_str(app, "name", "app"),
373
410
  version=_opt_str(app, "version") or "1.0.0",
374
411
  build=_opt_int(app, "build", default=1),
375
- python_version=_opt_str(app, "python_version") or "3.11",
412
+ python_version=_opt_str(app, "python_version") or DEFAULT_PYTHON_VERSION,
376
413
  orientation=(_opt_str(app, "orientation") or "portrait").lower(),
377
414
  entry_point=_opt_str(app, "entry_point") or "app/main.py",
378
415
  url_schemes=_opt_str_list(app, "url_schemes"),
@@ -388,6 +425,7 @@ class AppConfig:
388
425
 
389
426
  requirements = _expect_table(data, "requirements", optional=True)
390
427
  config.requirements = _opt_str_list(requirements, "packages")
428
+ config.extra_index_urls = _opt_str_list(requirements, "extra_index_urls")
391
429
 
392
430
  plugins = _expect_table(data, "plugins", optional=True)
393
431
  config.plugin_paths = _opt_str_list(plugins, "paths")
@@ -440,17 +478,36 @@ class AppConfig:
440
478
  raise ConfigError("Unknown permission(s): " + ", ".join(unknown) + f".\nValid capabilities: {known}")
441
479
 
442
480
  _validate_requirements(self.requirements)
481
+ for url in self.extra_index_urls:
482
+ if not _URL_RE.match(url):
483
+ raise ConfigError(
484
+ f"[requirements].extra_index_urls entry {url!r} is invalid; use a full http(s) URL "
485
+ "to a PEP 503 simple index (e.g. 'https://wheels.example.com/simple')."
486
+ )
443
487
 
444
488
  if self.ios.signing.export_method not in VALID_IOS_EXPORT_METHODS:
445
489
  valid = ", ".join(VALID_IOS_EXPORT_METHODS)
446
490
  raise ConfigError(
447
491
  f"[ios.signing].export_method {self.ios.signing.export_method!r} is invalid (choose one of: {valid})."
448
492
  )
493
+ if not _IOS_VERSION_RE.match(self.ios.deployment_target):
494
+ raise ConfigError(
495
+ f"[ios].deployment_target {self.ios.deployment_target!r} must look like '13.0' or '16.4'."
496
+ )
497
+ if _version_tuple(self.ios.deployment_target) < _version_tuple(MIN_IOS_DEPLOYMENT_TARGET):
498
+ raise ConfigError(
499
+ f"[ios].deployment_target must be at least {MIN_IOS_DEPLOYMENT_TARGET} "
500
+ "(the embedded CPython runtime and iOS binary wheels target that floor)."
501
+ )
449
502
 
450
- if self.android.min_sdk < 21:
451
- raise ConfigError("[android].min_sdk must be at least 21 (Chaquopy requirement).")
503
+ if self.android.min_sdk < MIN_ANDROID_SDK:
504
+ raise ConfigError(f"[android].min_sdk must be at least {MIN_ANDROID_SDK} (Chaquopy requirement).")
452
505
  if self.android.target_sdk < self.android.min_sdk:
453
506
  raise ConfigError("[android].target_sdk must be >= min_sdk.")
507
+ for abi in self.android.abi_filters:
508
+ if abi not in VALID_ANDROID_ABIS:
509
+ valid = ", ".join(VALID_ANDROID_ABIS)
510
+ raise ConfigError(f"[android].abi_filters entry {abi!r} is invalid (choose from: {valid}).")
454
511
 
455
512
 
456
513
  # ======================================================================
@@ -635,7 +692,7 @@ def _toml_escape(value: str) -> str:
635
692
  return "".join(out)
636
693
 
637
694
 
638
- def render_default_toml(*, name: str, app_id: str, python_version: str = "3.11") -> str:
695
+ def render_default_toml(*, name: str, app_id: str, python_version: str = DEFAULT_PYTHON_VERSION) -> str:
639
696
  """Render a starter ``pythonnative.toml`` for ``pn init``.
640
697
 
641
698
  Every interpolated value is escaped for a TOML basic string, so a
@@ -686,13 +743,16 @@ entry_point = "app/main.py"
686
743
  # icon = "assets/icon.png"
687
744
  # splash = "assets/splash.png"
688
745
 
689
- # Third-party pip packages bundled into the app (pure-Python or, on
690
- # Android, anything Chaquopy can build). Do NOT list "pythonnative".
746
+ # Third-party pip packages bundled into the app. Pure-Python packages
747
+ # always work; packages with C extensions need a wheel for each device
748
+ # platform. Run `pn deps` to see what resolves before you build.
749
+ # Do NOT list "pythonnative". Docs: https://pythonnative.com/guides/pypi-packages/
691
750
  [requirements]
692
751
  packages = []
752
+ # extra_index_urls = ["https://wheels.example.com/simple"]
693
753
 
694
754
  [ios]
695
- deployment_target = "13.0"
755
+ deployment_target = "{MIN_IOS_DEPLOYMENT_TARGET}"
696
756
  # development_team = "ABCDE12345"
697
757
  # bundle_id = "{app_id}"
698
758
 
@@ -701,10 +761,10 @@ export_method = "development" # development | ad-hoc | app-store | enterprise
701
761
  # provisioning_profile = "My App Distribution"
702
762
 
703
763
  [android]
704
- min_sdk = 24
764
+ min_sdk = {MIN_ANDROID_SDK}
705
765
  target_sdk = 34
706
- # arm64 devices + x86_64 emulators (the default). Add "armeabi-v7a" or
707
- # "x86" only if you must support 32-bit hardware; each ABI adds ~30 MB.
766
+ # arm64 devices + x86_64 emulators. These are the only ABIs the embedded
767
+ # CPython (3.12+) and Android wheels on PyPI ship for.
708
768
  # abi_filters = ["arm64-v8a", "x86_64"]
709
769
 
710
770
  [android.signing]