Skip to content

Commit

Permalink
feat: mo.query_params (#1006)
Browse files Browse the repository at this point in the history
* feat: mo.query_params

* make query params reactive

* fixes

* wasm

* lint

* fixes

* fix test

* python 3.8 compat fix

* fixes

---------

Co-authored-by: Akshay Agrawal <akshay@marimo.io>
  • Loading branch information
mscolnick and akshayka committed Mar 30, 2024
1 parent 20f1789 commit 5383dd9
Show file tree
Hide file tree
Showing 25 changed files with 606 additions and 26 deletions.
2 changes: 2 additions & 0 deletions docs/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
outputs
control_flow
html
query_params
state
cell
debugging
Expand All @@ -40,6 +41,7 @@ Use the marimo library in marimo notebooks (`import marimo as mo`) to
| {doc}`outputs` | Modify cell outputs, redirect console output |
| {doc}`control_flow` | Control how cells execute |
| {doc}`html` | Manipulate HTML objects |
| {doc}`query_params` | Access and set query parameters with `mo.query_params` |
| {doc}`state` | Synchronize multiple UI elements with `mo.state` |
| {doc}`cell` | Run cells defined in another notebook |
| {doc}`debugging` | Debugging utilities |
10 changes: 10 additions & 0 deletions docs/api/query_params.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Query Parameters

Use `mo.query_params` to access query parameters passed to the notebook. You
can also use `mo.query_params` to set query parameters in order to keep track
of state in the URL. This is useful for bookmarking or sharing a particular
state of the notebook while running as an application with `marimo run`.

```{eval-rst}
.. autofunction:: marimo.query_params
```
6 changes: 1 addition & 5 deletions docs/guides/apps.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ By default, apps are laid out as a concetentation of their outputs, with
code hidden. You can customize the layout using marimo's built-in drag-and-drop
grid editor; you can also choose to include code in the app view.


## CLI
## CLI

```
Usage: marimo run [OPTIONS] NAME
Expand Down Expand Up @@ -36,7 +35,6 @@ While editing a notebook with `marimo edit`, you can preview the notebook
as an app by clicking the preview button in the bottom-left of the editor.
(You can also use the command palette.)


### Vertical layout

The default layout is the vertical layout: cell outputs are concatenated
Expand Down Expand Up @@ -64,5 +62,3 @@ Enable the grid editor in the app preview, via a dropdown:
marimo saves metadata about your constructed layout in a `layouts` folder;
make sure to include this folder when sharing your notebook so that others
can reconstruct your layout.


24 changes: 24 additions & 0 deletions frontend/src/core/kernel/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,30 @@ export type OperationMessage =
}>;
};
}
| {
op: "query-params-set";
data: {
key: string;
value: string | string[];
};
}
| {
op: "query-params-append";
data: {
key: string;
value: string;
};
}
| {
op: "query-params-delete";
data: {
key: string;
value: string | null;
};
}
| {
op: "query-params-clear";
}
| {
// transient toast / alert
op: "alert";
Expand Down
9 changes: 9 additions & 0 deletions frontend/src/core/pyodide/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,16 @@ export class PyodideBridge implements RunRequests, EditRequests {
const code = await notebookFileStore.readFile();
const fallbackCode = await fallbackFileStore.readFile();
const filename = PyodideRouter.getFilename();

const queryParameters: Record<string, string | string[]> = {};
const searchParams = new URLSearchParams(window.location.search);
for (const key of searchParams.keys()) {
const value = searchParams.getAll(key);
queryParameters[key] = value.length === 1 ? value[0] : value;
}

await this.rpc.proxy.request.startSession({
queryParameters: queryParameters,
code,
fallbackCode: fallbackCode || "",
filename,
Expand Down
13 changes: 11 additions & 2 deletions frontend/src/core/pyodide/worker/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export class DefaultWasmController implements WasmController {
}

async startSession(opts: {
queryParameters: Record<string, string | string[]>;
code: string | null;
fallbackCode: string;
filename: string | null;
Expand All @@ -106,7 +107,10 @@ export class DefaultWasmController implements WasmController {
// during processing of a cell's code.
//
// This adds a messenger object to the global scope (import js; js.messenger.callback)
self.messenger = { callback: opts.onMessage };
self.messenger = {
callback: opts.onMessage,
};
self.query_params = opts.queryParameters;

// Load packages from the code
await this.pyodide.loadPackagesFromImports(content, {
Expand All @@ -122,8 +126,13 @@ export class DefaultWasmController implements WasmController {
from marimo._pyodide.pyodide_session import create_session, instantiate
assert js.messenger, "messenger is not defined"
assert js.query_params, "query_params is not defined"
session, bridge = create_session(filename="${filename}", message_callback=js.messenger.callback)
session, bridge = create_session(
filename="${filename}",
query_params=js.query_params.to_py(),
message_callback=js.messenger.callback,
)
instantiate(session)
asyncio.create_task(session.start())
Expand Down
1 change: 1 addition & 0 deletions frontend/src/core/pyodide/worker/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export interface WasmController {
* @param opts.filename - The filename to start with
*/
startSession(opts: {
queryParameters: Record<string, string | string[]>;
code: string | null;
fallbackCode: string;
filename: string | null;
Expand Down
1 change: 1 addition & 0 deletions frontend/src/core/pyodide/worker/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ const requestHandler = createRPCRequestHandler({
* Start the session
*/
startSession: async (opts: {
queryParameters: Record<string, string | string[]>;
code: string | null;
fallbackCode: string;
filename: string | null;
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/core/websocket/createWsUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export function createWsUrl(sessionId: string): string {
url.protocol = protocol;
url.pathname = `${withoutTrailingSlash(url.pathname)}/ws`;

const searchParams = new URLSearchParams(url.search);
const searchParams = new URLSearchParams(window.location.search);
searchParams.set("session_id", sessionId);
url.search = searchParams.toString();

Expand Down
35 changes: 35 additions & 0 deletions frontend/src/core/websocket/useMarimoWebSocket.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,41 @@ export function useMarimoWebSocket(opts: {
kind: "installing",
});
return;
case "query-params-append": {
const url = new URL(window.location.href);
url.searchParams.append(msg.data.key, msg.data.value);
window.history.pushState({}, "", `${url.pathname}${url.search}`);
return;
}
case "query-params-set": {
const url = new URL(window.location.href);
if (Array.isArray(msg.data.value)) {
url.searchParams.delete(msg.data.key);
msg.data.value.forEach((v) =>
url.searchParams.append(msg.data.key, v),
);
} else {
url.searchParams.set(msg.data.key, msg.data.value);
}
window.history.pushState({}, "", `${url.pathname}${url.search}`);
return;
}
case "query-params-delete": {
const url = new URL(window.location.href);
if (msg.data.value == null) {
url.searchParams.delete(msg.data.key);
} else {
url.searchParams.delete(msg.data.key, msg.data.value);
}
window.history.pushState({}, "", `${url.pathname}${url.search}`);
return;
}
case "query-params-clear": {
const url = new URL(window.location.href);
url.search = "";
window.history.pushState({}, "", `${url.pathname}${url.search}`);
return;
}
default:
logNever(msg);
}
Expand Down
3 changes: 2 additions & 1 deletion marimo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"capture_stderr",
"center",
"defs",
"query_params",
"doc",
"download",
"hstack",
Expand Down Expand Up @@ -89,6 +90,6 @@
redirect_stdout,
)
from marimo._runtime.control_flow import MarimoStopError, stop
from marimo._runtime.runtime import defs, refs
from marimo._runtime.runtime import defs, query_params, refs
from marimo._runtime.state import state
from marimo._server.asgi import create_asgi_app
46 changes: 43 additions & 3 deletions marimo/_messaging/ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,21 +386,61 @@ class VariableValues(Op):
variables: List[VariableValue]


@dataclass
class QueryParamsSet(Op):
"""Set query parameters."""

name: ClassVar[str] = "query-params-set"
key: str
value: Union[str, List[str]]


@dataclass
class QueryParamsAppend(Op):
name: ClassVar[str] = "query-params-append"
key: str
value: str


@dataclass
class QueryParamsDelete(Op):
name: ClassVar[str] = "query-params-delete"
key: str
# If value is None, delete all values for the key
# If a value is provided, only that value is deleted
value: Optional[str]


@dataclass
class QueryParamsClear(Op):
# Clear all query parameters
name: ClassVar[str] = "query-params-clear"


MessageOperation = Union[
# Cell operations
CellOp,
HumanReadableStatus,
Reload,
Reconnected,
FunctionCallResult,
RemoveUIElements,
# Notebook operations
Reload,
Reconnected,
Interrupted,
CompletedRun,
KernelReady,
# Editor operations
CompletionResult,
# Alerts
Alert,
Banner,
MissingPackageAlert,
InstallingPackageAlert,
# Variables
Variables,
VariableValues,
# Query params
QueryParamsSet,
QueryParamsAppend,
QueryParamsDelete,
QueryParamsClear,
]
5 changes: 5 additions & 0 deletions marimo/_messaging/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ def write(self, op: str, data: Dict[Any, Any]) -> None:
pass


class NoopStream(Stream):
def write(self, op: str, data: Dict[Any, Any]) -> None:
pass


class Stdout(io.TextIOBase):
name = "stdout"

Expand Down
4 changes: 3 additions & 1 deletion marimo/_pyodide/pyodide_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
ControlRequest,
CreationRequest,
ExecutionRequest,
SerializedQueryParams,
SetUIElementValueRequest,
)
from marimo._runtime.runtime import Kernel
Expand Down Expand Up @@ -78,14 +79,15 @@ def instantiate(session: PyodideSession) -> None:

def create_session(
filename: str,
query_params: SerializedQueryParams,
message_callback: Callable[[str], None],
) -> tuple[PyodideSession, PyodideBridge]:
def write_kernel_message(op: KernelMessage) -> None:
message_callback(json.dumps({"op": op[0], "data": op[1]}))

app_file_manager = AppFileManager(filename=filename)
app = app_file_manager.app
app_metadata = AppMetadata(filename=filename)
app_metadata = AppMetadata(query_params=query_params, filename=filename)

session = PyodideSession(
app_file_manager, SessionMode.EDIT, write_kernel_message, app_metadata
Expand Down
Loading

0 comments on commit 5383dd9

Please sign in to comment.