flet-cli 0.86.2__tar.gz → 0.86.3__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 (60) hide show
  1. {flet_cli-0.86.2 → flet_cli-0.86.3}/PKG-INFO +2 -2
  2. {flet_cli-0.86.2 → flet_cli-0.86.3}/pyproject.toml +2 -2
  3. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/cli.py +86 -24
  4. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/commands/build_base.py +53 -17
  5. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/commands/run.py +12 -1
  6. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/utils/python_versions.py +1 -1
  7. flet_cli-0.86.3/src/flet_cli/version.py +1 -0
  8. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli.egg-info/PKG-INFO +2 -2
  9. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli.egg-info/SOURCES.txt +3 -1
  10. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli.egg-info/requires.txt +1 -1
  11. flet_cli-0.86.3/tests/test_find_platform_image.py +67 -0
  12. flet_cli-0.86.3/tests/test_run_script_args.py +103 -0
  13. flet_cli-0.86.2/src/flet_cli/version.py +0 -1
  14. {flet_cli-0.86.2 → flet_cli-0.86.3}/README.md +0 -0
  15. {flet_cli-0.86.2 → flet_cli-0.86.3}/setup.cfg +0 -0
  16. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/__pyinstaller/__init__.py +0 -0
  17. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/__pyinstaller/config.py +0 -0
  18. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/__pyinstaller/hook-flet.py +0 -0
  19. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/__pyinstaller/macos_utils.py +0 -0
  20. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/__pyinstaller/rthooks/pyi_rth_localhost_fletd.py +0 -0
  21. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/__pyinstaller/utils.py +0 -0
  22. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/__pyinstaller/win_utils.py +0 -0
  23. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/commands/base.py +0 -0
  24. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/commands/build.py +0 -0
  25. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/commands/clean.py +0 -0
  26. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/commands/create.py +0 -0
  27. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/commands/debug.py +0 -0
  28. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/commands/devices.py +0 -0
  29. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/commands/doctor.py +0 -0
  30. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/commands/emulators.py +0 -0
  31. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/commands/flutter_base.py +0 -0
  32. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/commands/mcp.py +0 -0
  33. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/commands/options.py +0 -0
  34. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/commands/pack.py +0 -0
  35. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/commands/publish.py +0 -0
  36. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/commands/serve.py +0 -0
  37. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/commands/test.py +0 -0
  38. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/commands/test_host.py +0 -0
  39. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/utils/android.py +0 -0
  40. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/utils/android_sdk.py +0 -0
  41. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/utils/cli.py +0 -0
  42. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/utils/distros.py +0 -0
  43. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/utils/flutter.py +0 -0
  44. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/utils/hash_stamp.py +0 -0
  45. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/utils/jdk.py +0 -0
  46. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/utils/linux_deps.py +0 -0
  47. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/utils/merge.py +0 -0
  48. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/utils/plist.py +0 -0
  49. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/utils/processes.py +0 -0
  50. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/utils/project_dependencies.py +0 -0
  51. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/utils/pyodide.py +0 -0
  52. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/utils/pyproject_toml.py +0 -0
  53. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli/utils/template_cache.py +0 -0
  54. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli.egg-info/dependency_links.txt +0 -0
  55. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli.egg-info/entry_points.txt +0 -0
  56. {flet_cli-0.86.2 → flet_cli-0.86.3}/src/flet_cli.egg-info/top_level.txt +0 -0
  57. {flet_cli-0.86.2 → flet_cli-0.86.3}/tests/test_clean.py +0 -0
  58. {flet_cli-0.86.2 → flet_cli-0.86.3}/tests/test_plist.py +0 -0
  59. {flet_cli-0.86.2 → flet_cli-0.86.3}/tests/test_project_dependencies.py +0 -0
  60. {flet_cli-0.86.2 → flet_cli-0.86.3}/tests/test_python_versions.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flet-cli
3
- Version: 0.86.2
3
+ Version: 0.86.3
4
4
  Summary: Flet CLI
5
5
  Author-email: "Appveyor Systems Inc." <hello@flet.dev>
6
6
  License-Expression: Apache-2.0
@@ -9,7 +9,7 @@ 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==0.86.2
12
+ Requires-Dist: flet==0.86.3
13
13
  Requires-Dist: watchdog>=4.0.0
