calkit-python 0.1.1__tar.gz → 0.2.0__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.
- {calkit_python-0.1.1 → calkit_python-0.2.0}/PKG-INFO +1 -1
- {calkit_python-0.1.1 → calkit_python-0.2.0}/calkit/__init__.py +1 -1
- {calkit_python-0.1.1 → calkit_python-0.2.0}/calkit/cli/core.py +5 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/calkit/cli/main.py +77 -33
- {calkit_python-0.1.1 → calkit_python-0.2.0}/calkit/cli/new.py +12 -25
- {calkit_python-0.1.1 → calkit_python-0.2.0}/calkit/dvc.py +15 -3
- {calkit_python-0.1.1 → calkit_python-0.2.0}/calkit/models.py +17 -0
- calkit_python-0.2.0/calkit/tests/test_dvc.py +15 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/.github/FUNDING.yml +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/.github/workflows/publish-test.yml +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/.github/workflows/publish.yml +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/.gitignore +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/LICENSE +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/README.md +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/calkit/cli/__init__.py +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/calkit/cli/config.py +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/calkit/cli/import_.py +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/calkit/cli/list.py +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/calkit/cli/notebooks.py +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/calkit/cloud.py +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/calkit/config.py +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/calkit/core.py +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/calkit/data.py +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/calkit/docker.py +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/calkit/git.py +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/calkit/gui.py +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/calkit/jupyter.py +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/calkit/server.py +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/calkit/tests/__init__.py +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/calkit/tests/cli/__init__.py +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/calkit/tests/cli/test_list.py +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/calkit/tests/cli/test_main.py +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/calkit/tests/cli/test_new.py +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/calkit/tests/test_core.py +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/calkit/tests/test_jupyter.py +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/examples/cfd-study/README.md +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/examples/cfd-study/calkit.yaml +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/examples/cfd-study/config/simulations/runs.csv +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/examples/cfd-study/notebook.ipynb +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/examples/ms-office/.gitignore +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/examples/ms-office/README.md +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/examples/ms-office/calkit.yaml +0 -0
- {calkit_python-0.1.1 → calkit_python-0.2.0}/pyproject.toml +0 -0
|
@@ -12,7 +12,7 @@ import typer
|
|
|
12
12
|
from typing_extensions import Annotated, Optional
|
|
13
13
|
|
|
14
14
|
import calkit
|
|
15
|
-
from calkit.cli import print_sep, run_cmd
|
|
15
|
+
from calkit.cli import print_sep, raise_error, run_cmd
|
|
16
16
|
from calkit.cli.config import config_app
|
|
17
17
|
from calkit.cli.import_ import import_app
|
|
18
18
|
from calkit.cli.list import list_app
|
|
@@ -26,8 +26,11 @@ app = typer.Typer(
|
|
|
26
26
|
pretty_exceptions_show_locals=False,
|
|
27
27
|
)
|
|
28
28
|
app.add_typer(config_app, name="config", help="Configure Calkit.")
|
|
29
|
+
app.add_typer(new_app, name="new", help="Create a new Calkit object.")
|
|
29
30
|
app.add_typer(
|
|
30
|
-
new_app,
|
|
31
|
+
new_app,
|
|
32
|
+
name="create",
|
|
33
|
+
help="Create a new Calkit object (alias for 'new').",
|
|
31
34
|
)
|
|
32
35
|
app.add_typer(notebooks_app, name="nb", help="Work with Jupyter notebooks.")
|
|
33
36
|
app.add_typer(list_app, name="list", help="List Calkit objects.")
|
|
@@ -46,6 +49,53 @@ def main(
|
|
|
46
49
|
raise typer.Exit()
|
|
47
50
|
|
|
48
51
|
|
|
52
|
+
@app.command(name="clone")
|
|
53
|
+
def clone(
|
|
54
|
+
url: Annotated[str, typer.Argument(help="Repo URL.")],
|
|
55
|
+
location: Annotated[
|
|
56
|
+
str,
|
|
57
|
+
typer.Argument(
|
|
58
|
+
help="Location to clone to (default will be ./{repo_name})"
|
|
59
|
+
),
|
|
60
|
+
] = None,
|
|
61
|
+
no_config_remote: Annotated[
|
|
62
|
+
bool,
|
|
63
|
+
typer.Option(
|
|
64
|
+
"--no-config-remote",
|
|
65
|
+
help="Do not automatically configure Calkit DVC remote.",
|
|
66
|
+
),
|
|
67
|
+
] = False,
|
|
68
|
+
no_dvc_pull: Annotated[
|
|
69
|
+
bool, typer.Option("--no-dvc-pull", help="Do not pull DVC objects.")
|
|
70
|
+
] = False,
|
|
71
|
+
):
|
|
72
|
+
"""Clone a Git repo and by default configure and pull from the DVC
|
|
73
|
+
remote.
|
|
74
|
+
"""
|
|
75
|
+
# Git clone
|
|
76
|
+
cmd = ["git", "clone", url]
|
|
77
|
+
if location is not None:
|
|
78
|
+
cmd.append(location)
|
|
79
|
+
try:
|
|
80
|
+
subprocess.call(cmd)
|
|
81
|
+
except Exception as e:
|
|
82
|
+
raise_error(str(e))
|
|
83
|
+
if location is None:
|
|
84
|
+
location = url.split("/")[-1].removesuffix(".git")
|
|
85
|
+
typer.echo(f"Moving into repo dir: {location}")
|
|
86
|
+
os.chdir(location)
|
|
87
|
+
# Setup auth for any Calkit remotes
|
|
88
|
+
if not no_config_remote:
|
|
89
|
+
remotes = calkit.dvc.get_remotes()
|
|
90
|
+
for name, url in remotes.items():
|
|
91
|
+
if name == "calkit" or name.startswith("calkit:"):
|
|
92
|
+
typer.echo(f"Setting up authentication for DVC remote: {name}")
|
|
93
|
+
calkit.dvc.set_remote_auth(remote_name=name)
|
|
94
|
+
# DVC pull
|
|
95
|
+
if not no_dvc_pull:
|
|
96
|
+
subprocess.call(["dvc", "pull"])
|
|
97
|
+
|
|
98
|
+
|
|
49
99
|
@app.command(name="status")
|
|
50
100
|
def get_status():
|
|
51
101
|
"""Get a unified Git and DVC status."""
|
|
@@ -97,8 +147,7 @@ def add(
|
|
|
97
147
|
adding any .dvc files to Git when adding to DVC.
|
|
98
148
|
"""
|
|
99
149
|
if to is not None and to not in ["git", "dvc"]:
|
|
100
|
-
|
|
101
|
-
raise typer.Exit(1)
|
|
150
|
+
raise_error(f"Invalid option for 'to': {to}")
|
|
102
151
|
# Ensure autostage is enabled for DVC
|
|
103
152
|
subprocess.call(["dvc", "config", "core.autostage", "true"])
|
|
104
153
|
subprocess.call(["git", "add", ".dvc/config"])
|
|
@@ -117,13 +166,11 @@ def add(
|
|
|
117
166
|
]
|
|
118
167
|
dvc_size_thresh_bytes = 1_000_000
|
|
119
168
|
if "." in paths and to is None:
|
|
120
|
-
|
|
121
|
-
raise typer.Exit(1)
|
|
169
|
+
raise_error("Cannot add '.' with calkit; use git or dvc")
|
|
122
170
|
if to is None:
|
|
123
171
|
for path in paths:
|
|
124
172
|
if os.path.isdir(path):
|
|
125
|
-
|
|
126
|
-
raise typer.Exit(1)
|
|
173
|
+
raise_error("Cannot auto-add directories; use git or dvc")
|
|
127
174
|
repo = git.Repo()
|
|
128
175
|
for path in paths:
|
|
129
176
|
# Detect if this file should be tracked with Git or DVC
|
|
@@ -323,8 +370,7 @@ def run_dvc_repro(
|
|
|
323
370
|
subprocess.call(["dvc", "repro"] + args)
|
|
324
371
|
# Now parse stage metadata for calkit objects
|
|
325
372
|
if not os.path.isfile("dvc.yaml"):
|
|
326
|
-
|
|
327
|
-
raise typer.Exit(1)
|
|
373
|
+
raise_error("No dvc.yaml file found")
|
|
328
374
|
objects = []
|
|
329
375
|
with open("dvc.yaml") as f:
|
|
330
376
|
pipeline = calkit.ryaml.load(f)
|
|
@@ -332,21 +378,18 @@ def run_dvc_repro(
|
|
|
332
378
|
ckmeta = stage_info.get("meta", {}).get("calkit")
|
|
333
379
|
if ckmeta is not None:
|
|
334
380
|
if not isinstance(ckmeta, dict):
|
|
335
|
-
|
|
381
|
+
raise_error(
|
|
336
382
|
f"Calkit metadata for {stage_name} is not a dictionary"
|
|
337
383
|
)
|
|
338
|
-
typer.Exit(1)
|
|
339
384
|
# Stage must have a single output
|
|
340
385
|
outs = stage_info.get("outs", [])
|
|
341
386
|
if len(outs) != 1:
|
|
342
|
-
|
|
387
|
+
raise_error(
|
|
343
388
|
f"Stage {stage_name} does not have exactly one output"
|
|
344
389
|
)
|
|
345
|
-
raise typer.Exit(1)
|
|
346
390
|
cktype = ckmeta.get("type")
|
|
347
391
|
if cktype not in ["figure", "dataset", "publication"]:
|
|
348
|
-
|
|
349
|
-
raise typer.Exit(1)
|
|
392
|
+
raise_error(f"Invalid Calkit output type '{cktype}'")
|
|
350
393
|
objects.append(
|
|
351
394
|
dict(path=outs[0]) | ckmeta | dict(stage=stage_name)
|
|
352
395
|
)
|
|
@@ -433,21 +476,24 @@ def run_in_env(
|
|
|
433
476
|
ck_info = calkit.load_calkit_info()
|
|
434
477
|
envs = ck_info.get("environments", {})
|
|
435
478
|
if not envs:
|
|
436
|
-
|
|
437
|
-
raise typer.Exit(1)
|
|
479
|
+
raise_error("No environments defined in calkit.yaml")
|
|
438
480
|
if isinstance(envs, list):
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
481
|
+
raise_error("Error: Environments should be a dict, not a list")
|
|
482
|
+
if env_name is None:
|
|
483
|
+
# See if there's a default env, or only one env defined
|
|
484
|
+
default_env_name = None
|
|
485
|
+
for n, e in envs.items():
|
|
486
|
+
if e.get("default"):
|
|
487
|
+
if default_env_name is not None:
|
|
488
|
+
raise_error(
|
|
489
|
+
"Only one default environment can be specified"
|
|
490
|
+
)
|
|
491
|
+
default_env_name = n
|
|
492
|
+
if default_env_name is None and len(envs) == 1:
|
|
493
|
+
default_env_name = list(envs.keys())[0]
|
|
494
|
+
env_name = default_env_name
|
|
449
495
|
if env_name is None:
|
|
450
|
-
|
|
496
|
+
raise_error("Environment must be specified if there are multiple")
|
|
451
497
|
env = envs[env_name]
|
|
452
498
|
cwd = os.getcwd()
|
|
453
499
|
image_name = env.get("image", env_name)
|
|
@@ -477,8 +523,7 @@ def run_in_env(
|
|
|
477
523
|
typer.echo(f"Running command: {cmd}")
|
|
478
524
|
subprocess.call(cmd)
|
|
479
525
|
else:
|
|
480
|
-
|
|
481
|
-
raise typer.Exit(1)
|
|
526
|
+
raise_error("Environment kind not supported")
|
|
482
527
|
|
|
483
528
|
|
|
484
529
|
@app.command(
|
|
@@ -507,8 +552,7 @@ def check_call(
|
|
|
507
552
|
subprocess.check_call(if_error, shell=True)
|
|
508
553
|
typer.echo("Fallback call succeeded")
|
|
509
554
|
except subprocess.CalledProcessError:
|
|
510
|
-
|
|
511
|
-
raise typer.Exit(1)
|
|
555
|
+
raise_error("Fallback call failed")
|
|
512
556
|
|
|
513
557
|
|
|
514
558
|
@app.command(
|
|
@@ -10,17 +10,13 @@ import typer
|
|
|
10
10
|
from typing_extensions import Annotated
|
|
11
11
|
|
|
12
12
|
import calkit
|
|
13
|
+
from calkit.cli import raise_error
|
|
13
14
|
from calkit.core import ryaml
|
|
14
15
|
from calkit.docker import LAYERS
|
|
15
16
|
|
|
16
17
|
new_app = typer.Typer(no_args_is_help=True)
|
|
17
18
|
|
|
18
19
|
|
|
19
|
-
def _error(txt):
|
|
20
|
-
typer.echo(txt, err=txt)
|
|
21
|
-
raise typer.Exit(1)
|
|
22
|
-
|
|
23
|
-
|
|
24
20
|
@new_app.command(name="figure")
|
|
25
21
|
def new_figure(
|
|
26
22
|
path: str,
|
|
@@ -69,18 +65,18 @@ def new_figure(
|
|
|
69
65
|
),
|
|
70
66
|
] = False,
|
|
71
67
|
):
|
|
72
|
-
"""
|
|
68
|
+
"""Create a new figure."""
|
|
73
69
|
ck_info = calkit.load_calkit_info()
|
|
74
70
|
figures = ck_info.get("figures", [])
|
|
75
71
|
paths = [f.get("path") for f in figures]
|
|
76
72
|
if not overwrite and path in paths:
|
|
77
|
-
|
|
73
|
+
raise_error(f"Figure at path {path} already exists")
|
|
78
74
|
if cmd is not None and stage_name is None:
|
|
79
|
-
|
|
75
|
+
raise_error("Stage name must be provided if command is specified")
|
|
80
76
|
if (deps or outs or outs_from_stage) and not cmd:
|
|
81
|
-
|
|
77
|
+
raise_error("Command must be provided")
|
|
82
78
|
if (deps or outs or outs_from_stage) and not stage_name:
|
|
83
|
-
|
|
79
|
+
raise_error("Stage name must be provided")
|
|
84
80
|
obj = dict(path=path, title=title)
|
|
85
81
|
if description is not None:
|
|
86
82
|
obj["description"] = description
|
|
@@ -91,7 +87,7 @@ def new_figure(
|
|
|
91
87
|
pipeline = calkit.dvc.read_pipeline()
|
|
92
88
|
stages = pipeline.get("stages", {})
|
|
93
89
|
if outs_from_stage not in stages:
|
|
94
|
-
|
|
90
|
+
raise_error(f"Stage {outs_from_stage} does not exist")
|
|
95
91
|
deps += stages[outs_from_stage].get("outs", [])
|
|
96
92
|
if path not in outs:
|
|
97
93
|
outs.append(path)
|
|
@@ -232,15 +228,9 @@ def new_docker_env(
|
|
|
232
228
|
):
|
|
233
229
|
"""Create a new Docker environment."""
|
|
234
230
|
if base and os.path.isfile(path) and not overwrite:
|
|
235
|
-
|
|
236
|
-
"Output path already exists (use -f to overwrite)", err=True
|
|
237
|
-
)
|
|
238
|
-
raise typer.Exit(1)
|
|
231
|
+
raise_error("Output path already exists (use -f to overwrite)")
|
|
239
232
|
if stage and not base:
|
|
240
|
-
|
|
241
|
-
"--from must be specified when creating a build stage", err=True
|
|
242
|
-
)
|
|
243
|
-
raise typer.Exit(1)
|
|
233
|
+
raise_error("--from must be specified when creating a build stage")
|
|
244
234
|
if image_name is None:
|
|
245
235
|
typer.echo("No image name specified; using environment name")
|
|
246
236
|
image_name = name
|
|
@@ -249,8 +239,7 @@ def new_docker_env(
|
|
|
249
239
|
txt = "FROM " + base + "\n\n"
|
|
250
240
|
for layer in layers:
|
|
251
241
|
if layer not in LAYERS:
|
|
252
|
-
|
|
253
|
-
raise typer.Exit(1)
|
|
242
|
+
raise_error(f"Unknown layer type '{layer}'")
|
|
254
243
|
txt += LAYERS[layer] + "\n\n"
|
|
255
244
|
txt += f"RUN mkdir {wdir}\n"
|
|
256
245
|
txt += f"WORKDIR {wdir}\n"
|
|
@@ -264,11 +253,10 @@ def new_docker_env(
|
|
|
264
253
|
typer.echo("Converting environments from list to dict")
|
|
265
254
|
envs = {env.pop("name"): env for env in envs}
|
|
266
255
|
if name in envs and not overwrite:
|
|
267
|
-
|
|
256
|
+
raise_error(
|
|
268
257
|
f"Environment with name {name} already exists "
|
|
269
258
|
"(use -f to overwrite)"
|
|
270
259
|
)
|
|
271
|
-
raise typer.Exit(1)
|
|
272
260
|
if base:
|
|
273
261
|
repo.git.add(path)
|
|
274
262
|
typer.echo("Adding environment to calkit.yaml")
|
|
@@ -348,8 +336,7 @@ def new_foreach_stage(
|
|
|
348
336
|
"""
|
|
349
337
|
pipeline = calkit.dvc.read_pipeline()
|
|
350
338
|
if name in pipeline and not overwrite:
|
|
351
|
-
|
|
352
|
-
raise typer.Exit(1)
|
|
339
|
+
raise_error("Stage already exists; use -f to overwrite")
|
|
353
340
|
if "stages" not in pipeline:
|
|
354
341
|
pipeline["stages"] = {}
|
|
355
342
|
pipeline["stages"][name] = dict(
|
|
@@ -67,9 +67,7 @@ def add_external_remote(owner_name: str, project_name: str):
|
|
|
67
67
|
base_url = calkit.cloud.get_base_url()
|
|
68
68
|
remote_url = f"{base_url}/projects/{owner_name}/{project_name}/dvc"
|
|
69
69
|
remote_name = f"{get_app_name()}:{owner_name}/{project_name}"
|
|
70
|
-
subprocess.call(
|
|
71
|
-
["dvc", "remote", "add", "-f", remote_name, remote_url]
|
|
72
|
-
)
|
|
70
|
+
subprocess.call(["dvc", "remote", "add", "-f", remote_name, remote_url])
|
|
73
71
|
subprocess.call(["dvc", "remote", "modify", remote_name, "auth", "custom"])
|
|
74
72
|
set_remote_auth(remote_name)
|
|
75
73
|
|
|
@@ -79,3 +77,17 @@ def read_pipeline() -> dict:
|
|
|
79
77
|
return {}
|
|
80
78
|
with open("dvc.yaml") as f:
|
|
81
79
|
return calkit.ryaml.load(f)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def get_remotes() -> dict[str, str]:
|
|
83
|
+
"""Get a dictionary of DVC remotes, keyed by name, with URL as the
|
|
84
|
+
value.
|
|
85
|
+
"""
|
|
86
|
+
out = subprocess.check_output(["dvc", "remote", "list"]).decode().strip()
|
|
87
|
+
if not out:
|
|
88
|
+
return {}
|
|
89
|
+
resp = {}
|
|
90
|
+
for line in out.split("\n"):
|
|
91
|
+
name, url = line.split("\t")
|
|
92
|
+
resp[name] = url
|
|
93
|
+
return resp
|
|
@@ -66,6 +66,7 @@ class Environment(BaseModel):
|
|
|
66
66
|
path: str | None = None
|
|
67
67
|
description: str | None = None
|
|
68
68
|
stage: str | None = None
|
|
69
|
+
default: bool | None = None
|
|
69
70
|
|
|
70
71
|
|
|
71
72
|
class DockerEnvironment(Environment):
|
|
@@ -89,8 +90,24 @@ class Notebook(_CalkitObject):
|
|
|
89
90
|
class ProjectInfo(BaseModel):
|
|
90
91
|
"""All of the project's information or metadata, written to the
|
|
91
92
|
``calkit.yaml`` file.
|
|
93
|
+
|
|
94
|
+
Attributes
|
|
95
|
+
----------
|
|
96
|
+
parent : str
|
|
97
|
+
The project's parent project, if applicable. This should be set if
|
|
98
|
+
the project was created as a copy of another. This is similar to the
|
|
99
|
+
concept of forking, but unlike a fork, a child project's changes
|
|
100
|
+
are not meant to be merged back into the parent.
|
|
101
|
+
The format of this field should be something like
|
|
102
|
+
{owner_name}/{project_name}, e.g., 'someuser/some-project-name'.
|
|
103
|
+
Note that individual objects can be imported from other projects, but
|
|
104
|
+
that doesn't necessarily make them parent projects.
|
|
105
|
+
This is probably not that important of a distinction.
|
|
106
|
+
The real use case is being able to trace where things came from and
|
|
107
|
+
distinguish what has been newly created here.
|
|
92
108
|
"""
|
|
93
109
|
|
|
110
|
+
parent: str | None = None
|
|
94
111
|
questions: list[str] = []
|
|
95
112
|
datasets: list[Dataset] = []
|
|
96
113
|
figures: list[Figure] = []
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Tests for the ``dvc`` module."""
|
|
2
|
+
|
|
3
|
+
import subprocess
|
|
4
|
+
|
|
5
|
+
import calkit
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_get_remotes(tmp_dir):
|
|
9
|
+
subprocess.call(["git", "init"])
|
|
10
|
+
assert not calkit.dvc.get_remotes()
|
|
11
|
+
subprocess.call(["dvc", "init"])
|
|
12
|
+
assert not calkit.dvc.get_remotes()
|
|
13
|
+
subprocess.call(["dvc", "remote", "add", "something", "https://sup.com"])
|
|
14
|
+
resp = calkit.dvc.get_remotes()
|
|
15
|
+
assert resp == {"something": "https://sup.com"}
|
|
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
|