Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: top level imports #3779

Merged
merged 19 commits into from
Feb 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
72a1643
feat: top level imports
dmadisetti Feb 13, 2025
022e799
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 13, 2025
314717a
fix: nvm imports _should_ have returns
dmadisetti Feb 13, 2025
14c50f4
Merge branch 'main' of github:marimo-team/marimo into dm/toplevel-imp…
dmadisetti Feb 13, 2025
042ce29
Merge branch 'dm/toplevel-imports' of github:marimo-team/marimo into …
dmadisetti Feb 13, 2025
7db678f
temp: import_guard relatively easy to put in
dmadisetti Feb 14, 2025
8d94f03
Merge branch 'main' into dm/toplevel-imports
dmadisetti Feb 14, 2025
8281b78
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 14, 2025
db9d257
format: formated imports with stripped comments over import_guard + t…
dmadisetti Feb 17, 2025
017f266
merge res
dmadisetti Feb 17, 2025
042417c
Merge branch 'main' of github:marimo-team/marimo into dm/toplevel-imp…
dmadisetti Feb 17, 2025
36aa98e
experimental upstream prior to design doc
dmadisetti Feb 17, 2025
ecd4b50
fix: py39 support
dmadisetti Feb 17, 2025
5f169a2
fix: more py39 typing support
dmadisetti Feb 17, 2025
310d46b
fix: py39 typing support
dmadisetti Feb 17, 2025
fffc67f
fix: wonder if my ruff is different locally?
dmadisetti Feb 17, 2025
b8f5889
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 17, 2025
22559eb
fix: strip whitespace (for now) for difference between CI
dmadisetti Feb 18, 2025
95c9d71
Merge branch 'dm/toplevel-imports' of github:marimo-team/marimo into …
dmadisetti Feb 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion marimo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"icon",
"iframe",
"image",
"import_guard",
"latex",
"lazy",
"left",
Expand Down Expand Up @@ -125,7 +126,7 @@
redirect_stderr,
redirect_stdout,
)
from marimo._runtime.context.utils import running_in_notebook
from marimo._runtime.context.utils import import_guard, running_in_notebook
from marimo._runtime.control_flow import MarimoStopError, stop
from marimo._runtime.runtime import (
app_meta,
Expand Down
27 changes: 20 additions & 7 deletions marimo/_ast/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@
TypeVar,
Union,
cast,
overload,
)
from uuid import uuid4

from typing_extensions import ParamSpec, TypeAlias

from marimo import _loggers
from marimo._ast.cell import Cell, CellConfig, CellId_t, CellImpl
from marimo._ast.cell_manager import CellManager
Expand Down Expand Up @@ -58,7 +61,9 @@
from marimo._runtime.context.types import ExecutionContext


Fn = TypeVar("Fn", bound=Callable[..., Any])
P = ParamSpec("P")
R = TypeVar("R")
Fn: TypeAlias = Callable[P, R]
LOGGER = _loggers.marimo_logger()


Expand Down Expand Up @@ -266,13 +271,13 @@ def clone(self) -> App:

def cell(
self,
func: Fn | None = None,
func: Fn[P, R] | None = None,
*,
column: Optional[int] = None,
disabled: bool = False,
hide_code: bool = False,
**kwargs: Any,
) -> Cell | Callable[[Fn], Cell]:
) -> Cell | Callable[[Fn[P, R]], Cell]:
"""A decorator to add a cell to the app.

This decorator can be called with or without parentheses. Each of the
Expand Down Expand Up @@ -302,21 +307,29 @@ def __(mo):
del kwargs

return cast(
Union[Cell, Callable[[Fn], Cell]],
Union[Cell, Callable[[Fn[P, R]], Cell]],
self._cell_manager.cell_decorator(
func, column, disabled, hide_code, app=InternalApp(self)
),
)

# Overloads are required to preserve the wrapped function's signature.
# mypy is not smart enough to carry transitive typing in this case.
@overload
def function(self, func: Fn[P, R]) -> Fn[P, R]: ...

@overload
def function(self, **kwargs: Any) -> Callable[[Fn[P, R]], Fn[P, R]]: ...

def function(
self,
func: Fn | None = None,
func: Fn[P, R] | None = None,
*,
column: Optional[int] = None,
disabled: bool = False,
hide_code: bool = False,
**kwargs: Any,
) -> Fn | Callable[[Fn], Fn]:
) -> Fn[P, R] | Callable[[Fn[P, R]], Fn[P, R]]:
"""A decorator to wrap a callable function into a cell in the app.

