moops 0.13.2__tar.gz → 0.13.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 (32) hide show
  1. {moops-0.13.2 → moops-0.13.3}/PKG-INFO +1 -1
  2. {moops-0.13.2 → moops-0.13.3}/pyproject.toml +1 -1
  3. {moops-0.13.2 → moops-0.13.3}/src/moops/_options.py +28 -2
  4. {moops-0.13.2 → moops-0.13.3}/src/moops/_text_wrap.py +4 -2
  5. {moops-0.13.2 → moops-0.13.3}/src/moops/interface.py +35 -8
  6. {moops-0.13.2 → moops-0.13.3}/README.md +0 -0
  7. {moops-0.13.2 → moops-0.13.3}/src/moops/__init__.py +0 -0
  8. {moops-0.13.2 → moops-0.13.3}/src/moops/_choice_options.py +0 -0
  9. {moops-0.13.2 → moops-0.13.3}/src/moops/_control_mirroring.py +0 -0
  10. {moops-0.13.2 → moops-0.13.3}/src/moops/_custom_element.py +0 -0
  11. {moops-0.13.2 → moops-0.13.3}/src/moops/_embed.py +0 -0
  12. {moops-0.13.2 → moops-0.13.3}/src/moops/_input_map.py +0 -0
  13. {moops-0.13.2 → moops-0.13.3}/src/moops/_interface_utils.py +0 -0
  14. {moops-0.13.2 → moops-0.13.3}/src/moops/_list_controls.py +0 -0
  15. {moops-0.13.2 → moops-0.13.3}/src/moops/_list_options.py +0 -0
  16. {moops-0.13.2 → moops-0.13.3}/src/moops/_marimo_controls.py +0 -0
  17. {moops-0.13.2 → moops-0.13.3}/src/moops/_markdown.py +0 -0
  18. {moops-0.13.2 → moops-0.13.3}/src/moops/_naming.py +0 -0
  19. {moops-0.13.2 → moops-0.13.3}/src/moops/_parse.py +0 -0
  20. {moops-0.13.2 → moops-0.13.3}/src/moops/_preset_state.py +0 -0
  21. {moops-0.13.2 → moops-0.13.3}/src/moops/_presets_ui.py +0 -0
  22. {moops-0.13.2 → moops-0.13.3}/src/moops/_query_params.py +0 -0
  23. {moops-0.13.2 → moops-0.13.3}/src/moops/_run.py +0 -0
  24. {moops-0.13.2 → moops-0.13.3}/src/moops/_run_button.py +0 -0
  25. {moops-0.13.2 → moops-0.13.3}/src/moops/_subgroup_registry.py +0 -0
  26. {moops-0.13.2 → moops-0.13.3}/src/moops/_terminal_graphics.py +0 -0
  27. {moops-0.13.2 → moops-0.13.3}/src/moops/_ui_workarounds.py +0 -0
  28. {moops-0.13.2 → moops-0.13.3}/src/moops/_value_resolution.py +0 -0
  29. {moops-0.13.2 → moops-0.13.3}/src/moops/_variant.py +0 -0
  30. {moops-0.13.2 → moops-0.13.3}/src/moops/group.py +0 -0
  31. {moops-0.13.2 → moops-0.13.3}/src/moops/presets.py +0 -0
  32. {moops-0.13.2 → moops-0.13.3}/src/moops/workarounds.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: moops
3
- Version: 0.13.2
3
+ Version: 0.13.3
4
4
  Summary: Write Marimo notebooks that also work as CLI scripts, with unified UI controls
5
5
  Keywords: marimo,notebooks,cli,testing
6
6
  Author: Yair Chuchem
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "moops"
7
- version = "0.13.2"
7
+ version = "0.13.3"
8
8
  description = "Write Marimo notebooks that also work as CLI scripts, with unified UI controls"
9
9
  license = "MIT"
10
10
  readme = "README.md"
@@ -425,6 +425,24 @@ def _missing_path_error(value: str) -> ParseError | None:
425
425
  return None
426
426
 
427
427
 
428
+ def _file_browser_paths(value: typing.Any) -> list[str]:
429
+ if not value:
430
+ return []
431
+ if isinstance(value, str):
432
+ return [value]
433
+ if isinstance(value, pathlib.Path):
434
+ return [str(value)]
435
+ if isinstance(value, typing.Sequence):
436
+ items = typing.cast(typing.Sequence[object], value)
437
+ return [
438
+ str(item.path)
439
+ if isinstance(item, _ui_workarounds.FileBrowserFileInfo)
440
+ else str(item)
441
+ for item in items
442
+ ]
443
+ return [str(value)]
444
+
445
+
428
446
  @dataclasses.dataclass
429
447
  class FileControl(TextControl):
