diff --git a/codemode.md b/codemode.md index 900f723..8e9f4db 100644 --- a/codemode.md +++ b/codemode.md @@ -25,6 +25,9 @@ uvx lkr-dev-cli code-mode sandbox --code="return me()" # Execute Python script file uvx lkr-dev-cli code-mode sandbox --file=./path/to/script.py + +# Execute Python script file with injected variables (--var / -v) +uvx lkr-dev-cli code-mode sandbox --file=./path/to/script.py --var project=my_project --var connection=my_connection ``` ### 2. Starting the Server diff --git a/lkr.md b/lkr.md index 4042f5b..7f1fa58 100644 --- a/lkr.md +++ b/lkr.md @@ -10,6 +10,7 @@ $ lkr [OPTIONS] COMMAND [ARGS]... **Options**: +* `--version`: Show the version and exit. * `--client-id TEXT`: [env var: LOOKERSDK_CLIENT_ID] * `--client-secret TEXT`: [env var: LOOKERSDK_CLIENT_SECRET] * `--base-url TEXT`: [env var: LOOKERSDK_BASE_URL] @@ -262,6 +263,7 @@ $ lkr tools lookml [OPTIONS] COMMAND [ARGS]... #### `lkr tools lookml push` Push local files to Looker, removing files on the instance that aren't being pushed. +If --file / -f is specified (or folder_name is a file), only that single file is pushed without deleting remote orphans. **Usage**: @@ -276,6 +278,7 @@ $ lkr tools lookml push [OPTIONS] FOLDER_NAME **Options**: * `--project-id, --project TEXT`: Looker project ID to push to (if different from folder name) +* `-f, --file TEXT`: Single file relative path (or absolute path) to push * `--deploy`: Commit and deploy to production after push * `--message TEXT`: Commit message when deploying [default: push from lkr cli] * `--help`: Show this message and exit. @@ -283,6 +286,7 @@ $ lkr tools lookml push [OPTIONS] FOLDER_NAME #### `lkr tools lookml pull` Pull remote files from Looker to local disk, removing local files that aren't on the instance. +If --file / -f is specified, only that single file is pulled without deleting local orphans. **Usage**: @@ -297,6 +301,7 @@ $ lkr tools lookml pull [OPTIONS] FOLDER_NAME **Options**: * `--project-id, --project TEXT`: Looker project ID to pull from (if different from folder name) +* `-f, --file TEXT`: Single file relative path to pull from Looker * `--deploy`: Commit and deploy to production on Looker after pull * `--message TEXT`: Commit message when deploying [default: pull from lkr cli then commit and deploy] * `--help`: Show this message and exit. @@ -335,8 +340,25 @@ $ lkr code-mode [OPTIONS] COMMAND [ARGS]... **Commands**: +* `sandbox` * `run` +### `lkr code-mode sandbox` + +**Usage**: + +```console +$ lkr code-mode sandbox [OPTIONS] +``` + +**Options**: + +* `-c, --code TEXT`: Execute Python code directly in the sandbox +* `-f, --file TEXT`: Execute Python code from a file in the sandbox +* `--dev-mode`: Run in dev mode +* `-v, --var TEXT`: Inject variable as key=value pair (e.g. -v project=my_project) +* `--help`: Show this message and exit. + ### `lkr code-mode run` **Usage**: diff --git a/lkr/codemode/main.py b/lkr/codemode/main.py index a89ef1d..8894eca 100644 --- a/lkr/codemode/main.py +++ b/lkr/codemode/main.py @@ -1,6 +1,7 @@ import ast import inspect import json +import keyword import os import re import sys @@ -223,6 +224,9 @@ def sandbox( dev_mode: bool = typer.Option( False, "--dev-mode", help="Run in dev mode" ), + var: list[str] | None = typer.Option( + None, "--var", "-v", help="Inject variable as key=value pair (e.g. -v project=my_project)" + ), ): if not code and not file: logger.error("Must specify either --code or --file") @@ -239,8 +243,24 @@ def sandbox( except Exception as e: logger.error(f"Failed to read file {file}: {e}") raise typer.Exit(1) - else: + elif code: code_to_run = code + else: + raise typer.Exit(1) + + if var: + var_defs = [] + for v_str in var: + if "=" not in v_str: + logger.error(f"Invalid variable format: '{v_str}'. Must be key=value.") + raise typer.Exit(1) + k, v = v_str.split("=", 1) + k = k.strip() + if not k.isidentifier() or keyword.iskeyword(k): + logger.error(f"Invalid variable name: '{k}'. Must be a valid Python identifier and not a keyword.") + raise typer.Exit(1) + var_defs.append(f"{k} = {json.dumps(v)}") + code_to_run = "\n".join(var_defs) + "\n" + code_to_run global ctx_lkr ctx_lkr = ( diff --git a/lkr/codemode/readme.py b/lkr/codemode/readme.py index efe8ec7..e08bcb7 100644 --- a/lkr/codemode/readme.py +++ b/lkr/codemode/readme.py @@ -13,6 +13,7 @@ def get_readme() -> str: 6. **Efficiency**: Always use the `fields` parameter (e.g., `all_dashboards(fields="id,title")`) when listing many objects to prevent timeouts. 7. **Nested Folders**: Use `folder_children(id)` to get sub-folders. 8. **Search with Lookups**: If `help('pattern')` returns too many results and you want to see full details for all of them at once, use `search_with_lookups('pattern')`. +9. **Injected CLI Variables**: When using `sandbox` with `--var key=value` flags, those variables are directly available in your code as top-level string constants. EXAMPLES: