wandb 0.15.3__py3-none-any.whl → 0.15.5__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- wandb/__init__.py +1 -1
- wandb/analytics/sentry.py +1 -0
- wandb/apis/importers/base.py +20 -5
- wandb/apis/importers/mlflow.py +7 -1
- wandb/apis/internal.py +12 -0
- wandb/apis/public.py +247 -1387
- wandb/apis/reports/_panels.py +58 -35
- wandb/beta/workflows.py +6 -7
- wandb/cli/cli.py +130 -60
- wandb/data_types.py +3 -1
- wandb/filesync/dir_watcher.py +21 -27
- wandb/filesync/step_checksum.py +8 -8
- wandb/filesync/step_prepare.py +23 -10
- wandb/filesync/step_upload.py +13 -13
- wandb/filesync/upload_job.py +4 -8
- wandb/integration/cohere/__init__.py +3 -0
- wandb/integration/cohere/cohere.py +21 -0
- wandb/integration/cohere/resolver.py +347 -0
- wandb/integration/gym/__init__.py +4 -6
- wandb/integration/huggingface/__init__.py +3 -0
- wandb/integration/huggingface/huggingface.py +18 -0
- wandb/integration/huggingface/resolver.py +213 -0
- wandb/integration/langchain/wandb_tracer.py +16 -179
- wandb/integration/openai/__init__.py +1 -3
- wandb/integration/openai/openai.py +11 -143
- wandb/integration/openai/resolver.py +111 -38
- wandb/integration/sagemaker/config.py +2 -2
- wandb/integration/tensorboard/log.py +4 -4
- wandb/old/settings.py +24 -7
- wandb/proto/v3/wandb_telemetry_pb2.py +12 -12
- wandb/proto/v4/wandb_telemetry_pb2.py +12 -12
- wandb/proto/wandb_deprecated.py +3 -1
- wandb/sdk/__init__.py +1 -1
- wandb/sdk/artifacts/__init__.py +0 -0
- wandb/sdk/artifacts/artifact.py +2101 -0
- wandb/sdk/artifacts/artifact_download_logger.py +42 -0
- wandb/sdk/artifacts/artifact_manifest.py +67 -0
- wandb/sdk/artifacts/artifact_manifest_entry.py +159 -0
- wandb/sdk/artifacts/artifact_manifests/__init__.py +0 -0
- wandb/sdk/artifacts/artifact_manifests/artifact_manifest_v1.py +91 -0
- wandb/sdk/{internal → artifacts}/artifact_saver.py +6 -5
- wandb/sdk/artifacts/artifact_state.py +10 -0
- wandb/sdk/{interface/artifacts/artifact_cache.py → artifacts/artifacts_cache.py} +22 -12
- wandb/sdk/artifacts/exceptions.py +55 -0
- wandb/sdk/artifacts/storage_handler.py +59 -0
- wandb/sdk/artifacts/storage_handlers/__init__.py +0 -0
- wandb/sdk/artifacts/storage_handlers/azure_handler.py +192 -0
- wandb/sdk/artifacts/storage_handlers/gcs_handler.py +224 -0
- wandb/sdk/artifacts/storage_handlers/http_handler.py +112 -0
- wandb/sdk/artifacts/storage_handlers/local_file_handler.py +134 -0
- wandb/sdk/artifacts/storage_handlers/multi_handler.py +53 -0
- wandb/sdk/artifacts/storage_handlers/s3_handler.py +301 -0
- wandb/sdk/artifacts/storage_handlers/tracking_handler.py +67 -0
- wandb/sdk/artifacts/storage_handlers/wb_artifact_handler.py +132 -0
- wandb/sdk/artifacts/storage_handlers/wb_local_artifact_handler.py +72 -0
- wandb/sdk/artifacts/storage_layout.py +6 -0
- wandb/sdk/artifacts/storage_policies/__init__.py +0 -0
- wandb/sdk/artifacts/storage_policies/s3_bucket_policy.py +61 -0
- wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py +386 -0
- wandb/sdk/{interface/artifacts/artifact_storage.py → artifacts/storage_policy.py} +5 -57
- wandb/sdk/data_types/_dtypes.py +7 -12
- wandb/sdk/data_types/base_types/json_metadata.py +3 -2
- wandb/sdk/data_types/base_types/media.py +8 -8
- wandb/sdk/data_types/base_types/wb_value.py +12 -13
- wandb/sdk/data_types/helper_types/bounding_boxes_2d.py +5 -6
- wandb/sdk/data_types/helper_types/classes.py +6 -8
- wandb/sdk/data_types/helper_types/image_mask.py +5 -6
- wandb/sdk/data_types/histogram.py +4 -3
- wandb/sdk/data_types/html.py +3 -4
- wandb/sdk/data_types/image.py +11 -9
- wandb/sdk/data_types/molecule.py +5 -3
- wandb/sdk/data_types/object_3d.py +7 -5
- wandb/sdk/data_types/plotly.py +3 -2
- wandb/sdk/data_types/saved_model.py +11 -11
- wandb/sdk/data_types/trace_tree.py +5 -4
- wandb/sdk/data_types/utils.py +3 -5
- wandb/sdk/data_types/video.py +5 -4
- wandb/sdk/integration_utils/auto_logging.py +215 -0
- wandb/sdk/interface/interface.py +15 -15
- wandb/sdk/internal/file_pusher.py +8 -16
- wandb/sdk/internal/file_stream.py +5 -11
- wandb/sdk/internal/handler.py +13 -1
- wandb/sdk/internal/internal_api.py +287 -13
- wandb/sdk/internal/job_builder.py +119 -30
- wandb/sdk/internal/sender.py +6 -26
- wandb/sdk/internal/settings_static.py +2 -0
- wandb/sdk/internal/system/assets/__init__.py +2 -0
- wandb/sdk/internal/system/assets/gpu.py +42 -0
- wandb/sdk/internal/system/assets/gpu_amd.py +216 -0
- wandb/sdk/internal/system/env_probe_helpers.py +13 -0
- wandb/sdk/internal/system/system_info.py +3 -3
- wandb/sdk/internal/tb_watcher.py +32 -22
- wandb/sdk/internal/thread_local_settings.py +18 -0
- wandb/sdk/launch/_project_spec.py +57 -11
- wandb/sdk/launch/agent/agent.py +147 -65
- wandb/sdk/launch/agent/job_status_tracker.py +34 -0
- wandb/sdk/launch/agent/run_queue_item_file_saver.py +45 -0
- wandb/sdk/launch/builder/abstract.py +5 -1
- wandb/sdk/launch/builder/build.py +21 -18
- wandb/sdk/launch/builder/docker_builder.py +10 -4
- wandb/sdk/launch/builder/kaniko_builder.py +113 -23
- wandb/sdk/launch/builder/noop.py +6 -3
- wandb/sdk/launch/builder/templates/_wandb_bootstrap.py +46 -14
- wandb/sdk/launch/environment/aws_environment.py +3 -2
- wandb/sdk/launch/environment/azure_environment.py +124 -0
- wandb/sdk/launch/environment/gcp_environment.py +2 -4
- wandb/sdk/launch/environment/local_environment.py +1 -1
- wandb/sdk/launch/errors.py +19 -0
- wandb/sdk/launch/github_reference.py +32 -19
- wandb/sdk/launch/launch.py +3 -8
- wandb/sdk/launch/launch_add.py +6 -2
- wandb/sdk/launch/loader.py +21 -2
- wandb/sdk/launch/registry/azure_container_registry.py +132 -0
- wandb/sdk/launch/registry/elastic_container_registry.py +39 -5
- wandb/sdk/launch/registry/google_artifact_registry.py +68 -26
- wandb/sdk/launch/registry/local_registry.py +2 -1
- wandb/sdk/launch/runner/abstract.py +24 -3
- wandb/sdk/launch/runner/kubernetes_runner.py +479 -26
- wandb/sdk/launch/runner/local_container.py +103 -51
- wandb/sdk/launch/runner/local_process.py +1 -1
- wandb/sdk/launch/runner/sagemaker_runner.py +60 -10
- wandb/sdk/launch/runner/vertex_runner.py +10 -5
- wandb/sdk/launch/sweeps/__init__.py +7 -9
- wandb/sdk/launch/sweeps/scheduler.py +307 -77
- wandb/sdk/launch/sweeps/scheduler_sweep.py +2 -1
- wandb/sdk/launch/sweeps/utils.py +82 -35
- wandb/sdk/launch/utils.py +89 -75
- wandb/sdk/lib/_settings_toposort_generated.py +7 -0
- wandb/sdk/lib/capped_dict.py +26 -0
- wandb/sdk/lib/{git.py → gitlib.py} +76 -59
- wandb/sdk/lib/hashutil.py +12 -4
- wandb/sdk/lib/paths.py +96 -8
- wandb/sdk/lib/sock_client.py +2 -2
- wandb/sdk/lib/timer.py +1 -0
- wandb/sdk/service/server.py +22 -9
- wandb/sdk/service/server_sock.py +1 -1
- wandb/sdk/service/service.py +27 -8
- wandb/sdk/verify/verify.py +4 -7
- wandb/sdk/wandb_config.py +2 -6
- wandb/sdk/wandb_init.py +57 -53
- wandb/sdk/wandb_require.py +7 -0
- wandb/sdk/wandb_run.py +61 -223
- wandb/sdk/wandb_settings.py +28 -4
- wandb/testing/relay.py +15 -2
- wandb/util.py +74 -36
- {wandb-0.15.3.dist-info → wandb-0.15.5.dist-info}/METADATA +15 -9
- {wandb-0.15.3.dist-info → wandb-0.15.5.dist-info}/RECORD +151 -116
- {wandb-0.15.3.dist-info → wandb-0.15.5.dist-info}/entry_points.txt +1 -0
- wandb/integration/langchain/util.py +0 -191
- wandb/sdk/interface/artifacts/__init__.py +0 -33
- wandb/sdk/interface/artifacts/artifact.py +0 -615
- wandb/sdk/interface/artifacts/artifact_manifest.py +0 -131
- wandb/sdk/wandb_artifacts.py +0 -2226
- {wandb-0.15.3.dist-info → wandb-0.15.5.dist-info}/LICENSE +0 -0
- {wandb-0.15.3.dist-info → wandb-0.15.5.dist-info}/WHEEL +0 -0
- {wandb-0.15.3.dist-info → wandb-0.15.5.dist-info}/top_level.txt +0 -0
wandb/sdk/launch/sweeps/utils.py
CHANGED
@@ -7,7 +7,8 @@ import yaml
|
|
7
7
|
|
8
8
|
import wandb
|
9
9
|
from wandb import util
|
10
|
-
from wandb.
|
10
|
+
from wandb.apis.public import Api as PublicApi
|
11
|
+
from wandb.sdk.launch.errors import LaunchError
|
11
12
|
|
12
13
|
DEFAULT_SWEEP_COMMAND: List[str] = [
|
13
14
|
"${env}",
|
@@ -118,16 +119,18 @@ def load_launch_sweep_config(config: Optional[str]) -> Any:
|
|
118
119
|
return parsed_config
|
119
120
|
|
120
121
|
|
121
|
-
def
|
122
|
+
def construct_scheduler_args(
|
122
123
|
sweep_config: Dict[str, Any],
|
123
124
|
queue: str,
|
124
125
|
project: str,
|
125
|
-
num_workers: Union[str, int],
|
126
126
|
author: Optional[str] = None,
|
127
|
-
|
128
|
-
|
127
|
+
sweep_type: Optional[str] = "wandb",
|
128
|
+
return_job: bool = False,
|
129
|
+
) -> Union[List[str], Dict[str, str], None]:
|
130
|
+
"""Construct sweep scheduler args.
|
129
131
|
|
130
|
-
logs error and returns None if misconfigured,
|
132
|
+
logs error and returns None if misconfigured,
|
133
|
+
otherwise returns args as a dict if is_job else a list of strings.
|
131
134
|
"""
|
132
135
|
job = sweep_config.get("job")
|
133
136
|
image_uri = sweep_config.get("image_uri")
|
@@ -135,47 +138,53 @@ def construct_scheduler_entrypoint(
|
|
135
138
|
wandb.termerror(
|
136
139
|
"No 'job' nor 'image_uri' top-level key found in sweep config, exactly one is required for a launch-sweep"
|
137
140
|
)
|
138
|
-
return
|
141
|
+
return None
|
139
142
|
elif job and image_uri:
|
140
143
|
wandb.termerror(
|
141
144
|
"Sweep config has both 'job' and 'image_uri' but a launch-sweep can use only one"
|
142
145
|
)
|
143
|
-
return
|
146
|
+
return None
|
144
147
|
|
145
|
-
if
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
148
|
+
# if scheduler is a job, return args as dict
|
149
|
+
if return_job:
|
150
|
+
args_dict: Dict[str, str] = {
|
151
|
+
"sweep_id": "WANDB_SWEEP_ID",
|
152
|
+
"queue": queue,
|
153
|
+
"project": project,
|
154
|
+
}
|
155
|
+
if job:
|
156
|
+
args_dict["job"] = job
|
157
|
+
elif image_uri:
|
158
|
+
args_dict["image_uri"] = image_uri
|
159
|
+
|
160
|
+
if author:
|
161
|
+
args_dict["author"] = author
|
162
|
+
|
163
|
+
return args_dict
|
164
|
+
|
165
|
+
# scheduler uses cli commands, pass args as param list
|
166
|
+
args = [
|
158
167
|
"--queue",
|
159
168
|
f"{queue!r}",
|
160
169
|
"--project",
|
161
|
-
project,
|
162
|
-
"--
|
163
|
-
f"{
|
170
|
+
f"{project!r}",
|
171
|
+
"--sweep_type",
|
172
|
+
f"{sweep_type}",
|
164
173
|
]
|
165
|
-
|
174
|
+
if author:
|
175
|
+
args += [
|
176
|
+
"--author",
|
177
|
+
f"{author!r}",
|
178
|
+
]
|
166
179
|
if job:
|
167
|
-
|
168
|
-
|
169
|
-
job
|
170
|
-
|
171
|
-
entrypoint += ["--job", job]
|
180
|
+
args += [
|
181
|
+
"--job",
|
182
|
+
f"{job!r}",
|
183
|
+
]
|
172
184
|
elif image_uri:
|
173
|
-
|
185
|
+
args += ["--image_uri", image_uri]
|
174
186
|
|
175
|
-
|
176
|
-
entrypoint += ["--author", author]
|
177
|
-
|
178
|
-
return entrypoint
|
187
|
+
return args
|
179
188
|
|
180
189
|
|
181
190
|
def create_sweep_command(command: Optional[List] = None) -> List:
|
@@ -265,3 +274,41 @@ def make_launch_sweep_entrypoint(
|
|
265
274
|
return None, macro_args
|
266
275
|
|
267
276
|
return entry_point, macro_args
|
277
|
+
|
278
|
+
|
279
|
+
def check_job_exists(public_api: PublicApi, job: Optional[str]) -> bool:
|
280
|
+
"""Check if the job exists using the public api.
|
281
|
+
|
282
|
+
Returns: True if no job is passed, or if the job exists.
|
283
|
+
Returns: False if the job is misformatted or doesn't exist.
|
284
|
+
"""
|
285
|
+
if not job:
|
286
|
+
return True
|
287
|
+
|
288
|
+
try:
|
289
|
+
public_api.job(job)
|
290
|
+
except Exception as e:
|
291
|
+
wandb.termerror(f"Failed to load job. {e}")
|
292
|
+
return False
|
293
|
+
return True
|
294
|
+
|
295
|
+
|
296
|
+
def get_previous_args(
|
297
|
+
run_spec: Dict[str, Any]
|
298
|
+
) -> Tuple[Dict[str, Any], Dict[str, Any]]:
|
299
|
+
"""Parse through previous scheduler run_spec.
|
300
|
+
|
301
|
+
returns scheduler_args and settings.
|
302
|
+
"""
|
303
|
+
scheduler_args = (
|
304
|
+
run_spec.get("overrides", {}).get("run_config", {}).get("scheduler", {})
|
305
|
+
)
|
306
|
+
# also pipe through top level resource setup
|
307
|
+
if run_spec.get("resource"):
|
308
|
+
scheduler_args["resource"] = run_spec["resource"]
|
309
|
+
if run_spec.get("resource_args"):
|
310
|
+
scheduler_args["resource_args"] = run_spec["resource_args"]
|
311
|
+
|
312
|
+
settings = run_spec.get("overrides", {}).get("run_config", {}).get("settings", {})
|
313
|
+
|
314
|
+
return scheduler_args, settings
|
wandb/sdk/launch/utils.py
CHANGED
@@ -13,7 +13,9 @@ import wandb
|
|
13
13
|
import wandb.docker as docker
|
14
14
|
from wandb import util
|
15
15
|
from wandb.apis.internal import Api
|
16
|
-
from wandb.errors import CommError
|
16
|
+
from wandb.errors import CommError
|
17
|
+
from wandb.sdk.launch.errors import LaunchError
|
18
|
+
from wandb.sdk.launch.github_reference import GitHubReference
|
17
19
|
from wandb.sdk.launch.wandb_reference import WandbReference
|
18
20
|
|
19
21
|
from .builder.templates._wandb_bootstrap import (
|
@@ -26,31 +28,8 @@ FAILED_PACKAGES_REGEX = re.compile(
|
|
26
28
|
)
|
27
29
|
|
28
30
|
if TYPE_CHECKING: # pragma: no cover
|
29
|
-
from wandb.
|
30
|
-
|
31
|
-
|
32
|
-
class LaunchError(Error):
|
33
|
-
"""Raised when a known error occurs in wandb launch."""
|
34
|
-
|
35
|
-
pass
|
36
|
-
|
37
|
-
|
38
|
-
class LaunchDockerError(Error):
|
39
|
-
"""Raised when Docker daemon is not running."""
|
40
|
-
|
41
|
-
pass
|
42
|
-
|
43
|
-
|
44
|
-
class ExecutionError(Error):
|
45
|
-
"""Generic execution exception."""
|
46
|
-
|
47
|
-
pass
|
48
|
-
|
49
|
-
|
50
|
-
class SweepError(Error):
|
51
|
-
"""Raised when a known error occurs with wandb sweeps."""
|
52
|
-
|
53
|
-
pass
|
31
|
+
from wandb.sdk.artifacts.artifact import Artifact
|
32
|
+
from wandb.sdk.launch.agent.job_status_tracker import JobAndRunStatusTracker
|
54
33
|
|
55
34
|
|
56
35
|
# TODO: this should be restricted to just Git repos and not S3 and stuff like that
|
@@ -69,7 +48,9 @@ _WANDB_LOCAL_DEV_URI_REGEX = re.compile(
|
|
69
48
|
r"^https?://localhost"
|
70
49
|
) # for testing, not sure if we wanna keep this
|
71
50
|
|
72
|
-
API_KEY_REGEX = r"WANDB_API_KEY=\w+"
|
51
|
+
API_KEY_REGEX = r"WANDB_API_KEY=\w+(-\w+)?"
|
52
|
+
|
53
|
+
MACRO_REGEX = re.compile(r"\$\{(\w+)\}")
|
73
54
|
|
74
55
|
PROJECT_SYNCHRONOUS = "SYNCHRONOUS"
|
75
56
|
|
@@ -77,7 +58,6 @@ UNCATEGORIZED_PROJECT = "uncategorized"
|
|
77
58
|
LAUNCH_CONFIG_FILE = "~/.config/wandb/launch-config.yaml"
|
78
59
|
LAUNCH_DEFAULT_PROJECT = "model-registry"
|
79
60
|
|
80
|
-
|
81
61
|
_logger = logging.getLogger(__name__)
|
82
62
|
LOG_PREFIX = f"{click.style('launch:', fg='magenta')} "
|
83
63
|
|
@@ -164,6 +144,7 @@ def construct_launch_spec(
|
|
164
144
|
run_id: Optional[str],
|
165
145
|
repository: Optional[str],
|
166
146
|
author: Optional[str],
|
147
|
+
sweep_id: Optional[str] = None,
|
167
148
|
) -> Dict[str, Any]:
|
168
149
|
"""Construct the launch specification from CLI arguments."""
|
169
150
|
# override base config (if supplied) with supplied args
|
@@ -191,6 +172,8 @@ def construct_launch_spec(
|
|
191
172
|
launch_spec["docker"] = {}
|
192
173
|
if docker_image:
|
193
174
|
launch_spec["docker"]["docker_image"] = docker_image
|
175
|
+
if sweep_id: # all runs in a sweep have this set
|
176
|
+
launch_spec["sweep_id"] = sweep_id
|
194
177
|
|
195
178
|
if "resource" not in launch_spec:
|
196
179
|
launch_spec["resource"] = resource if resource else None
|
@@ -461,49 +444,16 @@ def _fetch_git_repo(dst_dir: str, uri: str, version: Optional[str]) -> str:
|
|
461
444
|
"""
|
462
445
|
# We defer importing git until the last moment, because the import requires that the git
|
463
446
|
# executable is available on the PATH, so we only want to fail if we actually need it.
|
464
|
-
import git # type: ignore
|
465
447
|
|
466
448
|
_logger.info("Fetching git repo")
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
except git.exc.GitCommandError as e:
|
476
|
-
raise LaunchError(
|
477
|
-
f"Unable to checkout version '{version}' of git repo {uri}"
|
478
|
-
"- please ensure that the version exists in the repo. "
|
479
|
-
f"Error: {e}"
|
480
|
-
) from e
|
481
|
-
else:
|
482
|
-
if getattr(repo, "references", None) is not None:
|
483
|
-
branches = [ref.name for ref in repo.references]
|
484
|
-
else:
|
485
|
-
branches = []
|
486
|
-
# Check if main is in origin, else set branch to master
|
487
|
-
if "main" in branches or "origin/main" in branches:
|
488
|
-
version = "main"
|
489
|
-
else:
|
490
|
-
version = "master"
|
491
|
-
|
492
|
-
try:
|
493
|
-
repo.create_head(version, origin.refs[version])
|
494
|
-
repo.heads[version].checkout()
|
495
|
-
wandb.termlog(
|
496
|
-
f"{LOG_PREFIX}No git branch passed, defaulted to branch: {version}"
|
497
|
-
)
|
498
|
-
except (AttributeError, IndexError) as e:
|
499
|
-
raise LaunchError(
|
500
|
-
f"Unable to checkout default version '{version}' of git repo {uri} "
|
501
|
-
"- to specify a git version use: --git-version \n"
|
502
|
-
f"Error: {e}"
|
503
|
-
) from e
|
504
|
-
|
505
|
-
repo.submodule_update(init=True, recursive=True)
|
506
|
-
return version
|
449
|
+
ref = GitHubReference.parse(uri)
|
450
|
+
if ref is None:
|
451
|
+
raise LaunchError(f"Unable to parse git uri: {uri}")
|
452
|
+
if version:
|
453
|
+
ref.update_ref(version)
|
454
|
+
ref.fetch(dst_dir)
|
455
|
+
assert version is not None or ref.ref is not None or ref.default_branch is not None
|
456
|
+
return version or ref.ref or ref.default_branch # type: ignore
|
507
457
|
|
508
458
|
|
509
459
|
def merge_parameters(
|
@@ -522,9 +472,17 @@ def convert_jupyter_notebook_to_script(fname: str, project_dir: str) -> str:
|
|
522
472
|
)
|
523
473
|
|
524
474
|
_logger.info("Converting notebook to script")
|
525
|
-
new_name = fname.
|
475
|
+
new_name = fname.replace(".ipynb", ".py")
|
526
476
|
with open(os.path.join(project_dir, fname)) as fh:
|
527
477
|
nb = nbformat.reads(fh.read(), nbformat.NO_CONVERT)
|
478
|
+
for cell in nb.cells:
|
479
|
+
if cell.cell_type == "code":
|
480
|
+
source_lines = cell.source.split("\n")
|
481
|
+
modified_lines = []
|
482
|
+
for line in source_lines:
|
483
|
+
if not line.startswith("!"):
|
484
|
+
modified_lines.append(line)
|
485
|
+
cell.source = "\n".join(modified_lines)
|
528
486
|
|
529
487
|
exporter = nbconvert.PythonExporter()
|
530
488
|
source, meta = exporter.from_notebook_node(nb)
|
@@ -536,7 +494,7 @@ def convert_jupyter_notebook_to_script(fname: str, project_dir: str) -> str:
|
|
536
494
|
|
537
495
|
def check_and_download_code_artifacts(
|
538
496
|
entity: str, project: str, run_name: str, internal_api: Api, project_dir: str
|
539
|
-
) -> Optional["
|
497
|
+
) -> Optional["Artifact"]:
|
540
498
|
_logger.info("Checking for code artifacts")
|
541
499
|
public_api = wandb.PublicApi(
|
542
500
|
overrides={"base_url": internal_api.settings("base_url")}
|
@@ -663,12 +621,23 @@ def make_name_dns_safe(name: str) -> str:
|
|
663
621
|
return resp
|
664
622
|
|
665
623
|
|
666
|
-
def warn_failed_packages_from_build_logs(
|
624
|
+
def warn_failed_packages_from_build_logs(
|
625
|
+
log: str, image_uri: str, api: Api, job_tracker: Optional["JobAndRunStatusTracker"]
|
626
|
+
) -> None:
|
667
627
|
match = FAILED_PACKAGES_REGEX.search(log)
|
668
628
|
if match:
|
669
|
-
|
670
|
-
|
671
|
-
|
629
|
+
_msg = f"Failed to install the following packages: {match.group(1)} for image: {image_uri}. Will attempt to launch image without them."
|
630
|
+
wandb.termwarn(_msg)
|
631
|
+
if job_tracker is not None:
|
632
|
+
res = job_tracker.saver.save_contents(
|
633
|
+
_msg, "failed-packages.log", "warning"
|
634
|
+
)
|
635
|
+
api.update_run_queue_item_warning(
|
636
|
+
job_tracker.run_queue_item_id,
|
637
|
+
"Some packages were not successfully installed during the build",
|
638
|
+
"build",
|
639
|
+
res,
|
640
|
+
)
|
672
641
|
|
673
642
|
|
674
643
|
def docker_image_exists(docker_image: str, should_raise: bool = False) -> bool:
|
@@ -696,3 +665,48 @@ def pull_docker_image(docker_image: str) -> None:
|
|
696
665
|
docker.run(["docker", "pull", docker_image])
|
697
666
|
except docker.DockerError as e:
|
698
667
|
raise LaunchError(f"Docker server returned error: {e}")
|
668
|
+
|
669
|
+
|
670
|
+
def macro_sub(original: str, sub_dict: Dict[str, Optional[str]]) -> str:
|
671
|
+
"""Substitute macros in a string.
|
672
|
+
|
673
|
+
Macros occur in the string in the ${macro} format. The macro names are
|
674
|
+
substituted with their values from the given dictionary. If a macro
|
675
|
+
is not found in the dictionary, it is left unchanged.
|
676
|
+
|
677
|
+
Args:
|
678
|
+
original: The string to substitute macros in.
|
679
|
+
sub_dict: A dictionary mapping macro names to their values.
|
680
|
+
|
681
|
+
Returns:
|
682
|
+
The string with the macros substituted.
|
683
|
+
"""
|
684
|
+
return MACRO_REGEX.sub(
|
685
|
+
lambda match: str(sub_dict.get(match.group(1), match.group(0))), original
|
686
|
+
)
|
687
|
+
|
688
|
+
|
689
|
+
def recursive_macro_sub(source: Any, sub_dict: Dict[str, Optional[str]]) -> Any:
|
690
|
+
"""Recursively substitute macros in a parsed JSON or YAML blob.
|
691
|
+
|
692
|
+
Macros occur in strings at leaves of the blob in the ${macro} format.
|
693
|
+
The macro names are substituted with their values from the given dictionary.
|
694
|
+
If a macro is not found in the dictionary, it is left unchanged.
|
695
|
+
|
696
|
+
Arguments:
|
697
|
+
source: The JSON or YAML blob to substitute macros in.
|
698
|
+
sub_dict: A dictionary mapping macro names to their values.
|
699
|
+
|
700
|
+
Returns:
|
701
|
+
The blob with the macros substituted.
|
702
|
+
"""
|
703
|
+
if isinstance(source, str):
|
704
|
+
return macro_sub(source, sub_dict)
|
705
|
+
elif isinstance(source, list):
|
706
|
+
return [recursive_macro_sub(item, sub_dict) for item in source]
|
707
|
+
elif isinstance(source, dict):
|
708
|
+
return {
|
709
|
+
key: recursive_macro_sub(value, sub_dict) for key, value in source.items()
|
710
|
+
}
|
711
|
+
else:
|
712
|
+
return source
|
@@ -12,6 +12,7 @@ else:
|
|
12
12
|
|
13
13
|
_Setting = Literal[
|
14
14
|
"_args",
|
15
|
+
"_aws_lambda",
|
15
16
|
"_async_upload_concurrency_limit",
|
16
17
|
"_cli_only_mode",
|
17
18
|
"_colab",
|
@@ -22,6 +23,7 @@ _Setting = Literal[
|
|
22
23
|
"_disable_service",
|
23
24
|
"_disable_stats",
|
24
25
|
"_disable_viewer",
|
26
|
+
"_disable_setproctitle",
|
25
27
|
"_except_exit",
|
26
28
|
"_executable",
|
27
29
|
"_extra_http_headers",
|
@@ -47,6 +49,7 @@ _Setting = Literal[
|
|
47
49
|
"_platform",
|
48
50
|
"_python",
|
49
51
|
"_runqueue_item_id",
|
52
|
+
"_require_nexus",
|
50
53
|
"_save_requirements",
|
51
54
|
"_service_transport",
|
52
55
|
"_service_wait",
|
@@ -66,6 +69,7 @@ _Setting = Literal[
|
|
66
69
|
"allow_val_change",
|
67
70
|
"anonymous",
|
68
71
|
"api_key",
|
72
|
+
"azure_account_url_to_access_key",
|
69
73
|
"base_url",
|
70
74
|
"code_dir",
|
71
75
|
"config_paths",
|
@@ -90,6 +94,7 @@ _Setting = Literal[
|
|
90
94
|
"ignore_globs",
|
91
95
|
"init_timeout",
|
92
96
|
"is_local",
|
97
|
+
"job_source",
|
93
98
|
"label_disable",
|
94
99
|
"launch",
|
95
100
|
"launch_config_path",
|
@@ -162,11 +167,13 @@ SETTINGS_TOPOLOGICALLY_SORTED: Final[Tuple[_Setting, ...]] = (
|
|
162
167
|
"api_key",
|
163
168
|
"base_url",
|
164
169
|
"console",
|
170
|
+
"job_source",
|
165
171
|
"mode",
|
166
172
|
"problem",
|
167
173
|
"project",
|
168
174
|
"run_id",
|
169
175
|
"start_method",
|
176
|
+
"_aws_lambda",
|
170
177
|
"_colab",
|
171
178
|
"_console",
|
172
179
|
"_ipython",
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import collections
|
2
|
+
from typing import Any, Optional
|
3
|
+
|
4
|
+
|
5
|
+
class CappedDict(collections.OrderedDict):
|
6
|
+
default_max_size = 50
|
7
|
+
|
8
|
+
def __init__(self, max_size: Optional[int] = None) -> None:
|
9
|
+
self.max_size = max_size or self.default_max_size
|
10
|
+
super().__init__()
|
11
|
+
|
12
|
+
def __setitem__(self, key: str, val: Any) -> None:
|
13
|
+
if key not in self:
|
14
|
+
max_size = self.max_size - 1
|
15
|
+
self._prune_dict(max_size)
|
16
|
+
super().__setitem__(key, val)
|
17
|
+
|
18
|
+
def update(self, **kwargs: Any) -> None: # type: ignore[override]
|
19
|
+
super().update(**kwargs)
|
20
|
+
self._prune_dict(self.max_size)
|
21
|
+
|
22
|
+
def _prune_dict(self, max_size: int) -> None:
|
23
|
+
if len(self) >= max_size:
|
24
|
+
diff = len(self) - max_size
|
25
|
+
for k in list(self.keys())[:diff]:
|
26
|
+
del self[k]
|