flet-cli 1.1.0.dev2__tar.gz → 1.1.0.dev3__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 (98) hide show
  1. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/PKG-INFO +3 -3
  2. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/pyproject.toml +3 -3
  3. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/commands/build_base.py +50 -11
  4. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/commands/flutter_base.py +1 -0
  5. flet_cli-1.1.0.dev3/src/flet_cli/version.py +1 -0
  6. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli.egg-info/PKG-INFO +3 -3
  7. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli.egg-info/requires.txt +2 -2
  8. flet_cli-1.1.0.dev3/tests/test_build_package_cache.py +332 -0
  9. flet_cli-1.1.0.dev2/src/flet_cli/version.py +0 -1
  10. flet_cli-1.1.0.dev2/tests/test_build_package_cache.py +0 -103
  11. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/README.md +0 -0
  12. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/setup.cfg +0 -0
  13. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/__pyinstaller/__init__.py +0 -0
  14. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/__pyinstaller/config.py +0 -0
  15. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/__pyinstaller/extensions.py +0 -0
  16. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/__pyinstaller/hook-flet.py +0 -0
  17. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/__pyinstaller/hook-flet_desktop.py +0 -0
  18. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/__pyinstaller/macos_utils.py +0 -0
  19. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/__pyinstaller/rthooks/pyi_rth_localhost_fletd.py +0 -0
  20. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/__pyinstaller/rthooks.dat +0 -0
  21. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/__pyinstaller/utils.py +0 -0
  22. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/__pyinstaller/win_utils.py +0 -0
  23. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/cli.py +0 -0
  24. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/commands/base.py +0 -0
  25. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/commands/build.py +0 -0
  26. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/commands/clean.py +0 -0
  27. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/commands/create.py +0 -0
  28. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/commands/debug.py +0 -0
  29. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/commands/devices.py +0 -0
  30. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/commands/doctor.py +0 -0
  31. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/commands/emulators.py +0 -0
  32. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/commands/mcp.py +0 -0
  33. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/commands/options.py +0 -0
  34. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/commands/pack.py +0 -0
  35. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/commands/publish.py +0 -0
  36. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/commands/run.py +0 -0
  37. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/commands/serve.py +0 -0
  38. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/commands/test.py +0 -0
  39. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/commands/test_host.py +0 -0
  40. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/utils/android.py +0 -0
  41. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/utils/android_sdk.py +0 -0
  42. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/utils/app_excludes.py +0 -0
  43. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/utils/cli.py +0 -0
  44. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/utils/distros.py +0 -0
  45. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/utils/examples.py +0 -0
  46. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/utils/extension_settings.py +0 -0
  47. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/utils/flutter.py +0 -0
  48. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/utils/hash_stamp.py +0 -0
  49. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/utils/ios_sign.py +0 -0
  50. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/utils/jdk.py +0 -0
  51. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/utils/linux_deps.py +0 -0
  52. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/utils/log_format.py +0 -0
  53. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/utils/macos_sign.py +0 -0
  54. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/utils/merge.py +0 -0
  55. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/utils/plist.py +0 -0
  56. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/utils/processes.py +0 -0
  57. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/utils/project_dependencies.py +0 -0
  58. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/utils/pyodide.py +0 -0
  59. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/utils/pyproject_toml.py +0 -0
  60. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/utils/python_versions.py +0 -0
  61. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli/utils/template_cache.py +0 -0
  62. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli.egg-info/SOURCES.txt +0 -0
  63. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli.egg-info/dependency_links.txt +0 -0
  64. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli.egg-info/entry_points.txt +0 -0
  65. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/src/flet_cli.egg-info/top_level.txt +0 -0
  66. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_app_excludes.py +0 -0
  67. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_build_android.py +0 -0
  68. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_build_app_path.py +0 -0
  69. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_build_copy_output.py +0 -0
  70. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_build_icons.py +0 -0
  71. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_build_ios.py +0 -0
  72. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_build_linux.py +0 -0
  73. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_build_list_options.py +0 -0
  74. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_build_macos_signing.py +0 -0
  75. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_build_platform_settings.py +0 -0
  76. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_build_splash.py +0 -0
  77. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_build_svg.py +0 -0
  78. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_build_template.py +0 -0
  79. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_build_version.py +0 -0
  80. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_build_windows_arm64.py +0 -0
  81. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_clean.py +0 -0
  82. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_cli_pass_through_args.py +0 -0
  83. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_cli_positionals.py +0 -0
  84. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_create.py +0 -0
  85. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_extension_settings.py +0 -0
  86. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_find_platform_image.py +0 -0
  87. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_flutter.py +0 -0
  88. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_linux_desktop_entry.py +0 -0
  89. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_log_format.py +0 -0
  90. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_macos_sign.py +0 -0
  91. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_pack_extensions.py +0 -0
  92. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_plist.py +0 -0
  93. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_project_dependencies.py +0 -0
  94. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_python_versions.py +0 -0
  95. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_run_assets_dir.py +0 -0
  96. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_run_desktop_dependency.py +0 -0
  97. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_run_script_args.py +0 -0
  98. {flet_cli-1.1.0.dev2 → flet_cli-1.1.0.dev3}/tests/test_run_watcher_unavailable.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flet-cli