This decorator can be called with or without parentheses. Each of the
Expand Down Expand Up @@ -348,7 +361,7 @@ def multiply(a: int, b: int) -> int:
del kwargs

return cast(
Union[Fn, Callable[[Fn], Fn]],
Union[Fn[P, R], Callable[[Fn[P, R]], Fn[P, R]]],
self._cell_manager.cell_decorator(
func,
column,
Expand Down
14 changes: 9 additions & 5 deletions marimo/_ast/cell.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,8 +289,9 @@ def namespace_to_variable(self, namespace: str) -> Name | None:
def is_coroutine(self) -> bool:
return _is_coroutine(self.body) or _is_coroutine(self.last_expr)

@property
def is_toplevel_acceptable(self) -> bool:
def is_toplevel_acceptable(
self, allowed_refs: Optional[set[Name]] = None
) -> bool:
# Check no defs aside from the single function
if len(self.defs) != 1:
return False
Expand All @@ -316,13 +317,16 @@ def is_toplevel_acceptable(self) -> bool:
return False

# No required_refs are allowed for now
# TODO: Allow imports and other toplevel functions
refs = set().union(
*[v.required_refs for v in self.variable_data[name]]
)
refs -= set(globals()["__builtins__"].keys())
# NOTE: Builtins are allowed, but should be passed in under
# allowed_refs. Defers to allowed_refs because shadowed builtins
# are accounted for.
if allowed_refs is None:
allowed_refs = set(globals()["__builtins__"].keys())
# Allow recursion
refs -= {name}
refs -= {name} | allowed_refs
if refs:
return False

Expand Down
20 changes: 11 additions & 9 deletions marimo/_ast/cell_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@
import string
from typing import (
TYPE_CHECKING,
Any,
Callable,
Iterable,
Optional,
TypeVar,
)

from typing_extensions import ParamSpec, TypeAlias

from marimo._ast.cell import Cell, CellConfig, CellId_t
from marimo._ast.compiler import cell_factory, toplevel_cell_factory
from marimo._ast.models import CellData
Expand All @@ -23,7 +24,9 @@
if TYPE_CHECKING:
from marimo._ast.app import InternalApp

Fn = TypeVar("Fn", bound=Callable[..., Any])
P = ParamSpec("P")
R = TypeVar("R")
Fn: TypeAlias = Callable[P, R]


class CellManager:
Expand Down Expand Up @@ -74,20 +77,20 @@ def create_cell_id(self) -> CellId_t:
# TODO: maybe remove this, it is leaky
def cell_decorator(
self,
func: Fn | None,
func: Fn[P, R] | None,
column: Optional[int],
disabled: bool,
hide_code: bool,
app: InternalApp | None = None,
*,
top_level: bool = False,
) -> Cell | Fn | Callable[[Fn], Cell | Fn]:
) -> Cell | Fn[P, R] | Callable[[Fn[P, R]], Cell | Fn[P, R]]:
"""Create a cell decorator for marimo notebook cells."""
cell_config = CellConfig(
column=column, disabled=disabled, hide_code=hide_code
)

def _register(func: Fn) -> Cell | Fn:
def _register(func: Fn[P, R]) -> Cell | Fn[P, R]:
# Use PYTEST_VERSION here, opposed to PYTEST_CURRENT_TEST, in
# order to allow execution during test collection.
is_top_level_pytest = (
Expand All @@ -111,16 +114,15 @@ def _register(func: Fn) -> Cell | Fn:

# Manually set the signature for pytest.
if is_top_level_pytest:
func = wrap_fn_for_pytest(func, cell) # type: ignore
# NB. in place metadata update.
functools.wraps(func)(cell)
# NB. in place metadata update.
functools.wraps(wrap_fn_for_pytest(func, cell))(cell)
return cell

if func is None:
# If the decorator was used with parentheses, func will be None,
# and we return a decorator that takes the decorated function as an
# argument
def decorator(func: Fn) -> Cell | Fn:
def decorator(func: Fn[P, R]) -> Cell | Fn[P, R]:
return _register(func)

return decorator
Expand Down
Loading
Loading