430
448
  def create_marimo_element(
@@ -474,6 +492,14 @@ class FileControl(TextControl):
474
492
  continue
475
493
  return result
476
494
 
495
+ def format_value(self, value: typing.Any) -> list[str]:
496
+ paths = _file_browser_paths(value)
497
+ return super().format_value(paths[0] if paths else "")
498
+
499
+ def format_query_value(self, value: typing.Any) -> str | None:
500
+ paths = _file_browser_paths(value)
501
+ return super().format_query_value(paths[0] if paths else "")
502
+
477
503
 
478
504
  @dataclasses.dataclass
479
505
  class MultiFileControl(ValueControl):
@@ -554,13 +580,13 @@ class MultiFileControl(ValueControl):
554
580
  return [line]
555
581
 
556
582
  def format_value(self, value: typing.Any) -> list[str]:
557
- values = list(value)
583
+ values = _file_browser_paths(value)
558
584
  if values == self.default:
559
585
  return []
560
586
  return [option_value_token(self.option, v) for v in values]
561
587
 
562
588
  def format_query_value(self, value: typing.Any) -> str | None:
563
- values = list(value)
589
+ values = _file_browser_paths(value)
564
590
  return None if values == self.default else json.dumps(values)
565
591
 
566
592
  def prompt_interactive(self, effective_default: typing.Any = _UNSET) -> list[str]:
@@ -44,14 +44,16 @@ def wrap_help_line(line: str, width: int = 88) -> list[str]:
44
44
  return result
45
45
 
46
46
 
47
- def wrap_command(name: str, groups: list[str], width: int = 72) -> str:
47
+ def wrap_command(
48
+ name: str, groups: list[str], width: int = 72, *, quote_name: bool = True
49
+ ) -> str:
48
50
  """Render a script command, wrapping long lines with shell continuations.
49
51
 
50
52
  Short commands stay on one line. When the single-line form exceeds ``width``,
51
53
  each option group goes on its own line joined by `` \\`` continuations, which
52
54
  remains valid copy-pasteable shell.
53
55
  """
54
- quoted_name = shlex.quote(name)
56
+ quoted_name = shlex.quote(name) if quote_name else name
55
57
  single_line = " ".join([quoted_name, *groups])
56
58
  if not groups or len(single_line) <= width:
57
59
  return single_line
@@ -436,19 +436,22 @@ class Interface:
436
436
  if not self.notebook_name:
437
437
  return mo.md("Input bundle with no notebook name")
438
438
  notebook_name = html.escape(self.notebook_name)
439
- href = html.escape(self._standalone_url(), quote=True)
440
- link = mo.md(
441
- f'<a href="{href}" target="_blank" rel="noopener">'
442
- f"An embedded instance of `{notebook_name}`</a>"
443
- )
439
+ label = f"An embedded instance of `{notebook_name}`"
440
+ if _running_in_edit_mode():
441
+ href = html.escape(self._standalone_url(), quote=True)
442
+ heading: mo.Html = mo.md(
443
+ f'<a href="{href}" target="_blank" rel="noopener">{label}</a>'
444
+ )
445
+ else:
446
+ heading = mo.md(label)
444
447
  if self.embedded_extra_overrides != frozenset():
445
448
  # None: embedded outside moops.embed, so overrides are unknown.
446
449
  # Non-empty: the parent injected defs beyond args, so no CLI
447
450
  # command can reproduce this setup.
448
- return link
451
+ return heading
449
452
  command = _wrap_command(self.notebook_file, self._standalone_arg_groups())
450
453
  return mo.vstack(
451
- [link, mo.md(f"To run as a standalone script:\n```\n{command}\n```")]
454
+ [heading, mo.md(f"To run as a standalone script:\n```\n{command}\n```")]
452
455
  )
453
456
 
454
457
  def _standalone_url(self) -> str:
@@ -633,7 +636,9 @@ def attached_interface(ctrl: typing.Any) -> Interface | None:
633
636
 
634
637
  def _wrap_command(command: str, groups: list[str]) -> str:
635
638
  if _should_use_uv_run(command):
636
- return _text_wrap.wrap_command("uv", [f"run {shlex.quote(command)}", *groups])
639
+ return _text_wrap.wrap_command(
640
+ f"uv run {shlex.quote(command)}", groups, quote_name=False
641
+ )
637
642
  return _text_wrap.wrap_command(command, groups)
638
643
 
639
644
 
@@ -716,3 +721,25 @@ def _collect_usage_placeholders(
716
721
  for ctrl in iface.controls:
717
722
  if sub_iface := attached_interface(ctrl):
718
723
  _collect_usage_placeholders(sub_iface, result)
724
+
725
+
726
+ def _running_in_edit_mode() -> bool:
727
+ """Return True only when running inside a marimo *edit* session.
728
+
729
+ ``mo.running_in_notebook()`` is True for both ``marimo edit`` and
730
+ ``marimo run``, but links to ``/?file=...`` only work in edit sessions.
731
+ """
732
+ if not mo.running_in_notebook():
733
+ return False
734
+ try:
735
+ from marimo._runtime.context import get_context
736
+ from marimo._runtime.context.kernel_context import KernelRuntimeContext
737
+ from marimo._session.model import SessionMode
738
+
739
+ ctx = get_context()
740
+ return (
741
+ isinstance(ctx, KernelRuntimeContext)
742
+ and ctx.session_mode == SessionMode.EDIT
743
+ )
744
+ except Exception:
745
+ return False
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