wandb 0.17.3__py3-none-win_amd64.whl → 0.17.4__py3-none-win_amd64.whl
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.
- wandb/__init__.py +1 -1
- wandb/apis/internal.py +4 -0
- wandb/bin/wandb-core +0 -0
- wandb/cli/cli.py +7 -6
- wandb/env.py +16 -0
- wandb/proto/v3/wandb_settings_pb2.py +2 -2
- wandb/proto/v4/wandb_settings_pb2.py +2 -2
- wandb/proto/v5/wandb_settings_pb2.py +2 -2
- wandb/sdk/artifacts/artifact.py +2 -0
- wandb/sdk/artifacts/artifact_file_cache.py +35 -13
- wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py +11 -6
- wandb/sdk/internal/internal_api.py +65 -12
- wandb/sdk/launch/agent/agent.py +3 -1
- wandb/sdk/launch/sweeps/scheduler.py +2 -0
- wandb/sdk/lib/_settings_toposort_generated.py +3 -0
- wandb/sdk/lib/credentials.py +141 -0
- wandb/sdk/wandb_init.py +12 -2
- wandb/sdk/wandb_login.py +6 -0
- wandb/sdk/wandb_manager.py +25 -16
- wandb/sdk/wandb_settings.py +13 -2
- wandb/sdk/wandb_setup.py +12 -13
- {wandb-0.17.3.dist-info → wandb-0.17.4.dist-info}/METADATA +1 -1
- {wandb-0.17.3.dist-info → wandb-0.17.4.dist-info}/RECORD +26 -25
- {wandb-0.17.3.dist-info → wandb-0.17.4.dist-info}/WHEEL +0 -0
- {wandb-0.17.3.dist-info → wandb-0.17.4.dist-info}/entry_points.txt +0 -0
- {wandb-0.17.3.dist-info → wandb-0.17.4.dist-info}/licenses/LICENSE +0 -0
@@ -11,6 +11,7 @@ import socket
|
|
11
11
|
import sys
|
12
12
|
import threading
|
13
13
|
from copy import deepcopy
|
14
|
+
from pathlib import Path
|
14
15
|
from typing import (
|
15
16
|
IO,
|
16
17
|
TYPE_CHECKING,
|
@@ -37,14 +38,14 @@ from wandb_gql.client import RetryError
|
|
37
38
|
import wandb
|
38
39
|
from wandb import env, util
|
39
40
|
from wandb.apis.normalize import normalize_exceptions, parse_backend_error_messages
|
40
|
-
from wandb.errors import CommError, UnsupportedError, UsageError
|
41
|
+
from wandb.errors import AuthenticationError, CommError, UnsupportedError, UsageError
|
41
42
|
from wandb.integration.sagemaker import parse_sm_secrets
|
42
43
|
from wandb.old.settings import Settings
|
43
44
|
from wandb.sdk.internal.thread_local_settings import _thread_local_api_settings
|
44
45
|
from wandb.sdk.lib.gql_request import GraphQLSession
|
45
46
|
from wandb.sdk.lib.hashutil import B64MD5, md5_file_b64
|
46
47
|
|
47
|
-
from ..lib import retry
|
48
|
+
from ..lib import credentials, retry
|
48
49
|
from ..lib.filenames import DIFF_FNAME, METADATA_FNAME
|
49
50
|
from ..lib.gitlib import GitRepo
|
50
51
|
from . import context
|
@@ -234,14 +235,18 @@ class Api:
|
|
234
235
|
extra_http_headers = self.settings("_extra_http_headers") or json.loads(
|
235
236
|
self._environ.get("WANDB__EXTRA_HTTP_HEADERS", "{}")
|
236
237
|
)
|
238
|
+
extra_http_headers.update(_thread_local_api_settings.headers or {})
|
239
|
+
|
240
|
+
auth = None
|
241
|
+
if self.access_token is not None:
|
242
|
+
extra_http_headers["Authorization"] = f"Bearer {self.access_token}"
|
243
|
+
elif _thread_local_api_settings.cookies is None:
|
244
|
+
auth = ("api", self.api_key or "")
|
245
|
+
|
237
246
|
proxies = self.settings("_proxies") or json.loads(
|
238
247
|
self._environ.get("WANDB__PROXIES", "{}")
|
239
248
|
)
|
240
249
|
|
241
|
-
auth = None
|
242
|
-
if _thread_local_api_settings.cookies is None:
|
243
|
-
auth = ("api", self.api_key or "")
|
244
|
-
extra_http_headers.update(_thread_local_api_settings.headers or {})
|
245
250
|
self.client = Client(
|
246
251
|
transport=GraphQLSession(
|
247
252
|
headers={
|
@@ -376,6 +381,35 @@ class Api:
|
|
376
381
|
default_key: Optional[str] = self.default_settings.get("api_key")
|
377
382
|
return env_key or key or sagemaker_key or default_key
|
378
383
|
|
384
|
+
@property
|
385
|
+
def access_token(self) -> Optional[str]:
|
386
|
+
"""Retrieves an access token for authentication.
|
387
|
+
|
388
|
+
This function attempts to exchange an identity token for a temporary
|
389
|
+
access token from the server, and save it to the credentials file.
|
390
|
+
It uses the path to the identity token as defined in the environment
|
391
|
+
variables. If the environment variable is not set, it returns None.
|
392
|
+
|
393
|
+
Returns:
|
394
|
+
Optional[str]: The access token if available, otherwise None if
|
395
|
+
no identity token is supplied.
|
396
|
+
Raises:
|
397
|
+
AuthenticationError: If the path to the identity token is not found.
|
398
|
+
"""
|
399
|
+
token_file_str = self._environ.get(env.IDENTITY_TOKEN_FILE)
|
400
|
+
if not token_file_str:
|
401
|
+
return None
|
402
|
+
|
403
|
+
token_file = Path(token_file_str)
|
404
|
+
if not token_file.exists():
|
405
|
+
raise AuthenticationError(f"Identity token file not found: {token_file}")
|
406
|
+
|
407
|
+
base_url = self.settings("base_url")
|
408
|
+
credentials_file = env.get_credentials_file(
|
409
|
+
str(credentials.DEFAULT_WANDB_CREDENTIALS_FILE), self._environ
|
410
|
+
)
|
411
|
+
return credentials.access_token(base_url, token_file, credentials_file)
|
412
|
+
|
379
413
|
@property
|
380
414
|
def api_url(self) -> str:
|
381
415
|
return self.settings("base_url") # type: ignore
|
@@ -2674,14 +2708,20 @@ class Api:
|
|
2674
2708
|
A tuple of the content length and the streaming response
|
2675
2709
|
"""
|
2676
2710
|
check_httpclient_logger_handler()
|
2711
|
+
|
2712
|
+
http_headers = _thread_local_api_settings.headers or {}
|
2713
|
+
|
2677
2714
|
auth = None
|
2678
|
-
if
|
2679
|
-
|
2715
|
+
if self.access_token is not None:
|
2716
|
+
http_headers["Authorization"] = f"Bearer {self.access_token}"
|
2717
|
+
elif _thread_local_api_settings.cookies is None:
|
2718
|
+
auth = ("api", self.api_key or "")
|
2719
|
+
|
2680
2720
|
response = requests.get(
|
2681
2721
|
url,
|
2682
2722
|
auth=auth,
|
2683
2723
|
cookies=_thread_local_api_settings.cookies or {},
|
2684
|
-
headers=
|
2724
|
+
headers=http_headers,
|
2685
2725
|
stream=True,
|
2686
2726
|
)
|
2687
2727
|
response.raise_for_status()
|
@@ -3039,6 +3079,7 @@ class Api:
|
|
3039
3079
|
entity: Optional[str] = None,
|
3040
3080
|
state: Optional[str] = None,
|
3041
3081
|
prior_runs: Optional[List[str]] = None,
|
3082
|
+
template_variable_values: Optional[Dict[str, Any]] = None,
|
3042
3083
|
) -> Tuple[str, List[str]]:
|
3043
3084
|
"""Upsert a sweep object.
|
3044
3085
|
|
@@ -3052,6 +3093,7 @@ class Api:
|
|
3052
3093
|
entity (str): entity to use
|
3053
3094
|
state (str): state
|
3054
3095
|
prior_runs (list): IDs of existing runs to add to the sweep
|
3096
|
+
template_variable_values (dict): template variable values
|
3055
3097
|
"""
|
3056
3098
|
project_query = """
|
3057
3099
|
project {
|
@@ -3096,7 +3138,17 @@ class Api:
|
|
3096
3138
|
"""
|
3097
3139
|
# TODO(jhr): we need protocol versioning to know schema is not supported
|
3098
3140
|
# for now we will just try both new and old query
|
3099
|
-
|
3141
|
+
mutation_5 = gql(
|
3142
|
+
mutation_str.replace(
|
3143
|
+
"$controller: JSONString,",
|
3144
|
+
"$controller: JSONString,$launchScheduler: JSONString, $templateVariableValues: JSONString,",
|
3145
|
+
)
|
3146
|
+
.replace(
|
3147
|
+
"controller: $controller,",
|
3148
|
+
"controller: $controller,launchScheduler: $launchScheduler,templateVariableValues: $templateVariableValues,",
|
3149
|
+
)
|
3150
|
+
.replace("_PROJECT_QUERY_", project_query)
|
3151
|
+
)
|
3100
3152
|
# launchScheduler was introduced in core v0.14.0
|
3101
3153
|
mutation_4 = gql(
|
3102
3154
|
mutation_str.replace(
|
@@ -3105,7 +3157,7 @@ class Api:
|
|
3105
3157
|
)
|
3106
3158
|
.replace(
|
3107
3159
|
"controller: $controller,",
|
3108
|
-
"controller: $controller,launchScheduler: $launchScheduler
|
3160
|
+
"controller: $controller,launchScheduler: $launchScheduler",
|
3109
3161
|
)
|
3110
3162
|
.replace("_PROJECT_QUERY_", project_query)
|
3111
3163
|
)
|
@@ -3124,7 +3176,7 @@ class Api:
|
|
3124
3176
|
)
|
3125
3177
|
|
3126
3178
|
# TODO(dag): replace this with a query for protocol versioning
|
3127
|
-
mutations = [mutation_4, mutation_3, mutation_2, mutation_1]
|
3179
|
+
mutations = [mutation_5, mutation_4, mutation_3, mutation_2, mutation_1]
|
3128
3180
|
|
3129
3181
|
config = self._validate_config_and_fill_distribution(config)
|
3130
3182
|
|
@@ -3148,6 +3200,7 @@ class Api:
|
|
3148
3200
|
"projectName": project or self.settings("project"),
|
3149
3201
|
"controller": controller,
|
3150
3202
|
"launchScheduler": launch_scheduler,
|
3203
|
+
"templateVariableValues": json.dumps(template_variable_values),
|
3151
3204
|
"scheduler": scheduler,
|
3152
3205
|
"priorRunsFilters": filters,
|
3153
3206
|
}
|
wandb/sdk/launch/agent/agent.py
CHANGED
@@ -308,7 +308,9 @@ class LaunchAgent:
|
|
308
308
|
self._wandb_run = None
|
309
309
|
|
310
310
|
if self.gorilla_supports_agents:
|
311
|
-
settings = wandb.Settings(
|
311
|
+
settings = wandb.Settings(
|
312
|
+
silent=True, disable_git=True, disable_job_creation=True
|
313
|
+
)
|
312
314
|
self._wandb_run = wandb.init(
|
313
315
|
project=self._project,
|
314
316
|
entity=self._entity,
|
@@ -259,10 +259,12 @@ class Scheduler(ABC):
|
|
259
259
|
|
260
260
|
def _init_wandb_run(self) -> "SdkRun":
|
261
261
|
"""Controls resume or init logic for a scheduler wandb run."""
|
262
|
+
settings = wandb.Settings(disable_job_creation=True)
|
262
263
|
run: SdkRun = wandb.init( # type: ignore
|
263
264
|
name=f"Scheduler.{self._sweep_id}",
|
264
265
|
resume="allow",
|
265
266
|
config=self._kwargs, # when run as a job, this sets config
|
267
|
+
settings=settings,
|
266
268
|
)
|
267
269
|
return run
|
268
270
|
|
@@ -26,6 +26,7 @@ _Setting = Literal[
|
|
26
26
|
"_disable_machine_info",
|
27
27
|
"_executable",
|
28
28
|
"_extra_http_headers",
|
29
|
+
"_file_stream_max_bytes",
|
29
30
|
"_file_stream_retry_max",
|
30
31
|
"_file_stream_retry_wait_min_seconds",
|
31
32
|
"_file_stream_retry_wait_max_seconds",
|
@@ -91,6 +92,7 @@ _Setting = Literal[
|
|
91
92
|
"config_paths",
|
92
93
|
"console",
|
93
94
|
"console_multipart",
|
95
|
+
"credentials_file",
|
94
96
|
"deployment",
|
95
97
|
"disable_code",
|
96
98
|
"disable_git",
|
@@ -112,6 +114,7 @@ _Setting = Literal[
|
|
112
114
|
"host",
|
113
115
|
"http_proxy",
|
114
116
|
"https_proxy",
|
117
|
+
"identity_token_file",
|
115
118
|
"ignore_globs",
|
116
119
|
"init_timeout",
|
117
120
|
"is_local",
|
@@ -0,0 +1,141 @@
|
|
1
|
+
import json
|
2
|
+
import os
|
3
|
+
from datetime import datetime, timedelta
|
4
|
+
from pathlib import Path
|
5
|
+
|
6
|
+
import requests.utils
|
7
|
+
|
8
|
+
from wandb.errors import AuthenticationError
|
9
|
+
|
10
|
+
DEFAULT_WANDB_CREDENTIALS_FILE = Path(
|
11
|
+
os.path.expanduser("~/.config/wandb/credentials.json")
|
12
|
+
)
|
13
|
+
|
14
|
+
_expires_at_fmt = "%Y-%m-%d %H:%M:%S"
|
15
|
+
|
16
|
+
|
17
|
+
def access_token(base_url: str, token_file: Path, credentials_file: Path) -> str:
|
18
|
+
"""Retrieve an access token from the credentials file.
|
19
|
+
|
20
|
+
If no access token exists, create a new one by exchanging the identity
|
21
|
+
token from the token file, and save it to the credentials file.
|
22
|
+
|
23
|
+
Args:
|
24
|
+
base_url (str): The base URL of the server
|
25
|
+
token_file (pathlib.Path): The path to the file containing the
|
26
|
+
identity token
|
27
|
+
credentials_file (pathlib.Path): The path to file used to save
|
28
|
+
temporary access tokens
|
29
|
+
|
30
|
+
Returns:
|
31
|
+
str: The access token
|
32
|
+
"""
|
33
|
+
if not credentials_file.exists():
|
34
|
+
_write_credentials_file(base_url, token_file, credentials_file)
|
35
|
+
|
36
|
+
data = _fetch_credentials(base_url, token_file, credentials_file)
|
37
|
+
return data["access_token"]
|
38
|
+
|
39
|
+
|
40
|
+
def _write_credentials_file(base_url: str, token_file: Path, credentials_file: Path):
|
41
|
+
"""Obtain an access token from the server and write it to the credentials file.
|
42
|
+
|
43
|
+
Args:
|
44
|
+
base_url (str): The base URL of the server
|
45
|
+
token_file (pathlib.Path): The path to the file containing the
|
46
|
+
identity token
|
47
|
+
credentials_file (pathlib.Path): The path to file used to save
|
48
|
+
temporary access tokens
|
49
|
+
"""
|
50
|
+
credentials = _create_access_token(base_url, token_file)
|
51
|
+
data = {"credentials": {base_url: credentials}}
|
52
|
+
with open(credentials_file, "w") as file:
|
53
|
+
json.dump(data, file, indent=4)
|
54
|
+
|
55
|
+
# Set file permissions to be read/write by the owner only
|
56
|
+
os.chmod(credentials_file, 0o600)
|
57
|
+
|
58
|
+
|
59
|
+
def _fetch_credentials(base_url: str, token_file: Path, credentials_file: Path) -> dict:
|
60
|
+
"""Fetch the access token from the credentials file.
|
61
|
+
|
62
|
+
If the access token has expired, fetch a new one from the server and save it
|
63
|
+
to the credentials file.
|
64
|
+
|
65
|
+
Args:
|
66
|
+
base_url (str): The base URL of the server
|
67
|
+
token_file (pathlib.Path): The path to the file containing the
|
68
|
+
identity token
|
69
|
+
credentials_file (pathlib.Path): The path to file used to save
|
70
|
+
temporary access tokens
|
71
|
+
|
72
|
+
Returns:
|
73
|
+
dict: The credentials including the access token.
|
74
|
+
"""
|
75
|
+
creds = {}
|
76
|
+
with open(credentials_file) as file:
|
77
|
+
data = json.load(file)
|
78
|
+
if "credentials" not in data:
|
79
|
+
data["credentials"] = {}
|
80
|
+
if base_url in data["credentials"]:
|
81
|
+
creds = data["credentials"][base_url]
|
82
|
+
|
83
|
+
expires_at = datetime.utcnow()
|
84
|
+
if "expires_at" in creds:
|
85
|
+
expires_at = datetime.strptime(creds["expires_at"], _expires_at_fmt)
|
86
|
+
|
87
|
+
if expires_at <= datetime.utcnow():
|
88
|
+
creds = _create_access_token(base_url, token_file)
|
89
|
+
with open(credentials_file, "w") as file:
|
90
|
+
data["credentials"][base_url] = creds
|
91
|
+
json.dump(data, file, indent=4)
|
92
|
+
|
93
|
+
return creds
|
94
|
+
|
95
|
+
|
96
|
+
def _create_access_token(base_url: str, token_file: Path) -> dict:
|
97
|
+
"""Exchange an identity token for an access token from the server.
|
98
|
+
|
99
|
+
Args:
|
100
|
+
base_url (str): The base URL of the server.
|
101
|
+
token_file (pathlib.Path): The path to the file containing the
|
102
|
+
identity token
|
103
|
+
|
104
|
+
Returns:
|
105
|
+
dict: The access token and its expiration.
|
106
|
+
|
107
|
+
Raises:
|
108
|
+
FileNotFoundError: If the token file is not found.
|
109
|
+
OSError: If there is an issue reading the token file.
|
110
|
+
AuthenticationError: If the server fails to provide an access token.
|
111
|
+
"""
|
112
|
+
try:
|
113
|
+
with open(token_file) as file:
|
114
|
+
token = file.read().strip()
|
115
|
+
except FileNotFoundError as e:
|
116
|
+
raise FileNotFoundError(f"Identity token file not found: {token_file}") from e
|
117
|
+
except OSError as e:
|
118
|
+
raise OSError(
|
119
|
+
f"Failed to read the identity token from file: {token_file}"
|
120
|
+
) from e
|
121
|
+
|
122
|
+
url = f"{base_url}/oidc/token"
|
123
|
+
data = {
|
124
|
+
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
125
|
+
"assertion": token,
|
126
|
+
}
|
127
|
+
headers = {"Content-Type": "application/x-www-form-urlencoded"}
|
128
|
+
|
129
|
+
response = requests.post(url, data=data, headers=headers)
|
130
|
+
|
131
|
+
if response.status_code != 200:
|
132
|
+
raise AuthenticationError(
|
133
|
+
f"Failed to retrieve access token: {response.status_code}, {response.text}"
|
134
|
+
)
|
135
|
+
|
136
|
+
resp_json = response.json()
|
137
|
+
expires_at = datetime.utcnow() + timedelta(seconds=float(resp_json["expires_in"]))
|
138
|
+
resp_json["expires_at"] = expires_at.strftime(_expires_at_fmt)
|
139
|
+
del resp_json["expires_in"]
|
140
|
+
|
141
|
+
return resp_json
|
wandb/sdk/wandb_init.py
CHANGED
@@ -323,6 +323,15 @@ class _WandbInit:
|
|
323
323
|
if save_code_pre_user_settings is False:
|
324
324
|
settings.update({"save_code": False}, source=Source.INIT)
|
325
325
|
|
326
|
+
# TODO: remove this once we refactor the client. This is a temporary
|
327
|
+
# fix to make sure that we use the same project name for wandb-core.
|
328
|
+
# The reason this is not going throught the settings object is to
|
329
|
+
# avoid failure cases in other parts of the code that will be
|
330
|
+
# removed with the switch to wandb-core.
|
331
|
+
if settings.project is None:
|
332
|
+
project = wandb.util.auto_project_name(settings.program)
|
333
|
+
settings.update({"project": project}, source=Source.INIT)
|
334
|
+
|
326
335
|
# TODO(jhr): should this be moved? probably.
|
327
336
|
settings._set_run_start_time(source=Source.INIT)
|
328
337
|
|
@@ -989,8 +998,9 @@ def init(
|
|
989
998
|
|
990
999
|
Arguments:
|
991
1000
|
project: (str, optional) The name of the project where you're sending
|
992
|
-
the new run. If the project is not specified,
|
993
|
-
|
1001
|
+
the new run. If the project is not specified, we will try to infer
|
1002
|
+
the project name from git root or the current program file. If we
|
1003
|
+
can't infer the project name, we will default to `"uncategorized"`.
|
994
1004
|
entity: (str, optional) An entity is a username or team name where
|
995
1005
|
you're sending runs. This entity must exist before you can send runs
|
996
1006
|
there, so make sure to create your account or team in the UI before
|
wandb/sdk/wandb_login.py
CHANGED
@@ -156,6 +156,9 @@ class _WandbLogin:
|
|
156
156
|
"""Returns whether an API key is set or can be inferred."""
|
157
157
|
return apikey.api_key(settings=self._settings) is not None
|
158
158
|
|
159
|
+
def should_use_identity_token(self):
|
160
|
+
return self._settings.identity_token_file is not None
|
161
|
+
|
159
162
|
def set_backend(self, backend):
|
160
163
|
self._backend = backend
|
161
164
|
|
@@ -327,6 +330,9 @@ def _login(
|
|
327
330
|
)
|
328
331
|
return False
|
329
332
|
|
333
|
+
if wlogin.should_use_identity_token():
|
334
|
+
return True
|
335
|
+
|
330
336
|
# perform a login
|
331
337
|
logged_in = wlogin.login()
|
332
338
|
|
wandb/sdk/wandb_manager.py
CHANGED
@@ -127,6 +127,7 @@ class _Manager:
|
|
127
127
|
raise ManagerConnectionError(f"Connection to wandb service failed: {e}")
|
128
128
|
|
129
129
|
def __init__(self, settings: "Settings") -> None:
|
130
|
+
"""Connects to the internal service, starting it if necessary."""
|
130
131
|
from wandb.sdk.service import service
|
131
132
|
|
132
133
|
self._settings = settings
|
@@ -134,6 +135,7 @@ class _Manager:
|
|
134
135
|
self._hooks = None
|
135
136
|
|
136
137
|
self._service = service._Service(settings=self._settings)
|
138
|
+
|
137
139
|
token = _ManagerToken.from_environment()
|
138
140
|
if not token:
|
139
141
|
self._service.start()
|
@@ -144,7 +146,6 @@ class _Manager:
|
|
144
146
|
token = _ManagerToken.from_params(transport=transport, host=host, port=port)
|
145
147
|
token.set_environment()
|
146
148
|
self._atexit_setup()
|
147
|
-
|
148
149
|
self._token = token
|
149
150
|
|
150
151
|
try:
|
@@ -152,6 +153,24 @@ class _Manager:
|
|
152
153
|
except ManagerConnectionError as e:
|
153
154
|
wandb._sentry.reraise(e)
|
154
155
|
|
156
|
+
def _teardown(self, exit_code: int) -> int:
|
157
|
+
"""Shuts down the internal process and returns its exit code.
|
158
|
+
|
159
|
+
This sends a teardown record to the process. An exception is raised if
|
160
|
+
the process has already been shut down.
|
161
|
+
"""
|
162
|
+
unregister_all_post_import_hooks()
|
163
|
+
|
164
|
+
if self._atexit_lambda:
|
165
|
+
atexit.unregister(self._atexit_lambda)
|
166
|
+
self._atexit_lambda = None
|
167
|
+
|
168
|
+
try:
|
169
|
+
self._inform_teardown(exit_code)
|
170
|
+
return self._service.join()
|
171
|
+
finally:
|
172
|
+
self._token.reset_environment()
|
173
|
+
|
155
174
|
def _atexit_setup(self) -> None:
|
156
175
|
self._atexit_lambda = lambda: self._atexit_teardown()
|
157
176
|
|
@@ -161,28 +180,18 @@ class _Manager:
|
|
161
180
|
|
162
181
|
def _atexit_teardown(self) -> None:
|
163
182
|
trigger.call("on_finished")
|
164
|
-
exit_code = self._hooks.exit_code if self._hooks else 0
|
165
|
-
self._teardown(exit_code)
|
166
|
-
|
167
|
-
def _teardown(self, exit_code: int) -> None:
|
168
|
-
unregister_all_post_import_hooks()
|
169
183
|
|
170
|
-
|
171
|
-
|
172
|
-
|
184
|
+
# Clear the atexit hook---we're executing it now, after which the
|
185
|
+
# process will exit.
|
186
|
+
self._atexit_lambda = None
|
173
187
|
|
174
188
|
try:
|
175
|
-
self.
|
176
|
-
result = self._service.join()
|
177
|
-
if result and not self._settings._notebook:
|
178
|
-
os._exit(result)
|
189
|
+
self._teardown(self._hooks.exit_code if self._hooks else 0)
|
179
190
|
except Exception as e:
|
180
191
|
wandb.termlog(
|
181
|
-
f"
|
192
|
+
f"Encountered an error while tearing down the service manager: {e}",
|
182
193
|
repeat=False,
|
183
194
|
)
|
184
|
-
finally:
|
185
|
-
self._token.reset_environment()
|
186
195
|
|
187
196
|
def _get_service(self) -> "service._Service":
|
188
197
|
return self._service
|
wandb/sdk/wandb_settings.py
CHANGED
@@ -43,7 +43,7 @@ from wandb.apis.internal import Api
|
|
43
43
|
from wandb.errors import UsageError
|
44
44
|
from wandb.proto import wandb_settings_pb2
|
45
45
|
from wandb.sdk.internal.system.env_probe_helpers import is_aws_lambda
|
46
|
-
from wandb.sdk.lib import filesystem
|
46
|
+
from wandb.sdk.lib import credentials, filesystem
|
47
47
|
from wandb.sdk.lib._settings_toposort_generated import SETTINGS_TOPOLOGICALLY_SORTED
|
48
48
|
from wandb.sdk.lib.run_moment import RunMoment
|
49
49
|
from wandb.sdk.wandb_setup import _EarlyLogger
|
@@ -314,6 +314,7 @@ class SettingsData:
|
|
314
314
|
_disable_machine_info: bool # Disable automatic machine info collection
|
315
315
|
_executable: str
|
316
316
|
_extra_http_headers: Mapping[str, str]
|
317
|
+
_file_stream_max_bytes: int # max size for filestream requests in core
|
317
318
|
# file stream retry client configuration
|
318
319
|
_file_stream_retry_max: int # max number of retries
|
319
320
|
_file_stream_retry_wait_min_seconds: float # min wait time between retries
|
@@ -391,6 +392,7 @@ class SettingsData:
|
|
391
392
|
config_paths: Sequence[str]
|
392
393
|
console: str
|
393
394
|
console_multipart: bool # whether to produce multipart console log files
|
395
|
+
credentials_file: str # file path to write access tokens
|
394
396
|
deployment: str
|
395
397
|
disable_code: bool
|
396
398
|
disable_git: bool
|
@@ -412,6 +414,7 @@ class SettingsData:
|
|
412
414
|
host: str
|
413
415
|
http_proxy: str # proxy server for the http requests to W&B
|
414
416
|
https_proxy: str # proxy server for the https requests to W&B
|
417
|
+
identity_token_file: str # file path to supply a jwt for authentication
|
415
418
|
ignore_globs: Tuple[str]
|
416
419
|
init_timeout: float
|
417
420
|
is_local: bool
|
@@ -659,6 +662,7 @@ class Settings(SettingsData):
|
|
659
662
|
_disable_update_check={"preprocessor": _str_as_bool},
|
660
663
|
_disable_viewer={"preprocessor": _str_as_bool},
|
661
664
|
_extra_http_headers={"preprocessor": _str_as_json},
|
665
|
+
_file_stream_max_bytes={"preprocessor": int},
|
662
666
|
_file_stream_retry_max={"preprocessor": int},
|
663
667
|
_file_stream_retry_wait_min_seconds={"preprocessor": float},
|
664
668
|
_file_stream_retry_wait_max_seconds={"preprocessor": float},
|
@@ -783,6 +787,10 @@ class Settings(SettingsData):
|
|
783
787
|
"auto_hook": True,
|
784
788
|
},
|
785
789
|
console_multipart={"value": False, "preprocessor": _str_as_bool},
|
790
|
+
credentials_file={
|
791
|
+
"value": str(credentials.DEFAULT_WANDB_CREDENTIALS_FILE),
|
792
|
+
"preprocessor": str,
|
793
|
+
},
|
786
794
|
deployment={
|
787
795
|
"hook": lambda _: "local" if self.is_local else "cloud",
|
788
796
|
"auto_hook": True,
|
@@ -829,6 +837,7 @@ class Settings(SettingsData):
|
|
829
837
|
"hook": lambda x: self._proxies and self._proxies.get("https") or x,
|
830
838
|
"auto_hook": True,
|
831
839
|
},
|
840
|
+
identity_token_file={"value": None, "preprocessor": str},
|
832
841
|
ignore_globs={
|
833
842
|
"value": tuple(),
|
834
843
|
"preprocessor": lambda x: tuple(x) if not isinstance(x, tuple) else x,
|
@@ -873,7 +882,9 @@ class Settings(SettingsData):
|
|
873
882
|
program={
|
874
883
|
"hook": lambda x: self._get_program(x),
|
875
884
|
},
|
876
|
-
project={
|
885
|
+
project={
|
886
|
+
"validator": self._validate_project,
|
887
|
+
},
|
877
888
|
project_url={"hook": lambda _: self._project_url(), "auto_hook": True},
|
878
889
|
quiet={"preprocessor": _str_as_bool},
|
879
890
|
reinit={"preprocessor": _str_as_bool},
|
wandb/sdk/wandb_setup.py
CHANGED
@@ -268,20 +268,20 @@ class _WandbSetup__WandbSetup: # noqa: N801
|
|
268
268
|
self._config = config_dict
|
269
269
|
|
270
270
|
def _teardown(self, exit_code: Optional[int] = None) -> None:
|
271
|
-
|
272
|
-
|
271
|
+
if not self._manager:
|
272
|
+
return
|
273
|
+
|
274
|
+
internal_exit_code = self._manager._teardown(exit_code or 0)
|
275
|
+
self._manager = None
|
276
|
+
|
277
|
+
if internal_exit_code != 0:
|
278
|
+
sys.exit(internal_exit_code)
|
273
279
|
|
274
280
|
def _setup_manager(self) -> None:
|
275
281
|
if self._settings._disable_service:
|
276
282
|
return
|
277
283
|
self._manager = wandb_manager._Manager(settings=self._settings)
|
278
284
|
|
279
|
-
def _teardown_manager(self, exit_code: int) -> None:
|
280
|
-
if not self._manager:
|
281
|
-
return
|
282
|
-
self._manager._teardown(exit_code)
|
283
|
-
self._manager = None
|
284
|
-
|
285
285
|
def _get_manager(self) -> Optional[wandb_manager._Manager]:
|
286
286
|
return self._manager
|
287
287
|
|
@@ -312,11 +312,9 @@ def _setup(
|
|
312
312
|
) -> Optional["_WandbSetup"]:
|
313
313
|
"""Set up library context."""
|
314
314
|
if _reset:
|
315
|
-
|
316
|
-
if setup_instance:
|
317
|
-
setup_instance._teardown()
|
318
|
-
_WandbSetup._instance = None
|
315
|
+
teardown()
|
319
316
|
return None
|
317
|
+
|
320
318
|
wl = _WandbSetup(settings=settings)
|
321
319
|
return wl
|
322
320
|
|
@@ -330,6 +328,7 @@ def setup(
|
|
330
328
|
|
331
329
|
def teardown(exit_code: Optional[int] = None) -> None:
|
332
330
|
setup_instance = _WandbSetup._instance
|
331
|
+
_WandbSetup._instance = None
|
332
|
+
|
333
333
|
if setup_instance:
|
334
334
|
setup_instance._teardown(exit_code=exit_code)
|
335
|
-
_WandbSetup._instance = None
|