3
- Version: 1.1.0.dev2
3
+ Version: 1.1.0.dev3
4
4
  Summary: Flet CLI
5
5
  Author-email: "Appveyor Systems Inc." <hello@flet.dev>
6
6
  License-Expression: Apache-2.0
@@ -9,8 +9,8 @@ Project-URL: Repository, https://github.com/flet-dev/flet
9
9
  Project-URL: Documentation, https://flet.dev/docs
10
10
  Requires-Python: >=3.10
11
11
  Description-Content-Type: text/markdown
12
- Requires-Dist: flet==1.1.0.dev2
13
- Requires-Dist: flet-platform-assets==1.1.0.dev2
12
+ Requires-Dist: flet==1.1.0.dev3
13
+ Requires-Dist: flet-platform-assets==1.1.0.dev3
14
14
  Requires-Dist: watchdog>=4.0.0
15
15
  Requires-Dist: packaging>=25.0
16
16
  Requires-Dist: qrcode>=7.4.2
@@ -1,14 +1,14 @@
1
1
  [project]
2
2
  name = "flet-cli"
3
- version = "1.1.0.dev2"
3
+ version = "1.1.0.dev3"
4
4
  description = "Flet CLI"
5
5
  authors = [{ name = "Appveyor Systems Inc.", email = "hello@flet.dev" }]
6
6
  license = "Apache-2.0"
7
7
  readme = "README.md"
8
8
  requires-python = ">=3.10"