14
14
  Requires-Dist: packaging>=25.0
15
15
  Requires-Dist: qrcode>=7.4.2
@@ -1,13 +1,13 @@
1
1
  [project]
2
2
  name = "flet-cli"
3
- version = "0.86.2"
3
+ version = "0.86.3"
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==0.86.2",
10
+ "flet==0.86.3",
11
11
  "watchdog >=4.0.0",
12
12
  "packaging >=25.0",
13
13
  "qrcode >=7.4.2",
@@ -1,6 +1,7 @@
1
1
  import argparse
2
2
  import json
3
3
  import sys
4
+ from typing import Optional
4
5
 
5
6
  import flet.version
6
7
  import flet_cli.commands.build
@@ -41,23 +42,25 @@ def _render_version(as_json: bool) -> str:
41
42
 
42
43
  # Source https://stackoverflow.com/a/26379693
43
44
  def set_default_subparser(
44
- parser: argparse.ArgumentParser, name: str, args: list = None, index: int = 0
45
- ):
45
+ parser: argparse.ArgumentParser, name: str, args: list
46
+ ) -> list:
46
47
  """
47
- Set a default subparser when no subparser is provided.
48
+ Prepend a default subparser name when the command line doesn't name one.
48
49
  This should be called after setting up the argument parser but before
49
50
  `parse_args()`.
50
51
 
51
52
  Args:
53
+ parser: The root argument parser.
52
54
  name: The name of the default subparser to use.
53
- args: A list of arguments passed to `parse_args()`.
54
- index: Position in `sys.argv` where the default subparser should be
55
- inserted.
55
+ args: The command-line arguments, without the program name.
56
+
57
+ Returns:
58
+ `args`, with the default subparser name prepended when applicable.
56
59
  """
57
60
 
58
- # exit if help or version flags are present
59
- if any(flag in sys.argv[1:] for flag in {"-h", "--help", "-V", "--version"}):
60
- return
61
+ # exit if no arguments, or help or version flags are present
62
+ if not args or any(flag in args for flag in {"-h", "--help", "-V", "--version"}):
63
+ return args
61
64
 
62
65
  # all subparser actions
