lkr-dev-cli 0.0.43__tar.gz → 0.0.45__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.
- lkr_dev_cli-0.0.45/.github/workflows/website.yml +18 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/PKG-INFO +21 -9
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/README.md +20 -8
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/codemode.md +16 -5
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/codemode/main.py +60 -2
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/pyproject.toml +1 -1
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/tests/test_codemode.py +38 -1
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/.github/workflows/ci.yml +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/.github/workflows/release.yml +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/.gitignore +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/.python-version +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/.vscode/launch.json +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/.vscode/settings.json +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/Dockerfile +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/LICENSE +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/Makefile +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/cloudbuild.yaml +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/__init__.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/auth/__init__.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/auth/main.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/auth/oauth.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/auth_service.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/classes.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/codemode/LOCAL.md +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/codemode/__init__.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/codemode/constant.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/codemode/download_swagger.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/codemode/examples.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/codemode/help.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/codemode/readme.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/codemode/swagger.json +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/codemode/type.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/constants.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/custom_types.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/exceptions.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/extended_sdk_methods/__init__.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/extended_sdk_methods/classes.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/extended_sdk_methods/main.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/logger.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/main.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/mcp/classes.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/mcp/main.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/mcp/utils.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/observability/classes.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/observability/embed_container.html +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/observability/main.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/observability/utils.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/tools/classes.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/tools/lookml.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/tools/main.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr/tools/permission_deprecation.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr.md +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/lkr_dev_cli/__init__.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/tests/TESTING.md +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/tests/test_dependency_resolution.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/tests/test_deps.sh +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/tests/test_extended_sdk_methods.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/tests/test_init_sdk.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/tests/test_lookml_tools.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/tests/test_oauth_account.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/tests/test_permission_deprecation.py +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/ty.toml +0 -0
- {lkr_dev_cli-0.0.43 → lkr_dev_cli-0.0.45}/uv.lock +0 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
name: Deploy Website
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
paths:
|
|
8
|
+
- "**.md"
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
deploy:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- name: Trigger Website Deploy
|
|
15
|
+
run: |
|
|
16
|
+
curl -fSL "$DEPLOY_URL"
|
|
17
|
+
env:
|
|
18
|
+
DEPLOY_URL: ${{ secrets.DEPLOY_URL }}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lkr-dev-cli
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.45
|
|
4
4
|
Summary: lkr: a command line interface for looker
|
|
5
5
|
Author: bwebs
|
|
6
6
|
License-Expression: MIT
|
|
@@ -40,7 +40,7 @@ The `lkr` cli is a tool for interacting with Looker. It combines Looker's SDK an
|
|
|
40
40
|
|
|
41
41
|
## Usage
|
|
42
42
|
|
|
43
|
-
`uv` makes everyone's life easier. Go [install it](https://docs.astral.sh/uv/getting-started/installation/). You can start using `lkr` by running `uvx
|
|
43
|
+
`uv` makes everyone's life easier. Go [install it](https://docs.astral.sh/uv/getting-started/installation/). You can start using `lkr` by running `uvx lkr-dev-cli --help`.
|
|
44
44
|
|
|
45
45
|
Alternatively, you can install `lkr` with `pip install lkr-dev-cli[all]` and use commands directly like `lkr <command>`.
|
|
46
46
|
|
|
@@ -60,7 +60,7 @@ See the [prerequisites section](#oauth2-prerequisites)
|
|
|
60
60
|
Login to `lkr`
|
|
61
61
|
|
|
62
62
|
```bash
|
|
63
|
-
uvx
|
|
63
|
+
uvx lkr-dev-cli auth login
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
- Select a new instance
|
|
@@ -73,7 +73,7 @@ You will be redirected to the Looker OAuth authorization page, click Allow. If y
|
|
|
73
73
|
If everything is successful, you will see `Successfully authenticated!`. Test it with
|
|
74
74
|
|
|
75
75
|
```bash
|
|
76
|
-
uvx
|
|
76
|
+
uvx lkr-dev-cli auth whoami
|
|
77
77
|
```
|
|
78
78
|
|
|
79
79
|
### Using API Key
|
|
@@ -81,7 +81,7 @@ uvx --from lkr-dev-cli[all] lkr auth whoami
|
|
|
81
81
|
If you provide environment variables for `LOOKERSDK_CLIENT_ID`, `LOOKERSDK_CLIENT_SECRET`, and `LOOKERSDK_BASE_URL`, `lkr` will use the API key to authenticate and the commands. We also support command line arguments to pass in the client id, client secret, and base url.
|
|
82
82
|
|
|
83
83
|
```bash
|
|
84
|
-
uvx
|
|
84
|
+
uvx lkr-dev-cli --client-id <your client id> --client-secret <your client secret> --base-url <your instance url> auth whoami
|
|
85
85
|
```
|
|
86
86
|
|
|
87
87
|
|
|
@@ -111,7 +111,19 @@ Go to the Looker API Explorer for Register OAuth App (https://your.looker.instan
|
|
|
111
111
|
|
|
112
112
|
## Code Mode
|
|
113
113
|
|
|
114
|
-
Execute Python code safely with full Looker SDK coverage within a secure sandbox environment.
|
|
114
|
+
Execute Python code safely with full Looker SDK coverage within a secure sandbox environment (`Monty`). It dynamically inspects the Looker SDK for all public methods and injects them into the sandbox safely, supporting both Model Context Protocol (MCP) server mode and direct standalone CLI execution.
|
|
115
|
+
|
|
116
|
+
### Direct CLI Execution (Sandbox Mode)
|
|
117
|
+
Run scripts or inline code directly against your active Looker instance:
|
|
118
|
+
```bash
|
|
119
|
+
# Execute inline Python code
|
|
120
|
+
uvx lkr-dev-cli code-mode sandbox --code="return me()"
|
|
121
|
+
|
|
122
|
+
# Execute a Python script file
|
|
123
|
+
uvx lkr-dev-cli code-mode sandbox --file=./path/to/script.py
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
For detailed options, safe primitive transformations, MCP server configurations, and PKCE recovery, view the full [Code Mode Docs](./codemode.md).
|
|
115
127
|
|
|
116
128
|
## MCP
|
|
117
129
|
Built into the `lkr` is an MCP server. Right now its tools are based on helping you work within an IDE. To use it a tool like [Cursor](https://www.cursor.com/), add this to your mcp.json
|
|
@@ -120,8 +132,8 @@ Built into the `lkr` is an MCP server. Right now its tools are based on helping
|
|
|
120
132
|
{
|
|
121
133
|
"mcpServers": {
|
|
122
134
|
"lkr-mcp": {
|
|
123
|
-
"command": "
|
|
124
|
-
"args": ["
|
|
135
|
+
"command": "uvx",
|
|
136
|
+
"args": ["lkr-dev-cli[all]", "mcp", "run"]
|
|
125
137
|
},
|
|
126
138
|
"lkr-mcp-docker": {
|
|
127
139
|
"command": "docker",
|
|
@@ -422,7 +434,7 @@ By running this tool, an admin can proactively identify and fix permission gaps
|
|
|
422
434
|
This command should be run by a **Looker Admin**.
|
|
423
435
|
|
|
424
436
|
```bash
|
|
425
|
-
uvx
|
|
437
|
+
uvx lkr-dev-cli[all] tools schedule-download-deprecation
|
|
426
438
|
```
|
|
427
439
|
|
|
428
440
|
Options:
|
|
@@ -4,7 +4,7 @@ The `lkr` cli is a tool for interacting with Looker. It combines Looker's SDK an
|
|
|
4
4
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
|
7
|
-
`uv` makes everyone's life easier. Go [install it](https://docs.astral.sh/uv/getting-started/installation/). You can start using `lkr` by running `uvx
|
|
7
|
+
`uv` makes everyone's life easier. Go [install it](https://docs.astral.sh/uv/getting-started/installation/). You can start using `lkr` by running `uvx lkr-dev-cli --help`.
|
|
8
8
|
|
|
9
9
|
Alternatively, you can install `lkr` with `pip install lkr-dev-cli[all]` and use commands directly like `lkr <command>`.
|
|
10
10
|
|
|
@@ -24,7 +24,7 @@ See the [prerequisites section](#oauth2-prerequisites)
|
|
|
24
24
|
Login to `lkr`
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
uvx
|
|
27
|
+
uvx lkr-dev-cli auth login
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
- Select a new instance
|
|
@@ -37,7 +37,7 @@ You will be redirected to the Looker OAuth authorization page, click Allow. If y
|
|
|
37
37
|
If everything is successful, you will see `Successfully authenticated!`. Test it with
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
|
-
uvx
|
|
40
|
+
uvx lkr-dev-cli auth whoami
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
### Using API Key
|
|
@@ -45,7 +45,7 @@ uvx --from lkr-dev-cli[all] lkr auth whoami
|
|
|
45
45
|
If you provide environment variables for `LOOKERSDK_CLIENT_ID`, `LOOKERSDK_CLIENT_SECRET`, and `LOOKERSDK_BASE_URL`, `lkr` will use the API key to authenticate and the commands. We also support command line arguments to pass in the client id, client secret, and base url.
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
|
-
uvx
|
|
48
|
+
uvx lkr-dev-cli --client-id <your client id> --client-secret <your client secret> --base-url <your instance url> auth whoami
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
|
|
@@ -75,7 +75,19 @@ Go to the Looker API Explorer for Register OAuth App (https://your.looker.instan
|
|
|
75
75
|
|
|
76
76
|
## Code Mode
|
|
77
77
|
|
|
78
|
-
Execute Python code safely with full Looker SDK coverage within a secure sandbox environment.
|
|
78
|
+
Execute Python code safely with full Looker SDK coverage within a secure sandbox environment (`Monty`). It dynamically inspects the Looker SDK for all public methods and injects them into the sandbox safely, supporting both Model Context Protocol (MCP) server mode and direct standalone CLI execution.
|
|
79
|
+
|
|
80
|
+
### Direct CLI Execution (Sandbox Mode)
|
|
81
|
+
Run scripts or inline code directly against your active Looker instance:
|
|
82
|
+
```bash
|
|
83
|
+
# Execute inline Python code
|
|
84
|
+
uvx lkr-dev-cli code-mode sandbox --code="return me()"
|
|
85
|
+
|
|
86
|
+
# Execute a Python script file
|
|
87
|
+
uvx lkr-dev-cli code-mode sandbox --file=./path/to/script.py
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
For detailed options, safe primitive transformations, MCP server configurations, and PKCE recovery, view the full [Code Mode Docs](./codemode.md).
|
|
79
91
|
|
|
80
92
|
## MCP
|
|
81
93
|
Built into the `lkr` is an MCP server. Right now its tools are based on helping you work within an IDE. To use it a tool like [Cursor](https://www.cursor.com/), add this to your mcp.json
|
|
@@ -84,8 +96,8 @@ Built into the `lkr` is an MCP server. Right now its tools are based on helping
|
|
|
84
96
|
{
|
|
85
97
|
"mcpServers": {
|
|
86
98
|
"lkr-mcp": {
|
|
87
|
-
"command": "
|
|
88
|
-
"args": ["
|
|
99
|
+
"command": "uvx",
|
|
100
|
+
"args": ["lkr-dev-cli[all]", "mcp", "run"]
|
|
89
101
|
},
|
|
90
102
|
"lkr-mcp-docker": {
|
|
91
103
|
"command": "docker",
|
|
@@ -386,7 +398,7 @@ By running this tool, an admin can proactively identify and fix permission gaps
|
|
|
386
398
|
This command should be run by a **Looker Admin**.
|
|
387
399
|
|
|
388
400
|
```bash
|
|
389
|
-
uvx
|
|
401
|
+
uvx lkr-dev-cli[all] tools schedule-download-deprecation
|
|
390
402
|
```
|
|
391
403
|
|
|
392
404
|
Options:
|
|
@@ -16,10 +16,21 @@ The tool instantiates Looker SDK natively, searches all bound methods, and passe
|
|
|
16
16
|
|
|
17
17
|
## Continuous Usage
|
|
18
18
|
|
|
19
|
-
### 1.
|
|
20
|
-
|
|
19
|
+
### 1. Direct CLI Execution (Sandbox Mode)
|
|
20
|
+
If you want to execute Python scripts or inline code directly against your Looker instance without setting up an MCP client, use the `sandbox` command:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Execute inline Python code
|
|
24
|
+
uvx lkr-dev-cli code-mode sandbox --code="return me()"
|
|
25
|
+
|
|
26
|
+
# Execute Python script file
|
|
27
|
+
uvx lkr-dev-cli code-mode sandbox --file=./path/to/script.py
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### 2. Starting the Server
|
|
31
|
+
To immediately trigger the MCP stdio listener, use:
|
|
21
32
|
```bash
|
|
22
|
-
uvx -q
|
|
33
|
+
uvx -q lkr-dev-cli[codemode] code-mode run
|
|
23
34
|
```
|
|
24
35
|
|
|
25
36
|
### 2. Client Configuration
|
|
@@ -30,7 +41,7 @@ To hook this server into Cursor or Claude Desktop natively over stdio, append th
|
|
|
30
41
|
"mcpServers": {
|
|
31
42
|
"lkr_codemode": {
|
|
32
43
|
"command": "uvx",
|
|
33
|
-
"args": ["-q", "
|
|
44
|
+
"args": ["-q", "lkr-dev-cli[codemode]", "code-mode", "run"],
|
|
34
45
|
"env": {
|
|
35
46
|
"LOOKERSDK_BASE_URL": "https://your.looker.instance",
|
|
36
47
|
"LOOKERSDK_CLIENT_ID": "your-client-id",
|
|
@@ -44,7 +55,7 @@ To hook this server into Cursor or Claude Desktop natively over stdio, append th
|
|
|
44
55
|
### 3. Visual Inspector
|
|
45
56
|
To check things out on a web panel:
|
|
46
57
|
```bash
|
|
47
|
-
npx @modelcontextprotocol/inspector uvx -q
|
|
58
|
+
npx @modelcontextprotocol/inspector uvx -q lkr-dev-cli[codemode] code-mode run
|
|
48
59
|
```
|
|
49
60
|
|
|
50
61
|
## Architectural & Troubleshooting Notes
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import ast
|
|
1
2
|
import inspect
|
|
2
3
|
import json
|
|
3
4
|
import os
|
|
5
|
+
import re
|
|
4
6
|
import sys
|
|
5
7
|
import tempfile
|
|
6
8
|
from contextlib import contextmanager
|
|
@@ -21,7 +23,7 @@ from lkr.logger import logger
|
|
|
21
23
|
__all__ = ["group"]
|
|
22
24
|
|
|
23
25
|
mcp = FastMCP("lkr:codemode")
|
|
24
|
-
group = typer.Typer()
|
|
26
|
+
group = typer.Typer(no_args_is_help=True)
|
|
25
27
|
ctx_lkr: LkrCtxObj | None = None
|
|
26
28
|
class OSCapture:
|
|
27
29
|
def __init__(self):
|
|
@@ -156,6 +158,24 @@ def run_python_code(code: str, dev_mode: bool = False) -> str:
|
|
|
156
158
|
setattr(SDK, name, staticmethod(func))
|
|
157
159
|
external_funcs['sdk'] = SDK
|
|
158
160
|
|
|
161
|
+
# Monty external_functions do not support attribute lookups on objects.
|
|
162
|
+
# Pre-process the code to replace `sdk.method_name` with `method_name` safely using AST.
|
|
163
|
+
try:
|
|
164
|
+
class SDKAttributeRewriter(ast.NodeTransformer):
|
|
165
|
+
def visit_Attribute(self, node):
|
|
166
|
+
self.generic_visit(node)
|
|
167
|
+
if isinstance(node.value, ast.Name) and node.value.id == 'sdk':
|
|
168
|
+
return ast.copy_location(ast.Name(id=node.attr, ctx=node.ctx), node)
|
|
169
|
+
return node
|
|
170
|
+
|
|
171
|
+
tree = ast.parse(code)
|
|
172
|
+
tree = SDKAttributeRewriter().visit(tree)
|
|
173
|
+
ast.fix_missing_locations(tree)
|
|
174
|
+
code = ast.unparse(tree)
|
|
175
|
+
except Exception:
|
|
176
|
+
# Fallback to regex if parsing fails
|
|
177
|
+
code = re.sub(r"\bsdk\.([a-zA-Z_][a-zA-Z0-9_]*)\b", r"\1", code)
|
|
178
|
+
|
|
159
179
|
m = pydantic_monty.Monty(code)
|
|
160
180
|
|
|
161
181
|
# Use low-level OS stdout capture to ensure any print() statements
|
|
@@ -191,7 +211,45 @@ def run_python_code(code: str, dev_mode: bool = False) -> str:
|
|
|
191
211
|
return f"Error: {str(e)}"
|
|
192
212
|
|
|
193
213
|
|
|
214
|
+
@group.command(name="sandbox")
|
|
215
|
+
def sandbox(
|
|
216
|
+
ctx: typer.Context,
|
|
217
|
+
code: str | None = typer.Option(
|
|
218
|
+
None, "--code", "-c", help="Execute Python code directly in the sandbox"
|
|
219
|
+
),
|
|
220
|
+
file: str | None = typer.Option(
|
|
221
|
+
None, "--file", "-f", help="Execute Python code from a file in the sandbox"
|
|
222
|
+
),
|
|
223
|
+
dev_mode: bool = typer.Option(
|
|
224
|
+
False, "--dev-mode", help="Run in dev mode"
|
|
225
|
+
),
|
|
226
|
+
):
|
|
227
|
+
if not code and not file:
|
|
228
|
+
logger.error("Must specify either --code or --file")
|
|
229
|
+
raise typer.Exit(1)
|
|
230
|
+
|
|
231
|
+
if code and file:
|
|
232
|
+
logger.error("Cannot specify both --code and --file")
|
|
233
|
+
raise typer.Exit(1)
|
|
234
|
+
|
|
235
|
+
if file:
|
|
236
|
+
try:
|
|
237
|
+
with open(file, "r", encoding="utf-8") as f:
|
|
238
|
+
code_to_run = f.read()
|
|
239
|
+
except Exception as e:
|
|
240
|
+
logger.error(f"Failed to read file {file}: {e}")
|
|
241
|
+
raise typer.Exit(1)
|
|
242
|
+
else:
|
|
243
|
+
code_to_run = code
|
|
194
244
|
|
|
245
|
+
global ctx_lkr
|
|
246
|
+
ctx_lkr = (
|
|
247
|
+
ctx.obj.get("ctx_lkr")
|
|
248
|
+
if (ctx and ctx.obj and "ctx_lkr" in ctx.obj)
|
|
249
|
+
else LkrCtxObj(force_oauth=False)
|
|
250
|
+
)
|
|
251
|
+
result = run_python_code(code_to_run, dev_mode=dev_mode)
|
|
252
|
+
typer.echo(result)
|
|
195
253
|
|
|
196
254
|
|
|
197
255
|
@group.command(name="run")
|
|
@@ -202,7 +260,7 @@ def run(
|
|
|
202
260
|
global ctx_lkr
|
|
203
261
|
ctx_lkr = (
|
|
204
262
|
ctx.obj.get("ctx_lkr")
|
|
205
|
-
if (ctx.obj and "ctx_lkr" in ctx.obj)
|
|
263
|
+
if (ctx and ctx.obj and "ctx_lkr" in ctx.obj)
|
|
206
264
|
else LkrCtxObj(force_oauth=False)
|
|
207
265
|
)
|
|
208
266
|
mcp.run()
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
from typing import Any, cast
|
|
2
2
|
import json
|
|
3
|
+
import os
|
|
4
|
+
import tempfile
|
|
3
5
|
import pytest
|
|
4
6
|
from unittest.mock import patch, MagicMock
|
|
7
|
+
from typer.testing import CliRunner
|
|
5
8
|
from looker_sdk.rtl.auth_session import AuthSession
|
|
6
9
|
from looker_sdk.rtl.transport import Transport
|
|
7
10
|
from lkr.codemode.main import run_python_code
|
|
8
11
|
from lkr.extended_sdk_methods import ExtendedLooker40SDK
|
|
12
|
+
from lkr.main import app
|
|
9
13
|
|
|
10
14
|
|
|
11
15
|
class DummyAuth:
|
|
@@ -67,7 +71,7 @@ me_obj = sdk.me()
|
|
|
67
71
|
return me_obj["first_name"]
|
|
68
72
|
"""
|
|
69
73
|
result = run_python_code(code_sdk)
|
|
70
|
-
assert
|
|
74
|
+
assert result == "Test"
|
|
71
75
|
|
|
72
76
|
def test_examples():
|
|
73
77
|
code_examples = """
|
|
@@ -283,3 +287,36 @@ return {"status": "clean"}
|
|
|
283
287
|
|
|
284
288
|
out, err = capfd.readouterr()
|
|
285
289
|
assert out == ""
|
|
290
|
+
|
|
291
|
+
runner = CliRunner()
|
|
292
|
+
|
|
293
|
+
def test_cli_code_mode_sandbox_code():
|
|
294
|
+
result = runner.invoke(app, ["code-mode", "sandbox", "--code", "return me()"])
|
|
295
|
+
assert result.exit_code == 0
|
|
296
|
+
assert "Test" in result.stdout
|
|
297
|
+
|
|
298
|
+
def test_cli_code_mode_sandbox_sdk_code():
|
|
299
|
+
result = runner.invoke(app, ["code-mode", "sandbox", "--code", "return sdk.me()"])
|
|
300
|
+
assert result.exit_code == 0
|
|
301
|
+
assert "Test" in result.stdout
|
|
302
|
+
|
|
303
|
+
def test_cli_code_mode_sandbox_file():
|
|
304
|
+
with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
|
|
305
|
+
f.write("return me()")
|
|
306
|
+
temp_path = f.name
|
|
307
|
+
|
|
308
|
+
try:
|
|
309
|
+
result = runner.invoke(app, ["code-mode", "sandbox", "--file", temp_path])
|
|
310
|
+
assert result.exit_code == 0
|
|
311
|
+
assert "Test" in result.stdout
|
|
312
|
+
finally:
|
|
313
|
+
if os.path.exists(temp_path):
|
|
314
|
+
os.remove(temp_path)
|
|
315
|
+
|
|
316
|
+
def test_cli_code_mode_sandbox_both_error():
|
|
317
|
+
result = runner.invoke(app, ["code-mode", "sandbox", "--code", "return me()", "--file", "dummy.py"])
|
|
318
|
+
assert result.exit_code == 1
|
|
319
|
+
|
|
320
|
+
def test_cli_code_mode_sandbox_neither_error():
|
|
321
|
+
result = runner.invoke(app, ["code-mode", "sandbox"])
|
|
322
|
+
assert result.exit_code == 1
|
|
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
|
|
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
|
|
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
|
|
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
|