9
9
  dependencies = [
10
- "flet==1.1.0.dev2",
11
- "flet-platform-assets==1.1.0.dev2",
10
+ "flet==1.1.0.dev3",
11
+ "flet-platform-assets==1.1.0.dev3",
12
12
  "watchdog >=4.0.0",
13
13
  "packaging >=25.0",
14
14
  "qrcode >=7.4.2",
@@ -1886,6 +1886,13 @@ class BaseBuildCommand(BaseFlutterCommand):
1886
1886
  assert self.build_dir
1887
1887
  assert self.pubspec_path
1888
1888
 
1889
+ # A deleted extension in build/flutter-packages would make this build
1890
+ # fail before it could reinstall it: start over with a fresh project,
1891
+ # fresh packages, and regenerated icons and splash images.
1892
+ if not second_pass and self._flutter_packages_missing():
1893
+ for stamp in ("template-1", "template-2", "package", "icons", "splashes"):
1894
+ (self.build_dir / ".hash" / stamp).unlink(missing_ok=True)
1895
+
1889
1896
  hash = HashStamp(
1890
1897
  self.build_dir / ".hash" / f"template-{'2' if second_pass else '1'}"
1891
1898
  )
@@ -2399,6 +2406,38 @@ class BaseBuildCommand(BaseFlutterCommand):
2399
2406
  return AssetSpec(ico_sizes=sizes)
2400
2407
  return None
2401
2408
 
2409
+ def _flutter_packages_missing(self) -> bool:
2410
+ """
2411
+ Whether the Flutter project depends on an extension that is gone.
2412
+
2413
+ The previous build's `pubspec.yaml` lists the Flutter packages of the
2414
+ app's extensions by their path in `build/flutter-packages`. If one of
2415
+ them was deleted since, resolving that `pubspec.yaml` fails, and it is
2416
+ resolved before the packages could be installed again, so the build
2417
+ can't recover on its own. The build then starts over: the Flutter
2418
+ project is rendered again, which also restores the template's icons
2419
+ and splash images, so those are generated again too.
2420
+
2421
+ Returns:
2422
+ `True` when a dependency's path is inside `build/flutter-packages`
2423
+ and has no `pubspec.yaml`.
2424
+ """
2425
+
2426
+ assert self.pubspec_path
2427
+ assert self.build_dir
2428
+
2429
+ if not os.path.exists(self.pubspec_path):
2430
+ return False
2431
+ pubspec = self.load_yaml(self.pubspec_path) or {}
2432
+ packages_dir = (self.build_dir / "flutter-packages").resolve()
2433
+ for dependency in (pubspec.get("dependencies") or {}).values():
2434
+ if not isinstance(dependency, dict) or "path" not in dependency:
2435
+ continue
2436
+ path = (Path(self.pubspec_path).parent / dependency["path"]).resolve()
2437
+ if packages_dir in path.parents and not (path / "pubspec.yaml").exists():
2438
+ return True
2439
+ return False
2440
+
2402
2441
  def _site_packages_missing(self) -> bool:
2403
2442
  """
2404
2443
  Whether any required platform package directory has no package files.
@@ -2947,17 +2986,17 @@ class BaseBuildCommand(BaseFlutterCommand):
2947
2986
  for arg in package_args:
2948
2987
  hash.update(arg)
2949
2988
 
2950
- if not dev_packages_configured:
2951
- if not hash.has_changed() and not self._site_packages_missing():
2952
- package_args.append("--skip-site-packages")
2953
- # serious_python skips copying Flutter packages to the temp dir
2954
- # under --skip-site-packages, so register_flutter_extensions must
2955
- # keep (not wipe) the permanent flutter-packages copy from the
2956
- # previous build.
2957
- self.site_packages_skipped = True
2958
- else:
2959
- if self.flutter_packages_dir.exists():
2960
- shutil.rmtree(self.flutter_packages_dir, ignore_errors=True)
2989
+ if (
2990
+ not dev_packages_configured
2991
+ and not hash.has_changed()
2992
+ and not self._site_packages_missing()
2993
+ ):
2994
+ package_args.append("--skip-site-packages")
2995
+ # serious_python skips copying Flutter packages to the temp dir
2996
+ # under --skip-site-packages, so register_flutter_extensions must
2997
+ # keep (not wipe) the permanent flutter-packages copy from the
2998
+ # previous build.
2999
+ self.site_packages_skipped = True
2961
3000
 
2962
3001
  package_result = self.run(
2963
3002
  package_args,
@@ -187,6 +187,7 @@ class BaseFlutterCommand(BaseCommand):
187
187
  assert self.options
188
188
  self.required_flutter_version = version.Version(flet.version.flutter_version)
189
189
  if self.required_flutter_version == version.Version("0"):
190
+ self.skip_flutter_doctor = True
190
191
  self.cleanup(
191
192
  1,
192
193
  "Unable to determine the required Flutter SDK version. "
@@ -0,0 +1 @@
1
+ version = "1.1.0.dev3"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flet-cli
3
- Version: 1.1.0.dev2
3
+ Version: 1.1.0.dev3
4
4
  Summary: Flet CLI
5
5
  Author-email: "Appveyor Systems Inc." <hello@flet.dev>
6
6
  License-Expression: Apache-2.0
@@ -9,8 +9,8 @@ Project-URL: Repository, https://github.com/flet-dev/flet
9
9
  Project-URL: Documentation, https://flet.dev/docs
10
10
  Requires-Python: >=3.10
11
11
  Description-Content-Type: text/markdown
12
- Requires-Dist: flet==1.1.0.dev2
13
- Requires-Dist: flet-platform-assets==1.1.0.dev2
12
+ Requires-Dist: flet==1.1.0.dev3
13
+ Requires-Dist: flet-platform-assets==1.1.0.dev3
14
14
  Requires-Dist: watchdog>=4.0.0
15
15
  Requires-Dist: packaging>=25.0
16
16
  Requires-Dist: qrcode>=7.4.2
@@ -1,5 +1,5 @@
1
- flet==1.1.0.dev2
2
- flet-platform-assets==1.1.0.dev2
1
+ flet==1.1.0.dev3
2
+ flet-platform-assets==1.1.0.dev3
3
3
  watchdog>=4.0.0
4
4
  packaging>=25.0
5
5
  qrcode>=7.4.2
@@ -0,0 +1,332 @@
1
+ """Reuse and recovery of the packages a previous `flet build` left in build/."""
2
+
3
+ import contextlib
4
+ import shutil
5
+ from pathlib import Path
6
+ from types import SimpleNamespace
7
+
8
+ import pytest
9
+ import yaml
10
+
11
+ from flet_cli.cli import parse_command_line
12
+ from flet_cli.commands.build_base import BaseBuildCommand
13
+
14
+
15
+ @pytest.fixture
16
+ def command(tmp_path):
17
+ """A build command with its build directories under `tmp_path`."""
18
+ cmd = BaseBuildCommand.__new__(BaseBuildCommand)
19
+ cmd.build_dir = tmp_path / "build"
20
+ cmd.flutter_dir = cmd.build_dir / "flutter"
21
+ cmd.template_data = {"_options": {"target_arch": []}}
22
+ cmd.python_release = SimpleNamespace(android_abis=("arm64-v8a", "x86_64"))
23
+ return cmd
24
+
25
+
26
+ def write_package(directory):
27
+ """Add an installed `flet` package to a site-packages `directory`."""
28
+ package = directory / "flet" / "__init__.pyc"
29
+ package.parent.mkdir(parents=True, exist_ok=True)
30
+ package.write_bytes(b"package")
31
+
32
+
33
+ def write_pubspec(command, **extensions):
34
+ """Write the Flutter project's pubspec.yaml with these path dependencies."""
35
+ command.pubspec_path = str(command.flutter_dir / "pubspec.yaml")
36
+ command.flutter_dir.mkdir(parents=True, exist_ok=True)
37
+ dependencies = {"flutter": {"sdk": "flutter"}, "provider": "^6.0.0"}
38
+ for name, path in extensions.items():
39
+ dependencies[name] = {"path": str(path)}
40
+ Path(command.pubspec_path).write_text(
41
+ yaml.safe_dump({"dependencies": dependencies})
42
+ )
43
+
44
+
45
+ def pubspec_dependencies(command):
46
+ """Return the dependencies listed in the Flutter project's pubspec.yaml."""
47
+ return yaml.safe_load(Path(command.pubspec_path).read_text())["dependencies"]
48
+
49
+
50
+ def write_extension(directory, name="flet_map", version="1.0.0"):
51
+ """Put a Flutter package called `name` into `directory`."""
52
+ extension = directory / name
53
+ extension.mkdir(parents=True, exist_ok=True)
54
+ (extension / "pubspec.yaml").write_text(f"name: {name}\nversion: {version}\n")
55
+
56
+
57
+ class TestSitePackagesMissing:
58
+ """`_site_packages_missing()` decides when installed packages can be reused."""
59
+
60
+ @pytest.mark.parametrize("platform", ["Windows", "Linux", "Darwin", "Emscripten"])
61
+ def test_missing_empty_and_populated_package_directory(self, command, platform):
62
+ """Counts site-packages as present only once it holds package files."""
63
+ command.package_platform = platform
64
+ root = command.flutter_dir if platform == "Emscripten" else command.build_dir
65
+ packages = root / "site-packages"
66
+ if platform == "Emscripten":
67
+ packages = root / "build" / "site-packages"
68
+ # A native cache must not mask missing web packages.
69
+ write_package(command.build_dir / "site-packages")
70
+ assert command._site_packages_missing()
71
+
72
+ (packages / "flet").mkdir(parents=True)
73
+ assert command._site_packages_missing()
74
+
75
+ write_package(packages)
76
+ assert not command._site_packages_missing()
77
+
78
+ def test_darwin_pod_metadata_is_not_a_package(self, command, tmp_path):
79
+ """Ignores the hidden `.pod` link serious_python leaves on Darwin."""
80
+ command.package_platform = "Darwin"
81
+ packages = command.build_dir / "site-packages"
82
+ packages.mkdir(parents=True)
83
+ pod = tmp_path / "pod"
84
+ pod.mkdir()
85
+ (pod / "sync_site_packages.sh").write_text("#!/bin/sh\n")
86
+ try:
87
+ (packages / ".pod").symlink_to(pod, target_is_directory=True)
88
+ except OSError:
89
+ pytest.skip("Creating symlinks is unavailable")
90
+
91
+ assert command._site_packages_missing()
92
+
93
+ write_package(packages)
94
+ assert not command._site_packages_missing()
95
+
96
+ @pytest.mark.parametrize(
97
+ ("platform", "architectures"),
98
+ [
99
+ ("Android", ("arm64-v8a", "x86_64")),
100
+ (
101
+ "iOS",
102
+ ("iphoneos.arm64", "iphonesimulator.arm64", "iphonesimulator.x86_64"),
103
+ ),
104
+ ],
105
+ )
106
+ def test_every_default_mobile_architecture_needs_packages(
107
+ self, command, platform, architectures
108
+ ):
109
+ """Requires packages for every default mobile architecture."""
110
+ command.package_platform = platform
111
+ packages = command.build_dir / "site-packages"
112
+ # Unrelated root files and empty ABI directories are not a usable cache.
113
+ write_package(packages)
114
+ for arch in architectures:
115
+ (packages / arch).mkdir()
116
+ assert command._site_packages_missing()
117
+
118
+ for arch in architectures[:-1]:
119
+ write_package(packages / arch)
120
+ assert command._site_packages_missing()
121
+
122
+ write_package(packages / architectures[-1])
123
+ assert not command._site_packages_missing()
124
+
125
+ (packages / architectures[-1] / "flet" / "__init__.pyc").unlink()
126
+ assert command._site_packages_missing()
127
+
128
+ @pytest.mark.parametrize(
129
+ ("platform", "architecture"),
130
+ [("Android", "arm64-v8a"), ("iOS", "iphoneos.arm64")],
131
+ )
132
+ def test_only_selected_architectures_need_packages(
133
+ self, command, platform, architecture
134
+ ):
135
+ """Requires packages only for the architectures chosen with `--arch`."""
136
+ command.package_platform = platform
137
+ command.template_data["_options"]["target_arch"] = [architecture]
138
+ write_package(command.build_dir / "site-packages" / architecture)
139
+
140
+ assert not command._site_packages_missing()
141
+
142
+
143
+ class TestFlutterPackagesMissing:
144
+ """`_flutter_packages_missing()` finds extensions pubspec.yaml points to."""
145
+
146
+ @pytest.mark.parametrize("relative", [False, True])
147
+ def test_detects_deleted_extension(self, command, relative):
148
+ """Reports an extension in build/flutter-packages until it has a package."""
149
+ packages = command.build_dir / "flutter-packages"
150
+ write_pubspec(
151
+ command,
152
+ flet_map=Path("../flutter-packages/flet_map")
153
+ if relative
154
+ else packages / "flet_map",
155
+ )
156
+ assert command._flutter_packages_missing()
157
+
158
+ (packages / "flet_map").mkdir(parents=True)
159
+ assert command._flutter_packages_missing()
160
+
161
+ write_extension(packages)
162
+ assert not command._flutter_packages_missing()
163
+
164
+ def test_detects_one_deleted_extension_among_several(self, command):
165
+ """Reports a deleted extension when other extensions are still there."""
166
+ packages = command.build_dir / "flutter-packages"
167
+ write_pubspec(
168
+ command,
169
+ flet_map=packages / "flet_map",
170
+ flet_geolocator=packages / "flet_geolocator",
171
+ )
172
+ write_extension(packages, "flet_map")
173
+ assert command._flutter_packages_missing()
174
+
175
+ write_extension(packages, "flet_geolocator")
176
+ assert not command._flutter_packages_missing()
177
+
178
+ def test_ignores_other_dependencies(self, command, tmp_path):
179
+ """Ignores a missing pubspec.yaml and path dependencies elsewhere."""
180
+ command.pubspec_path = str(command.flutter_dir / "pubspec.yaml")
181
+ assert not command._flutter_packages_missing()
182
+
183
+ write_pubspec(command, flet_map=tmp_path / "elsewhere" / "flet_map")
184
+ assert not command._flutter_packages_missing()
185
+
186
+
187
+ class TestMissingExtensionRecovery:
188
+ """`create_flutter_project()` starting over when an extension is gone."""
189
+
190
+ @pytest.mark.parametrize("extension_deleted", [False, True])
191
+ def test_restarts_the_build(
192
+ self, command, monkeypatch, tmp_path, extension_deleted
193
+ ):
194
+ """Starts over with a clean project only when an extension is gone."""
195
+ import cookiecutter.main
196
+
197
+ command.options = SimpleNamespace(
198
+ template=str(tmp_path / "template"), template_ref=None, template_dir=None
199
+ )
200
+ command.template_data = {"project_name": "test_app"}
201
+ command.get_pyproject = lambda *_: None
202
+ command.config_platform = "web"
203
+ command.emojis = {"checkmark": ""}
204
+ command.verbose = 0
205
+ command.step = lambda *_: contextlib.nullcontext()
206
+ command.pubspec_path = str(command.flutter_dir / "pubspec.yaml")
207
+ # The template renders a pubspec.yaml without extensions.
208
+ monkeypatch.setattr(
209
+ cookiecutter.main, "cookiecutter", lambda **_: write_pubspec(command)
210
+ )
211
+ assert command.create_flutter_project()
212
+
213
+ # A previous build added its extension after packaging.
214
+ packages = command.build_dir / "flutter-packages"
215
+ write_extension(packages)
216
+ write_pubspec(command, flet_map=packages / "flet_map")
217
+ stamps = command.build_dir / ".hash"
218
+ derived = ["template-2", "package", "icons", "splashes"]
219
+ for stamp in derived:
220
+ (stamps / stamp).write_text("digest")
221
+ if extension_deleted:
222
+ shutil.rmtree(packages)
223
+
224
+ assert command.create_flutter_project() is extension_deleted
225
+ assert [(stamps / s).exists() for s in derived] == [not extension_deleted] * 4
226
+ assert ("flet_map" in pubspec_dependencies(command)) is not extension_deleted
227
+
228
+
229
+ class TestPackagePythonApp:
230
+ """`package_python_app()` keeping the previous Flutter packages."""
231
+
232
+ @pytest.fixture
233
+ def packaging(self, command):
234
+ """Package a web app that has an extension; returns what the command saw."""
235
+ command.options = parse_command_line(["build", "web"])
236
+ command.package_platform = "Emscripten"
237
+ command.target_platform = "web"
238
+ command.config_platform = "web"
239
+ command.python_release = SimpleNamespace(short="3.14", android_abis=())
240
+ command.dart_exe = "dart"
241
+ command.verbose = 0
242
+ command.python_app_path = command.package_app_path = (
243
+ command.build_dir.parent / "app"
244
+ )
245
+ command.python_app_path.mkdir(parents=True)
246
+ command.flutter_dir.mkdir(parents=True)
247
+ command.flutter_packages_dir = command.build_dir / "flutter-packages"
248
+ command.flutter_packages_temp_dir = command.build_dir / "flutter-packages-temp"
249
+ command.get_pyproject = lambda *_: None
250
+ command.step = lambda *_: contextlib.nullcontext()
251
+ write_extension(command.flutter_packages_dir)
252
+
253
+ seen = {}
254
+
255
+ class Stop(Exception):
256
+ """Ends the package step once the package command is reached."""
257
+
258
+ def run(args, cwd, env=None, capture_output=True):
259
+ """Record the state the package command would run in, then stop."""
260
+ seen["flutter_packages"] = (
261
+ command.flutter_packages_dir / "flet_map" / "pubspec.yaml"
262
+ ).exists()
263
+ seen["skip"] = "--skip-site-packages" in args
264
+ raise Stop
265
+
266
+ command.run = run
267
+
268
+ def package():
269
+ """Run the package step up to the package command."""
270
+ with pytest.raises(Stop):
271
+ command.package_python_app()
272
+ return seen
273
+
274
+ return package
275
+
276
+ @pytest.mark.parametrize("previous_build", [False, True])
277
+ def test_reinstall_keeps_flutter_packages(self, command, packaging, previous_build):
278
+ """Keeps build/flutter-packages when the package command starts."""
279
+ if previous_build:
280
+ # Packages installed by a build whose package arguments differed.
281
+ write_package(command.flutter_dir / "build" / "site-packages")
282
+ stamp = command.build_dir / ".hash" / "package"
283
+ stamp.parent.mkdir(parents=True)
284
+ stamp.write_text("previous package arguments")
285
+
286
+ assert packaging() == {"flutter_packages": True, "skip": False}
287
+
288
+
289
+ class TestRegisterFlutterExtensions:
290
+ """`register_flutter_extensions()` replacing Flutter packages after packaging."""
291
+
292
+ @pytest.fixture
293
+ def register(self, command):
294
+ """Register extensions over stale ones; returns the registered versions."""
295
+ command.flutter_packages_dir = command.build_dir / "flutter-packages"
296
+ command.flutter_packages_temp_dir = command.build_dir / "flutter-packages-temp"
297
+ command.flutter_dependencies = {}
298
+ command.template_data = {"flutter": {"dependencies": []}}
299
+ command.verbose = 0
300
+ command.emojis = {"checkmark": ""}
301
+ command.step = lambda *_: contextlib.nullcontext()
302
+ command.apply_extension_settings = lambda: None
303
+ write_extension(command.flutter_packages_dir, "flet_map", "1.0.0")
304
+ write_extension(command.flutter_packages_dir, "flet_old")
305
+
306
+ def register(skipped):
307
+ """Register extensions after packaging, reused or not."""
308
+ command.site_packages_skipped = skipped
309
+ command.register_flutter_extensions()
310
+ return {
311
+ name: yaml.safe_load(
312
+ (Path(dependency["path"]) / "pubspec.yaml").read_text()
313
+ )["version"]
314
+ for name, dependency in command.flutter_dependencies.items()
315
+ }
316
+
317
+ return register
318
+
319
+ def test_reinstall_replaces_stale_extensions(self, command, register):
320
+ """Replaces build/flutter-packages with the packages just installed."""
321
+ write_extension(command.flutter_packages_temp_dir, "flet_map", "2.0.0")
322
+ assert register(skipped=False) == {"flet_map": "2.0.0"}
323
+ assert not command.flutter_packages_temp_dir.exists()
324
+
325
+ def test_reinstall_without_extensions_removes_them(self, command, register):
326
+ """Removes the previous extensions when the app no longer has any."""
327
+ assert register(skipped=False) == {}
328
+ assert not command.flutter_packages_dir.exists()
329
+
330
+ def test_skipped_install_keeps_extensions(self, register):
331
+ """Keeps and registers the previous extensions when packages are reused."""
332
+ assert register(skipped=True) == {"flet_map": "1.0.0", "flet_old": "1.0.0"}
@@ -1 +0,0 @@
1
- version = "1.1.0.dev2"
@@ -1,103 +0,0 @@
1
- """Dependency cache recovery when build artifacts have been removed."""
2
-
3
- from types import SimpleNamespace
4
-
5
- import pytest
6
-
7
- from flet_cli.commands.build_base import BaseBuildCommand
8
-
9
-
10
- @pytest.fixture
11
- def command(tmp_path):
12
- cmd = BaseBuildCommand.__new__(BaseBuildCommand)
13
- cmd.build_dir = tmp_path / "build"
14
- cmd.flutter_dir = cmd.build_dir / "flutter"
15
- cmd.template_data = {"_options": {"target_arch": []}}
16
- cmd.python_release = SimpleNamespace(android_abis=("arm64-v8a", "x86_64"))
17
- return cmd
18
-
19
-
20
- def write_package(directory):
21
- package = directory / "flet" / "__init__.pyc"
22
- package.parent.mkdir(parents=True, exist_ok=True)
23
- package.write_bytes(b"package")
24
-
25
-
26
- @pytest.mark.parametrize("platform", ["Windows", "Linux", "Darwin", "Emscripten"])
27
- def test_missing_empty_and_populated_package_directory(command, platform):
28
- command.package_platform = platform
29
- root = command.flutter_dir if platform == "Emscripten" else command.build_dir
30
- packages = root / "site-packages"
31
- if platform == "Emscripten":
32
- packages = root / "build" / "site-packages"
33
- # A native cache must not mask missing web packages.
34
- write_package(command.build_dir / "site-packages")
35
- assert command._site_packages_missing()
36
-
37
- (packages / "flet").mkdir(parents=True)
38
- assert command._site_packages_missing()
39
-
40
- write_package(packages)
41
- assert not command._site_packages_missing()
42
-
43
-
44
- def test_darwin_pod_metadata_is_not_a_package(command, tmp_path):
45
- command.package_platform = "Darwin"
46
- packages = command.build_dir / "site-packages"
47
- packages.mkdir(parents=True)
48
- pod = tmp_path / "pod"
49
- pod.mkdir()
50
- (pod / "sync_site_packages.sh").write_text("#!/bin/sh\n")
51
- try:
52
- (packages / ".pod").symlink_to(pod, target_is_directory=True)
53
- except OSError:
54
- pytest.skip("Creating symlinks is unavailable")
55
-
56
- assert command._site_packages_missing()
57
-
58
- write_package(packages)
59
- assert not command._site_packages_missing()
60
-
61
-
62
- @pytest.mark.parametrize(
63
- ("platform", "architectures"),
64
- [
65
- ("Android", ("arm64-v8a", "x86_64")),
66
- (
67
- "iOS",
68
- ("iphoneos.arm64", "iphonesimulator.arm64", "iphonesimulator.x86_64"),
69
- ),
70
- ],
71
- )
72
- def test_every_default_mobile_architecture_needs_packages(
73
- command, platform, architectures
74
- ):
75
- command.package_platform = platform
76
- packages = command.build_dir / "site-packages"
77
- # Unrelated root files and empty ABI directories are not a usable cache.
78
- write_package(packages)
79
- for arch in architectures:
80
- (packages / arch).mkdir()
81
- assert command._site_packages_missing()
82
-
83
- for arch in architectures[:-1]:
84
- write_package(packages / arch)
85
- assert command._site_packages_missing()
86
-
87
- write_package(packages / architectures[-1])
88
- assert not command._site_packages_missing()
89
-
90
- (packages / architectures[-1] / "flet" / "__init__.pyc").unlink()
91
- assert command._site_packages_missing()
92
-
93
-
94
- @pytest.mark.parametrize(
95
- ("platform", "architecture"),
96
- [("Android", "arm64-v8a"), ("iOS", "iphoneos.arm64")],
97
- )
98
- def test_only_selected_architectures_need_packages(command, platform, architecture):
99
- command.package_platform = platform
100
- command.template_data["_options"]["target_arch"] = [architecture]
101
- write_package(command.build_dir / "site-packages" / architecture)
102
-
103
- assert not command._site_packages_missing()
File without changes
File without changes