63
66
  subparser_actions = [
@@ -72,19 +75,14 @@ def set_default_subparser(
72
75
  ]
73
76
 
74
77
  # if an existing subparser is provided, skip setting a default
75
- if any(arg in subparser_names for arg in sys.argv[1:]):
76
- return
78
+ if any(arg in subparser_names for arg in args):
79
+ return args
77
80
 
78
81
  # if the default subparser doesn't exist, register it in the first subparser action
79
82
  if (name not in subparser_names) and subparser_actions:
80
83
  subparser_actions[0].add_parser(name)
81
84
 
82
- # insert the default subparser into the appropriate argument list
83
- if args is None:
84
- if len(sys.argv) > 1:
85
- sys.argv.insert(index, name)
86
- else:
87
- args.insert(index, name)
85
+ return [name, *args]
88
86
 
89
87
 
90
88
  def get_parser() -> argparse.ArgumentParser:
@@ -132,22 +130,86 @@ def get_parser() -> argparse.ArgumentParser:
132
130
  except ImportError:
133
131
  pass
134
132
 
135
- # set "run" as the default subparser
136
- set_default_subparser(parser, name="run", index=1)
137
-
138
133
  return parser
139
134
 
140
135
 
141
- def main():
136
+ def split_script_args(argv: list) -> tuple[list, Optional[list]]:
137
+ """
138
+ Split a command line at the first bare `--` separator.
139
+
140
+ Everything after the separator is meant for the app script, not for Flet
141
+ itself, so it must be removed before the parser (and `set_default_subparser`)
142
+ sees it - otherwise `flet run app.py -- --web` would be read as Flet's own
143
+ `--web` option, and `flet app.py -- build` would suppress the default `run`
144
+ subcommand.
145
+
146
+ Args:
147
+ argv: Command-line arguments, without the program name.
148
+
149
+ Returns:
150
+ A `(argv, script_args)` tuple, where `script_args` is `None` when no
151
+ `--` separator is present.
152
+ """
153
+ try:
154
+ index = argv.index("--")
155
+ except ValueError:
156
+ return argv, None
157
+ return argv[:index], argv[index + 1 :]
158
+
159
+
160
+ def parse_command_line(argv: list) -> argparse.Namespace:
161
+ """
162
+ Parse a Flet command line into the namespace passed to a command handler.
163
+
164
+ Exits the process with a usage message when the command line is invalid.
165
+
166
+ Args:
167
+ argv: Command-line arguments, without the program name.
168
+
169
+ Returns:
170
+ The parsed arguments. For `flet run`, `script_args` holds the arguments
171
+ to forward to the app script.
172
+ """
173
+
174
+ # pull off the arguments meant for the app script before the parser sees them
175
+ argv, script_args = split_script_args(argv)
176
+
142
177
  parser = get_parser()
143
178
 
179
+ # "run" is the default subcommand
180
+ argv = set_default_subparser(parser, name="run", args=argv)
181
+
182
+ args, unrecognized = parser.parse_known_args(argv)
183
+
184
+ if unrecognized:
185
+ message = f"unrecognized arguments: {' '.join(unrecognized)}"
186
+ if getattr(args, "command", None) == "run":
187
+ message += (
188
+ "\nIf these are arguments for your app script, put them after "
189
+ "a `--` separator, e.g. "
190
+ f"`flet run {args.script} -- {' '.join(unrecognized)}`"
191
+ )
192
+ parser.error(message)
193
+
194
+ # forward the arguments that followed `--` to the app script
195
+ if script_args is not None:
196
+ if getattr(args, "command", None) != "run":
197
+ parser.error(
198
+ "the `--` separator is only supported by `flet run`, to pass "
199
+ "the arguments that follow it to your app script"
200
+ )
201
+ args.script_args = list(args.script_args) + script_args
202
+
203
+ return args
204
+
205
+
206
+ def main():
144
207
  # print usage/help if called without arguments
145
208
  if len(sys.argv) == 1:
146
- parser.print_help(sys.stdout)
209
+ get_parser().print_help(sys.stdout)
147
210
  sys.exit(1)
148
211
 
149
- # parse arguments
150
- args = parser.parse_args()
212
+ args = parse_command_line(sys.argv[1:])
151
213
 
152
214
  # handle `flet --version [--json]` (no subcommand/handler is set)
153
215
  if getattr(args, "version", False):
@@ -2854,11 +2854,14 @@ class BaseBuildCommand(BaseFlutterCommand):
2854
2854
  """
2855
2855
  Find the best matching image file for the current target platform.
2856
2856
 
2857
- When multiple files share the same base name (e.g. `icon.icns`,
2858
- `icon.ico`, `icon.png`), the method filters out formats that are
2859
- incompatible with the build target before selecting the first match.
2860
- For example, `.icns` is skipped on Windows builds because
2861
- `flutter_launcher_icons` cannot decode it.
2857
+ When multiple files share the same base name (e.g. `icon.png` and
2858
+ `icon.svg`), incompatible formats are filtered out and the rest are
2859
+ ranked so a raster image (`.png` first) always wins, making the
2860
+ choice deterministic regardless of filesystem ordering. Formats the
2861
+ icon/splash generators cannot decode are dropped: `.svg` (vector,
2862
+ never supported), `.icns` (macOS-only) and `.ico` (Windows-only). If
2863
+ the only candidate is a vector image, a warning is logged and `None`
2864
+ is returned so the default icon is used.
2862
2865
 
2863
2866
  Args:
2864
2867
  src_path: Source assets directory.
@@ -2871,21 +2874,54 @@ class BaseBuildCommand(BaseFlutterCommand):
2871
2874
  File name of matched image, or `None` if not found.
2872
2875
  """
2873
2876
 
2874
- # .icns is macOS-only and .ico is Windows-only; filter out
2875
- # incompatible formats so flutter_launcher_icons gets a decodable file.
2876
- images = list(
2877
- filter(
2878
- lambda p: not (
2879
- (ext := Path(p).suffix.lower()) == ".icns"
2880
- and self.target_platform != "macos"
2881
- or ext == ".ico"
2882
- and self.target_platform != "windows"
2883
- ),
2884
- glob.glob(str(src_path.joinpath(f"{image_name}.*"))),
2885
- )
2877
+ # flutter_launcher_icons / flutter_native_splash decode raster images
2878
+ # only, so drop any candidate they can't read: .svg is vector (never
2879
+ # supported), .icns is macOS-only and .ico is Windows-only.
2880
+ def _incompatible(p: str) -> bool:
2881
+ ext = Path(p).suffix.lower()
2882
+ return (
2883
+ ext == ".svg"
2884
+ or (ext == ".icns" and self.target_platform != "macos")
2885
+ or (ext == ".ico" and self.target_platform != "windows")
2886
+ )
2887
+
2888
+ # glob order is filesystem-dependent — sort so the choice is
2889
+ # deterministic across machines, then rank by format so a raster
2890
+ # icon (.png first) always wins when several candidates share a base
2891
+ # name (e.g. both icon.png and icon.svg present).
2892
+ ext_priority = {
2893
+ ".png": 0,
2894
+ ".webp": 1,
2895
+ ".jpg": 2,
2896
+ ".jpeg": 2,
2897
+ ".gif": 3,
2898
+ ".bmp": 4,
2899
+ ".tif": 5,
2900
+ ".tiff": 5,
2901
+ ".ico": 6,
2902
+ ".icns": 6,
2903
+ }
2904
+ candidates = sorted(glob.glob(str(src_path.joinpath(f"{image_name}.*"))))
2905
+ images = sorted(
2906
+ (p for p in candidates if not _incompatible(p)),
2907
+ key=lambda p: (ext_priority.get(Path(p).suffix.lower(), 99), p),
2886
2908
  )
2887
2909
 
2888
2910
  if not images:
2911
+ # Nothing usable. If the only candidate was a vector image (e.g.
2912
+ # an icon.svg with no raster sibling), say why it's ignored
2913
+ # instead of silently falling back to the default icon.
2914
+ svg = next(
2915
+ (p for p in candidates if Path(p).suffix.lower() == ".svg"), None
2916
+ )
2917
+ if svg:
2918
+ console.log(
2919
+ f'Warning: "{Path(svg).name}" is a vector (SVG) image and '
2920
+ f'cannot be used for "{image_name}". Provide a raster '
2921
+ f'"{image_name}.png" to customize it — using the default '
2922
+ f"for now.",
2923
+ style=warning_style,
2924
+ )
2889
2925
  return None
2890
2926
 
2891
2927
  best = images[0]
@@ -47,6 +47,16 @@ class Command(BaseCommand):
47
47
  default=".",
48
48
  help="Path to the Python script that starts your Flet app",
49
49
  )
50
+ parser.add_argument(
51
+ "script_args",
52
+ type=str,
53
+ nargs="*",
54
+ default=[],
55
+ metavar="args",
56
+ help="Arguments to pass to your app script, available there as "
57
+ "`sys.argv[1:]`. Separate them from Flet's own options with `--`, "
58
+ "e.g. `flet run --web app.py -- --verbose`",
59
+ )
50
60
  parser.add_argument(
51
61
  "-p",
52
62
  "--port",
@@ -287,7 +297,8 @@ class Command(BaseCommand):
287
297
  my_event_handler = Handler(
288
298
  args=[sys.executable, "-u"]
289
299
  + ["-m"] * options.module
290
- + [options.script if options.module else script_path],
300
+ + [options.script if options.module else script_path]
301
+ + list(options.script_args),
291
302
  watch_directory=options.directory or options.recursive,
292
303
  script_path=str(script_path),
293
304
  port=port,
@@ -28,7 +28,7 @@ from flet_cli.utils.template_cache import get_cache_root
28
28
  # python-build release this flet pins. Keep in sync with serious_python's
29
29
  # `pythonReleaseDate` (lib/src/python_versions.dart) — both should track the
30
30
  # same python-build release.
31
- PYTHON_BUILD_RELEASE_DATE = "20260720"
31
+ PYTHON_BUILD_RELEASE_DATE = "20260726"
32
32
 
33
33
  RELEASE_DATE_ENV = "FLET_PYTHON_BUILD_RELEASE_DATE"
34
34
  MANIFEST_PATH_ENV = "FLET_PYTHON_BUILD_MANIFEST"
@@ -0,0 +1 @@
1
+ version = "0.86.3"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flet-cli
3
- Version: 0.86.2
3
+ Version: 0.86.3
4
4
  Summary: Flet CLI
5
5
  Author-email: "Appveyor Systems Inc." <hello@flet.dev>
6
6
  License-Expression: Apache-2.0
@@ -9,7 +9,7 @@ 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==0.86.2
12
+ Requires-Dist: flet==0.86.3
13
13
  Requires-Dist: watchdog>=4.0.0
14
14
  Requires-Dist: packaging>=25.0
15
15
  Requires-Dist: qrcode>=7.4.2
@@ -50,6 +50,8 @@ src/flet_cli/utils/pyproject_toml.py
50
50
  src/flet_cli/utils/python_versions.py
51
51
  src/flet_cli/utils/template_cache.py
52
52
  tests/test_clean.py
53
+ tests/test_find_platform_image.py
53
54
  tests/test_plist.py
54
55
  tests/test_project_dependencies.py
55
- tests/test_python_versions.py
56
+ tests/test_python_versions.py
57
+ tests/test_run_script_args.py
@@ -1,4 +1,4 @@
1
- flet==0.86.2
1
+ flet==0.86.3
2
2
  watchdog>=4.0.0
3
3
  packaging>=25.0
4
4
  qrcode>=7.4.2
@@ -0,0 +1,67 @@
1
+ """Icon/splash image selection in `flet build`.
2
+
3
+ `find_platform_image` must pick a *decodable* image deterministically when
4
+ several files share a base name — otherwise the choice depends on filesystem
5
+ glob order and an `icon.svg` can slip through to `flutter_launcher_icons`,
6
+ which crashes with `NoDecoderForImageFormatException`.
7
+ """
8
+
9
+ from types import SimpleNamespace
10
+
11
+ from flet_cli.commands.build_base import BaseBuildCommand
12
+
13
+
14
+ class _FakeHash:
15
+ def update(self, *_):
16
+ pass
17
+
18
+
19
+ def _find(tmp_path, filenames, *, image_name="icon", target_platform="web"):
20
+ assets = tmp_path / "assets"
21
+ assets.mkdir(exist_ok=True)
22
+ for name in filenames:
23
+ (assets / name).write_bytes(b"x")
24
+ dest = tmp_path / "images"
25
+ dest.mkdir(exist_ok=True)
26
+ fake_self = SimpleNamespace(target_platform=target_platform, verbose=0)
27
+ copy_ops: list = []
28
+ result = BaseBuildCommand.find_platform_image(
29
+ fake_self, assets, dest, image_name, copy_ops, _FakeHash()
30
+ )
31
+ return result, copy_ops
32
+
33
+
34
+ def test_prefers_raster_over_svg(tmp_path):
35
+ # Both present: the raster png must win regardless of glob order.
36
+ result, copy_ops = _find(tmp_path, ["icon.svg", "icon.png"])
37
+ assert result == "icon.png"
38
+ assert copy_ops and str(copy_ops[0][0]).endswith("icon.png")
39
+
40
+
41
+ def test_png_preferred_over_other_raster(tmp_path):
42
+ # Ranking, not alphabetical order (jpg < png alphabetically).
43
+ result, _ = _find(tmp_path, ["icon.jpg", "icon.png"])
44
+ assert result == "icon.png"
45
+
46
+
47
+ def test_svg_only_returns_none_with_warning(tmp_path, capsys):
48
+ result, copy_ops = _find(tmp_path, ["icon.svg"])
49
+ assert result is None
50
+ assert copy_ops == []
51
+ combined = capsys.readouterr()
52
+ assert "icon.svg" in (combined.out + combined.err)
53
+
54
+
55
+ def test_no_candidates_is_silent_none(tmp_path, capsys):
56
+ result, copy_ops = _find(tmp_path, [])
57
+ assert result is None
58
+ assert copy_ops == []
59
+ combined = capsys.readouterr()
60
+ assert "Warning" not in (combined.out + combined.err)
61
+
62
+
63
+ def test_ico_is_windows_only(tmp_path):
64
+ # ico is incompatible on web (no raster sibling) -> default icon.
65
+ assert _find(tmp_path, ["icon.ico"], target_platform="web")[0] is None
66
+ # ...but valid on a windows build.
67
+ assert _find(tmp_path, ["icon.ico"], target_platform="windows")[0] == "icon.ico"
@@ -0,0 +1,103 @@
1
+ import pytest
2
+
3
+ from flet_cli.cli import parse_command_line
4
+
5
+
6
+ def _parse(*argv):
7
+ """Parse a Flet command line given as separate arguments."""
8
+ return parse_command_line(list(argv))
9
+
10
+
11
+ def test_no_script_args():
12
+ """A plain `flet run app.py` passes nothing to the script."""
13
+ args = _parse("run", "app.py")
14
+
15
+ assert args.script == "app.py"
16
+ assert args.script_args == []
17
+
18
+
19
+ def test_script_args_after_separator():
20
+ """Arguments after `--` are forwarded to the script, options and all."""
21
+ args = _parse("run", "app.py", "--", "--manual", "-x", "1")
22
+
23
+ assert args.script == "app.py"
24
+ assert args.script_args == ["--manual", "-x", "1"]
25
+
26
+
27
+ def test_flet_options_are_not_forwarded():
28
+ """Flet's own options before `--` stay with Flet."""
29
+ args = _parse("run", "-w", "app.py", "--", "--web")
30
+
31
+ assert args.web is True
32
+ assert args.script == "app.py"
33
+ assert args.script_args == ["--web"]
34
+
35
+
36
+ def test_flet_options_between_script_and_separator():
37
+ """Flet's options may follow the script path and precede `--`."""
38
+ args = _parse("run", "app.py", "--web", "--port", "8080", "--", "--manual")
39
+
40
+ assert args.web is True
41
+ assert args.port == 8080
42
+ assert args.script_args == ["--manual"]
43
+
44
+
45
+ def test_positional_script_args_need_no_separator():
46
+ """Arguments that don't look like options are forwarded without `--`."""
47
+ args = _parse("run", "app.py", "foo", "bar")
48
+
49
+ assert args.script == "app.py"
50
+ assert args.script_args == ["foo", "bar"]
51
+
52
+
53
+ def test_positional_and_separated_script_args_are_combined():
54
+ """Arguments before and after `--` are forwarded in command-line order."""
55
+ args = _parse("run", "app.py", "foo", "--", "--manual")
56
+
57
+ assert args.script_args == ["foo", "--manual"]
58
+
59
+
60
+ def test_script_args_with_module():
61
+ """`-m` module invocations forward script arguments too."""
62
+ args = _parse("run", "-m", "my_app.main", "--", "--manual")
63
+
64
+ assert args.module is True
65
+ assert args.script == "my_app.main"
66
+ assert args.script_args == ["--manual"]
67
+
68
+
69
+ def test_script_args_with_implicit_run_command():
70
+ """The default `run` subcommand is applied even with a `--` separator."""
71
+ args = _parse("app.py", "--", "--manual")
72
+
73
+ assert args.command == "run"
74
+ assert args.script == "app.py"
75
+ assert args.script_args == ["--manual"]
76
+
77
+
78
+ def test_script_args_named_like_a_subcommand():
79
+ """A script argument matching a subcommand name doesn't select it."""
80
+ args = _parse("app.py", "--", "build", "test")
81
+
82
+ assert args.command == "run"
83
+ assert args.script == "app.py"
84
+ assert args.script_args == ["build", "test"]
85
+
86
+
87
+ def test_unknown_flet_option_is_rejected(capsys):
88
+ """A mistyped Flet option still errors instead of reaching the script."""
89
+ with pytest.raises(SystemExit):
90
+ _parse("run", "app.py", "--wev")
91
+
92
+ err = capsys.readouterr().err
93
+ assert "unrecognized arguments: --wev" in err
94
+ # ...and the error suggests the `--` separator
95
+ assert "flet run app.py -- --wev" in err
96
+
97
+
98
+ def test_separator_rejected_for_other_commands(capsys):
99
+ """Only `flet run` accepts a `--` separator."""
100
+ with pytest.raises(SystemExit):
101
+ _parse("build", "apk", "--", "--manual")
102
+
103
+ assert "only supported by `flet run`" in capsys.readouterr().err
@@ -1 +0,0 @@
1
- version = "0.86.2"
File without changes
File without changes
File without changes
File without changes