wandb 0.19.12rc1__py3-none-macosx_11_0_arm64.whl → 0.20.1__py3-none-macosx_11_0_arm64.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 -2
- wandb/__init__.pyi +3 -6
- wandb/_iterutils.py +26 -7
- wandb/_pydantic/__init__.py +2 -1
- wandb/_pydantic/utils.py +7 -0
- wandb/agents/pyagent.py +9 -15
- wandb/analytics/sentry.py +1 -2
- wandb/apis/attrs.py +3 -4
- wandb/apis/importers/internals/util.py +1 -1
- wandb/apis/importers/validation.py +2 -2
- wandb/apis/importers/wandb.py +30 -25
- wandb/apis/normalize.py +2 -2
- wandb/apis/public/__init__.py +1 -0
- wandb/apis/public/api.py +37 -33
- wandb/apis/public/artifacts.py +103 -72
- wandb/apis/public/jobs.py +3 -2
- wandb/apis/public/registries/registries_search.py +4 -2
- wandb/apis/public/registries/registry.py +1 -1
- wandb/apis/public/registries/utils.py +9 -9
- wandb/apis/public/runs.py +18 -6
- wandb/automations/_filters/expressions.py +1 -1
- wandb/automations/_filters/operators.py +1 -1
- wandb/automations/_filters/run_metrics.py +1 -1
- wandb/beta/workflows.py +6 -5
- wandb/bin/gpu_stats +0 -0
- wandb/bin/wandb-core +0 -0
- wandb/cli/cli.py +54 -73
- wandb/docker/__init__.py +21 -74
- wandb/docker/names.py +40 -0
- wandb/env.py +0 -1
- wandb/errors/util.py +1 -1
- wandb/filesync/step_checksum.py +1 -1
- wandb/filesync/step_upload.py +1 -1
- wandb/integration/diffusers/resolvers/multimodal.py +1 -2
- wandb/integration/gym/__init__.py +5 -6
- wandb/integration/keras/callbacks/model_checkpoint.py +2 -2
- wandb/integration/keras/keras.py +13 -19
- wandb/integration/kfp/kfp_patch.py +2 -3
- wandb/integration/langchain/wandb_tracer.py +1 -1
- wandb/integration/metaflow/metaflow.py +13 -13
- wandb/integration/openai/fine_tuning.py +3 -2
- wandb/integration/sagemaker/auth.py +2 -1
- wandb/integration/sklearn/utils.py +2 -1
- wandb/integration/tensorboard/__init__.py +1 -1
- wandb/integration/tensorboard/log.py +2 -5
- wandb/integration/tensorflow/__init__.py +2 -2
- wandb/jupyter.py +20 -17
- wandb/plot/confusion_matrix.py +1 -1
- wandb/plot/utils.py +8 -7
- wandb/proto/v3/wandb_internal_pb2.py +355 -335
- wandb/proto/v3/wandb_settings_pb2.py +2 -2
- wandb/proto/v3/wandb_telemetry_pb2.py +12 -12
- wandb/proto/v4/wandb_internal_pb2.py +339 -335
- wandb/proto/v4/wandb_settings_pb2.py +2 -2
- wandb/proto/v4/wandb_telemetry_pb2.py +12 -12
- wandb/proto/v5/wandb_internal_pb2.py +339 -335
- wandb/proto/v5/wandb_settings_pb2.py +2 -2
- wandb/proto/v5/wandb_telemetry_pb2.py +12 -12
- wandb/proto/v6/wandb_internal_pb2.py +339 -335
- wandb/proto/v6/wandb_settings_pb2.py +2 -2
- wandb/proto/v6/wandb_telemetry_pb2.py +12 -12
- wandb/proto/wandb_deprecated.py +6 -8
- wandb/sdk/artifacts/_internal_artifact.py +43 -0
- wandb/sdk/artifacts/_validators.py +55 -35
- wandb/sdk/artifacts/artifact.py +117 -115
- wandb/sdk/artifacts/artifact_download_logger.py +2 -0
- wandb/sdk/artifacts/artifact_saver.py +1 -3
- wandb/sdk/artifacts/artifact_state.py +2 -0
- wandb/sdk/artifacts/artifact_ttl.py +2 -0
- wandb/sdk/artifacts/exceptions.py +14 -0
- wandb/sdk/artifacts/staging.py +2 -0
- wandb/sdk/artifacts/storage_handlers/local_file_handler.py +2 -6
- wandb/sdk/artifacts/storage_handlers/multi_handler.py +1 -1
- wandb/sdk/artifacts/storage_handlers/tracking_handler.py +2 -6
- wandb/sdk/artifacts/storage_handlers/wb_artifact_handler.py +1 -5
- wandb/sdk/artifacts/storage_handlers/wb_local_artifact_handler.py +1 -1
- wandb/sdk/artifacts/storage_layout.py +2 -0
- wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py +3 -3
- wandb/sdk/backend/backend.py +11 -182
- wandb/sdk/data_types/_dtypes.py +2 -6
- wandb/sdk/data_types/audio.py +20 -3
- wandb/sdk/data_types/base_types/media.py +12 -7
- wandb/sdk/data_types/base_types/wb_value.py +8 -18
- wandb/sdk/data_types/bokeh.py +19 -2
- wandb/sdk/data_types/helper_types/bounding_boxes_2d.py +17 -1
- wandb/sdk/data_types/helper_types/image_mask.py +7 -1
- wandb/sdk/data_types/html.py +4 -4
- wandb/sdk/data_types/image.py +178 -103
- wandb/sdk/data_types/molecule.py +6 -6
- wandb/sdk/data_types/object_3d.py +10 -5
- wandb/sdk/data_types/saved_model.py +11 -6
- wandb/sdk/data_types/table.py +313 -83
- wandb/sdk/data_types/table_decorators.py +108 -0
- wandb/sdk/data_types/utils.py +43 -7
- wandb/sdk/data_types/video.py +21 -3
- wandb/sdk/interface/interface.py +10 -0
- wandb/sdk/internal/datastore.py +2 -6
- wandb/sdk/internal/file_pusher.py +1 -5
- wandb/sdk/internal/file_stream.py +8 -17
- wandb/sdk/internal/handler.py +2 -2
- wandb/sdk/internal/incremental_table_util.py +53 -0
- wandb/sdk/internal/internal.py +3 -5
- wandb/sdk/internal/internal_api.py +66 -89
- wandb/sdk/internal/job_builder.py +2 -7
- wandb/sdk/internal/profiler.py +2 -2
- wandb/sdk/internal/progress.py +1 -3
- wandb/sdk/internal/run.py +1 -6
- wandb/sdk/internal/sender.py +24 -36
- wandb/sdk/internal/system/assets/aggregators.py +1 -7
- wandb/sdk/internal/system/assets/disk.py +3 -3
- wandb/sdk/internal/system/assets/gpu.py +4 -4
- wandb/sdk/internal/system/assets/gpu_amd.py +4 -4
- wandb/sdk/internal/system/assets/interfaces.py +6 -6
- wandb/sdk/internal/system/assets/tpu.py +1 -1
- wandb/sdk/internal/system/assets/trainium.py +6 -6
- wandb/sdk/internal/system/system_info.py +5 -7
- wandb/sdk/internal/system/system_monitor.py +4 -4
- wandb/sdk/internal/tb_watcher.py +5 -7
- wandb/sdk/launch/_launch.py +1 -1
- wandb/sdk/launch/_project_spec.py +19 -20
- wandb/sdk/launch/agent/agent.py +3 -3
- wandb/sdk/launch/agent/config.py +1 -1
- wandb/sdk/launch/agent/job_status_tracker.py +2 -2
- wandb/sdk/launch/builder/build.py +2 -3
- wandb/sdk/launch/builder/kaniko_builder.py +5 -4
- wandb/sdk/launch/environment/gcp_environment.py +1 -2
- wandb/sdk/launch/registry/azure_container_registry.py +2 -2
- wandb/sdk/launch/registry/elastic_container_registry.py +2 -2
- wandb/sdk/launch/registry/google_artifact_registry.py +3 -3
- wandb/sdk/launch/runner/abstract.py +5 -5
- wandb/sdk/launch/runner/kubernetes_monitor.py +2 -2
- wandb/sdk/launch/runner/kubernetes_runner.py +1 -1
- wandb/sdk/launch/runner/sagemaker_runner.py +2 -4
- wandb/sdk/launch/runner/vertex_runner.py +2 -7
- wandb/sdk/launch/sweeps/__init__.py +1 -1
- wandb/sdk/launch/sweeps/scheduler.py +2 -2
- wandb/sdk/launch/sweeps/utils.py +3 -3
- wandb/sdk/launch/utils.py +3 -4
- wandb/sdk/lib/apikey.py +5 -8
- wandb/sdk/lib/config_util.py +3 -3
- wandb/sdk/lib/fsm.py +3 -18
- wandb/sdk/lib/gitlib.py +6 -5
- wandb/sdk/lib/ipython.py +2 -2
- wandb/sdk/lib/json_util.py +9 -14
- wandb/sdk/lib/printer.py +3 -8
- wandb/sdk/lib/redirect.py +1 -1
- wandb/sdk/lib/retry.py +3 -7
- wandb/sdk/lib/run_moment.py +2 -2
- wandb/sdk/lib/service_connection.py +3 -1
- wandb/sdk/lib/service_token.py +1 -2
- wandb/sdk/mailbox/mailbox_handle.py +3 -7
- wandb/sdk/mailbox/response_handle.py +2 -6
- wandb/sdk/service/streams.py +3 -7
- wandb/sdk/verify/verify.py +5 -6
- wandb/sdk/wandb_config.py +1 -1
- wandb/sdk/wandb_init.py +38 -106
- wandb/sdk/wandb_login.py +7 -6
- wandb/sdk/wandb_run.py +52 -240
- wandb/sdk/wandb_settings.py +71 -60
- wandb/sdk/wandb_setup.py +40 -14
- wandb/sdk/wandb_watch.py +5 -7
- wandb/sync/__init__.py +1 -1
- wandb/sync/sync.py +13 -13
- wandb/util.py +17 -35
- wandb/wandb_agent.py +8 -11
- {wandb-0.19.12rc1.dist-info → wandb-0.20.1.dist-info}/METADATA +5 -5
- {wandb-0.19.12rc1.dist-info → wandb-0.20.1.dist-info}/RECORD +170 -168
- wandb/docker/auth.py +0 -435
- wandb/docker/www_authenticate.py +0 -94
- {wandb-0.19.12rc1.dist-info → wandb-0.20.1.dist-info}/WHEEL +0 -0
- {wandb-0.19.12rc1.dist-info → wandb-0.20.1.dist-info}/entry_points.txt +0 -0
- {wandb-0.19.12rc1.dist-info → wandb-0.20.1.dist-info}/licenses/LICENSE +0 -0
wandb/docker/auth.py
DELETED
@@ -1,435 +0,0 @@
|
|
1
|
-
# originally: https://github.com/docker/docker-py/blob/master/docker/auth.py
|
2
|
-
import base64
|
3
|
-
import json
|
4
|
-
import logging
|
5
|
-
import os
|
6
|
-
import platform
|
7
|
-
from typing import Any, Dict, Mapping, Optional, Tuple, Union
|
8
|
-
|
9
|
-
import dockerpycreds # type: ignore
|
10
|
-
|
11
|
-
IS_WINDOWS_PLATFORM = platform.system() == "Windows"
|
12
|
-
DOCKER_CONFIG_FILENAME = os.path.join(".docker", "config.json")
|
13
|
-
LEGACY_DOCKER_CONFIG_FILENAME = ".dockercfg"
|
14
|
-
INDEX_NAME = "docker.io"
|
15
|
-
INDEX_URL = f"https://index.{INDEX_NAME}/v1/"
|
16
|
-
TOKEN_USERNAME = "<token>"
|
17
|
-
|
18
|
-
log = logging.getLogger(__name__)
|
19
|
-
|
20
|
-
|
21
|
-
class DockerError(Exception):
|
22
|
-
"""Base class from which all other exceptions inherit.
|
23
|
-
|
24
|
-
If you want to catch all errors that the Docker SDK might raise,
|
25
|
-
catch this base exception.
|
26
|
-
"""
|
27
|
-
|
28
|
-
|
29
|
-
class InvalidConfigFileError(DockerError):
|
30
|
-
pass
|
31
|
-
|
32
|
-
|
33
|
-
class InvalidRepositoryError(DockerError):
|
34
|
-
pass
|
35
|
-
|
36
|
-
|
37
|
-
def find_config_file(config_path: Optional[str] = None) -> Optional[str]:
|
38
|
-
paths = list(
|
39
|
-
filter(
|
40
|
-
None,
|
41
|
-
[
|
42
|
-
config_path, # 1
|
43
|
-
config_path_from_environment(), # 2
|
44
|
-
os.path.join(home_dir(), DOCKER_CONFIG_FILENAME), # 3
|
45
|
-
os.path.join(home_dir(), LEGACY_DOCKER_CONFIG_FILENAME), # 4
|
46
|
-
],
|
47
|
-
)
|
48
|
-
)
|
49
|
-
|
50
|
-
log.debug(f"Trying paths: {repr(paths)}")
|
51
|
-
|
52
|
-
for path in paths:
|
53
|
-
if os.path.exists(path):
|
54
|
-
log.debug(f"Found file at path: {path}")
|
55
|
-
return path
|
56
|
-
|
57
|
-
log.debug("No config file found")
|
58
|
-
|
59
|
-
return None
|
60
|
-
|
61
|
-
|
62
|
-
def config_path_from_environment() -> Optional[str]:
|
63
|
-
config_dir = os.environ.get("DOCKER_CONFIG")
|
64
|
-
if not config_dir:
|
65
|
-
return None
|
66
|
-
return os.path.join(config_dir, os.path.basename(DOCKER_CONFIG_FILENAME))
|
67
|
-
|
68
|
-
|
69
|
-
def home_dir() -> str:
|
70
|
-
"""Get the user's home directory.
|
71
|
-
|
72
|
-
Uses the same logic as the Docker Engine client - use %USERPROFILE% on Windows,
|
73
|
-
$HOME/getuid on POSIX.
|
74
|
-
"""
|
75
|
-
if IS_WINDOWS_PLATFORM:
|
76
|
-
return os.environ.get("USERPROFILE", "")
|
77
|
-
else:
|
78
|
-
return os.path.expanduser("~")
|
79
|
-
|
80
|
-
|
81
|
-
def load_general_config(config_path: Optional[str] = None) -> Dict:
|
82
|
-
config_file = find_config_file(config_path)
|
83
|
-
|
84
|
-
if not config_file:
|
85
|
-
return {}
|
86
|
-
|
87
|
-
try:
|
88
|
-
with open(config_file) as f:
|
89
|
-
conf: Dict = json.load(f)
|
90
|
-
return conf
|
91
|
-
except (OSError, ValueError) as e:
|
92
|
-
# In the case of a legacy `.dockercfg` file, we won't
|
93
|
-
# be able to load any JSON data.
|
94
|
-
log.debug(e)
|
95
|
-
|
96
|
-
log.debug("All parsing attempts failed - returning empty config")
|
97
|
-
return {}
|
98
|
-
|
99
|
-
|
100
|
-
def resolve_repository_name(repo_name: str) -> Tuple[str, str]:
|
101
|
-
if "://" in repo_name:
|
102
|
-
raise InvalidRepositoryError(
|
103
|
-
f"Repository name cannot contain a scheme ({repo_name})"
|
104
|
-
)
|
105
|
-
|
106
|
-
index_name, remote_name = split_repo_name(repo_name)
|
107
|
-
if index_name[0] == "-" or index_name[-1] == "-":
|
108
|
-
raise InvalidRepositoryError(
|
109
|
-
f"Invalid index name ({index_name}). Cannot begin or end with a hyphen."
|
110
|
-
)
|
111
|
-
return resolve_index_name(index_name), remote_name
|
112
|
-
|
113
|
-
|
114
|
-
def resolve_index_name(index_name: str) -> str:
|
115
|
-
index_name = convert_to_hostname(index_name)
|
116
|
-
if index_name == "index." + INDEX_NAME:
|
117
|
-
index_name = INDEX_NAME
|
118
|
-
return index_name
|
119
|
-
|
120
|
-
|
121
|
-
def split_repo_name(repo_name: str) -> Tuple[str, str]:
|
122
|
-
parts = repo_name.split("/", 1)
|
123
|
-
if len(parts) == 1 or (
|
124
|
-
"." not in parts[0] and ":" not in parts[0] and parts[0] != "localhost"
|
125
|
-
):
|
126
|
-
# This is a docker index repo (ex: username/foobar or ubuntu)
|
127
|
-
return INDEX_NAME, repo_name
|
128
|
-
return parts[0], parts[1]
|
129
|
-
|
130
|
-
|
131
|
-
def get_credential_store(authconfig: Dict, registry: str) -> Optional[str]:
|
132
|
-
if not isinstance(authconfig, AuthConfig):
|
133
|
-
authconfig = AuthConfig(authconfig)
|
134
|
-
return authconfig.get_credential_store(registry)
|
135
|
-
|
136
|
-
|
137
|
-
class AuthConfig(dict):
|
138
|
-
def __init__(self, dct: Dict, credstore_env: Optional[Mapping] = None) -> None:
|
139
|
-
super().__init__(dct)
|
140
|
-
if "auths" not in dct:
|
141
|
-
dct["auths"] = {}
|
142
|
-
self.update(dct)
|
143
|
-
self._credstore_env = credstore_env
|
144
|
-
self._stores: Dict[str, dockerpycreds.Store] = dict()
|
145
|
-
|
146
|
-
@classmethod
|
147
|
-
def parse_auth(
|
148
|
-
cls,
|
149
|
-
entries: Dict[str, Dict[str, Any]],
|
150
|
-
raise_on_error: bool = False,
|
151
|
-
) -> Dict[str, Dict[str, Any]]:
|
152
|
-
"""Parse authentication entries.
|
153
|
-
|
154
|
-
Args:
|
155
|
-
entries: Dict of authentication entries.
|
156
|
-
raise_on_error: If set to true, an invalid format will raise
|
157
|
-
InvalidConfigFileError
|
158
|
-
Returns:
|
159
|
-
Authentication registry.
|
160
|
-
"""
|
161
|
-
conf = {}
|
162
|
-
for registry, entry in entries.items():
|
163
|
-
if not isinstance(entry, dict):
|
164
|
-
log.debug(f"Config entry for key {registry} is not auth config") # type: ignore
|
165
|
-
# We sometimes fall back to parsing the whole config as if it
|
166
|
-
# was the auth config by itself, for legacy purposes. In that
|
167
|
-
# case, we fail silently and return an empty conf if any of the
|
168
|
-
# keys is not formatted properly.
|
169
|
-
if raise_on_error:
|
170
|
-
raise InvalidConfigFileError(
|
171
|
-
f"Invalid configuration for registry {registry}"
|
172
|
-
)
|
173
|
-
return {}
|
174
|
-
if "identitytoken" in entry:
|
175
|
-
log.debug(f"Found an IdentityToken entry for registry {registry}")
|
176
|
-
conf[registry] = {"IdentityToken": entry["identitytoken"]}
|
177
|
-
continue # Other values are irrelevant if we have a token
|
178
|
-
|
179
|
-
if "auth" not in entry:
|
180
|
-
# Starting with engine v1.11 (API 1.23), an empty dictionary is
|
181
|
-
# a valid value in the auth's config.
|
182
|
-
# https://github.com/docker/compose/issues/3265
|
183
|
-
log.debug(
|
184
|
-
f"Auth data for {registry} is absent. Client might be using a "
|
185
|
-
"credentials store instead."
|
186
|
-
)
|
187
|
-
conf[registry] = {}
|
188
|
-
continue
|
189
|
-
|
190
|
-
username, password = decode_auth(entry["auth"])
|
191
|
-
log.debug(
|
192
|
-
f"Found entry (registry={repr(registry)}, username={repr(username)})"
|
193
|
-
)
|
194
|
-
|
195
|
-
conf[registry] = {
|
196
|
-
"username": username,
|
197
|
-
"password": password,
|
198
|
-
"email": entry.get("email"),
|
199
|
-
"serveraddress": registry,
|
200
|
-
}
|
201
|
-
return conf
|
202
|
-
|
203
|
-
@classmethod
|
204
|
-
def load_config(
|
205
|
-
cls,
|
206
|
-
config_path: Optional[str],
|
207
|
-
config_dict: Optional[Dict[str, Any]],
|
208
|
-
credstore_env: Optional[Mapping] = None,
|
209
|
-
) -> "AuthConfig":
|
210
|
-
"""Load authentication data from a Docker configuration file.
|
211
|
-
|
212
|
-
If the config_path is not passed in it looks for a configuration file in the
|
213
|
-
root directory.
|
214
|
-
|
215
|
-
Lookup priority:
|
216
|
-
explicit config_path parameter > DOCKER_CONFIG environment
|
217
|
-
variable > ~/.docker/config.json > ~/.dockercfg.
|
218
|
-
"""
|
219
|
-
if not config_dict:
|
220
|
-
config_file = find_config_file(config_path)
|
221
|
-
|
222
|
-
if not config_file:
|
223
|
-
return cls({}, credstore_env)
|
224
|
-
try:
|
225
|
-
with open(config_file) as f:
|
226
|
-
config_dict = json.load(f)
|
227
|
-
except (OSError, KeyError, ValueError) as e:
|
228
|
-
# Likely missing new Docker config file, or it's in an
|
229
|
-
# unknown format, continue to attempt to read old location
|
230
|
-
# and format.
|
231
|
-
log.debug(e)
|
232
|
-
return cls(_load_legacy_config(config_file), credstore_env)
|
233
|
-
|
234
|
-
res = {}
|
235
|
-
assert isinstance(config_dict, Dict) # worship mypy
|
236
|
-
if config_dict.get("auths"):
|
237
|
-
log.debug("Found 'auths' section")
|
238
|
-
res.update(
|
239
|
-
{"auths": cls.parse_auth(config_dict.pop("auths"), raise_on_error=True)}
|
240
|
-
)
|
241
|
-
if config_dict.get("credsStore"):
|
242
|
-
log.debug("Found 'credsStore' section")
|
243
|
-
res.update({"credsStore": config_dict.pop("credsStore")})
|
244
|
-
if config_dict.get("credHelpers"):
|
245
|
-
log.debug("Found 'credHelpers' section")
|
246
|
-
res.update({"credHelpers": config_dict.pop("credHelpers")})
|
247
|
-
if res:
|
248
|
-
return cls(res, credstore_env)
|
249
|
-
|
250
|
-
log.debug(
|
251
|
-
"Couldn't find auth-related section ; attempting to interpret "
|
252
|
-
"as auth-only file"
|
253
|
-
)
|
254
|
-
return cls({"auths": cls.parse_auth(config_dict)}, credstore_env)
|
255
|
-
|
256
|
-
@property
|
257
|
-
def auths(self) -> Dict[str, Dict[str, Any]]:
|
258
|
-
return self.get("auths", {}) # type: ignore
|
259
|
-
|
260
|
-
@property
|
261
|
-
def creds_store(self) -> Optional[str]:
|
262
|
-
return self.get("credsStore", None) # type: ignore
|
263
|
-
|
264
|
-
@property
|
265
|
-
def cred_helpers(self) -> Dict:
|
266
|
-
return self.get("credHelpers", {}) # type: ignore
|
267
|
-
|
268
|
-
@property
|
269
|
-
def is_empty(self) -> bool:
|
270
|
-
return not self.auths and not self.creds_store and not self.cred_helpers
|
271
|
-
|
272
|
-
def resolve_authconfig(
|
273
|
-
self, registry: Optional[str] = None
|
274
|
-
) -> Optional[Dict[str, Any]]:
|
275
|
-
"""Return the authentication data for a specific registry.
|
276
|
-
|
277
|
-
As with the Docker client, legacy entries in the config with full URLs are
|
278
|
-
stripped down to hostnames before checking for a match. Returns None if no match
|
279
|
-
was found.
|
280
|
-
"""
|
281
|
-
if self.creds_store or self.cred_helpers:
|
282
|
-
store_name = self.get_credential_store(registry)
|
283
|
-
if store_name is not None:
|
284
|
-
log.debug(f"Using credentials store {store_name!r}")
|
285
|
-
cfg = self._resolve_authconfig_credstore(registry, store_name)
|
286
|
-
if cfg is not None:
|
287
|
-
return cfg
|
288
|
-
log.debug("No entry in credstore - fetching from auth dict")
|
289
|
-
|
290
|
-
# Default to the public index server
|
291
|
-
registry = resolve_index_name(registry) if registry else INDEX_NAME
|
292
|
-
log.debug(f"Looking for auth entry for {repr(registry)}")
|
293
|
-
|
294
|
-
if registry in self.auths:
|
295
|
-
log.debug(f"Found {repr(registry)}")
|
296
|
-
return self.auths[registry]
|
297
|
-
|
298
|
-
for key, conf in self.auths.items():
|
299
|
-
if resolve_index_name(key) == registry:
|
300
|
-
log.debug(f"Found {repr(key)}")
|
301
|
-
return conf
|
302
|
-
|
303
|
-
log.debug("No entry found")
|
304
|
-
return None
|
305
|
-
|
306
|
-
def _resolve_authconfig_credstore(
|
307
|
-
self, registry: Optional[str], credstore_name: str
|
308
|
-
) -> Optional[Dict[str, Any]]:
|
309
|
-
if not registry or registry == INDEX_NAME:
|
310
|
-
# The ecosystem is a little schizophrenic with recker.io VS
|
311
|
-
# docker.io - in that case, it seems the full URL is necessary.
|
312
|
-
registry = INDEX_URL
|
313
|
-
log.debug(f"Looking for auth entry for {repr(registry)}")
|
314
|
-
store = self._get_store_instance(credstore_name)
|
315
|
-
try:
|
316
|
-
data = store.get(registry)
|
317
|
-
res = {
|
318
|
-
"ServerAddress": registry,
|
319
|
-
}
|
320
|
-
if data["Username"] == TOKEN_USERNAME:
|
321
|
-
res["IdentityToken"] = data["Secret"]
|
322
|
-
else:
|
323
|
-
res.update({"Username": data["Username"], "Password": data["Secret"]})
|
324
|
-
return res
|
325
|
-
except (dockerpycreds.CredentialsNotFound, ValueError):
|
326
|
-
log.debug("No entry found")
|
327
|
-
return None
|
328
|
-
except dockerpycreds.StoreError as e:
|
329
|
-
raise DockerError(f"Credentials store error: {repr(e)}")
|
330
|
-
|
331
|
-
def _get_store_instance(self, name: str) -> "dockerpycreds.Store":
|
332
|
-
if name not in self._stores:
|
333
|
-
self._stores[name] = dockerpycreds.Store(
|
334
|
-
name, environment=self._credstore_env
|
335
|
-
)
|
336
|
-
return self._stores[name]
|
337
|
-
|
338
|
-
def get_credential_store(self, registry: Optional[str]) -> Optional[str]:
|
339
|
-
if not registry or registry == INDEX_NAME:
|
340
|
-
registry = INDEX_URL
|
341
|
-
|
342
|
-
return self.cred_helpers.get(registry) or self.creds_store
|
343
|
-
|
344
|
-
def get_all_credentials(self) -> Dict[str, Dict[str, Any]]:
|
345
|
-
auth_data = self.auths.copy()
|
346
|
-
if self.creds_store:
|
347
|
-
# Retrieve all credentials from the default store
|
348
|
-
store = self._get_store_instance(self.creds_store)
|
349
|
-
for k in store.list().keys():
|
350
|
-
auth_data[k] = self._resolve_authconfig_credstore(k, self.creds_store) # type: ignore
|
351
|
-
|
352
|
-
# credHelpers entries take priority over all others
|
353
|
-
for reg, store_name in self.cred_helpers.items():
|
354
|
-
auth_data[reg] = self._resolve_authconfig_credstore(reg, store_name) # type: ignore
|
355
|
-
|
356
|
-
return auth_data
|
357
|
-
|
358
|
-
def add_auth(self, reg: str, data: Dict[str, Any]) -> None:
|
359
|
-
self["auths"][reg] = data
|
360
|
-
|
361
|
-
|
362
|
-
def resolve_authconfig(
|
363
|
-
authconfig: Dict,
|
364
|
-
registry: Optional[str] = None,
|
365
|
-
credstore_env: Optional[Mapping] = None,
|
366
|
-
) -> Optional[Dict[str, Any]]:
|
367
|
-
if not isinstance(authconfig, AuthConfig):
|
368
|
-
authconfig = AuthConfig(authconfig, credstore_env)
|
369
|
-
return authconfig.resolve_authconfig(registry)
|
370
|
-
|
371
|
-
|
372
|
-
def convert_to_hostname(url: str) -> str:
|
373
|
-
return url.replace("http://", "").replace("https://", "").split("/", 1)[0]
|
374
|
-
|
375
|
-
|
376
|
-
def decode_auth(auth: Union[str, bytes]) -> Tuple[str, str]:
|
377
|
-
if isinstance(auth, str):
|
378
|
-
auth = auth.encode("ascii")
|
379
|
-
s = base64.b64decode(auth)
|
380
|
-
login, pwd = s.split(b":", 1)
|
381
|
-
return login.decode("utf8"), pwd.decode("utf8")
|
382
|
-
|
383
|
-
|
384
|
-
def parse_auth(
|
385
|
-
entries: Dict, raise_on_error: bool = False
|
386
|
-
) -> Dict[str, Dict[str, Any]]:
|
387
|
-
"""Parse authentication entries.
|
388
|
-
|
389
|
-
Args:
|
390
|
-
entries: Dict of authentication entries.
|
391
|
-
raise_on_error: If set to true, an invalid format will raise
|
392
|
-
InvalidConfigFileError
|
393
|
-
Returns:
|
394
|
-
Authentication registry.
|
395
|
-
"""
|
396
|
-
return AuthConfig.parse_auth(entries, raise_on_error)
|
397
|
-
|
398
|
-
|
399
|
-
def load_config(
|
400
|
-
config_path: Optional[str] = None,
|
401
|
-
config_dict: Optional[Dict[str, Any]] = None,
|
402
|
-
credstore_env: Optional[Mapping] = None,
|
403
|
-
) -> AuthConfig:
|
404
|
-
return AuthConfig.load_config(config_path, config_dict, credstore_env)
|
405
|
-
|
406
|
-
|
407
|
-
def _load_legacy_config(
|
408
|
-
config_file: str,
|
409
|
-
) -> Dict[str, Dict[str, Union[str, Dict[str, str]]]]:
|
410
|
-
log.debug("Attempting to parse legacy auth file format")
|
411
|
-
try:
|
412
|
-
data = []
|
413
|
-
with open(config_file) as f:
|
414
|
-
for line in f.readlines():
|
415
|
-
data.append(line.strip().split(" = ")[1])
|
416
|
-
if len(data) < 2:
|
417
|
-
# Not enough data
|
418
|
-
raise InvalidConfigFileError("Invalid or empty configuration file!")
|
419
|
-
|
420
|
-
username, password = decode_auth(data[0])
|
421
|
-
return {
|
422
|
-
"auths": {
|
423
|
-
INDEX_NAME: {
|
424
|
-
"username": username,
|
425
|
-
"password": password,
|
426
|
-
"email": data[1],
|
427
|
-
"serveraddress": INDEX_URL,
|
428
|
-
}
|
429
|
-
}
|
430
|
-
}
|
431
|
-
except Exception as e:
|
432
|
-
log.debug(e)
|
433
|
-
|
434
|
-
log.debug("All parsing attempts failed - returning empty config")
|
435
|
-
return {}
|
wandb/docker/www_authenticate.py
DELETED
@@ -1,94 +0,0 @@
|
|
1
|
-
# Taken from: https://github.com/alexsdutton/www-authenticate
|
2
|
-
import re
|
3
|
-
from collections import OrderedDict
|
4
|
-
from typing import Any, Optional
|
5
|
-
|
6
|
-
_tokens = (
|
7
|
-
("token", re.compile(r"""^([!#$%&'*+\-.^_`|~\w/]+(?:={1,2}$)?)""")),
|
8
|
-
("token", re.compile(r'''^"((?:[^"\\]|\\\\|\\")+)"''')),
|
9
|
-
(None, re.compile(r"^\s+")),
|
10
|
-
("equals", re.compile(r"^(=)")),
|
11
|
-
("comma", re.compile(r"^(,)")),
|
12
|
-
)
|
13
|
-
|
14
|
-
|
15
|
-
def _casefold(value: str) -> str:
|
16
|
-
try:
|
17
|
-
return value.casefold()
|
18
|
-
except AttributeError:
|
19
|
-
return value.lower()
|
20
|
-
|
21
|
-
|
22
|
-
class CaseFoldedOrderedDict(OrderedDict):
|
23
|
-
def __getitem__(self, key: str) -> Any:
|
24
|
-
return super().__getitem__(_casefold(key))
|
25
|
-
|
26
|
-
def __setitem__(self, key: str, value: Any) -> None:
|
27
|
-
super().__setitem__(_casefold(key), value)
|
28
|
-
|
29
|
-
def __contains__(self, key: object) -> bool:
|
30
|
-
return super().__contains__(_casefold(key)) # type: ignore
|
31
|
-
|
32
|
-
def get(self, key: str, default: Optional[Any] = None) -> Any:
|
33
|
-
return super().get(_casefold(key), default)
|
34
|
-
|
35
|
-
def pop(self, key: str, default: Optional[Any] = None) -> Any:
|
36
|
-
return super().pop(_casefold(key), default)
|
37
|
-
|
38
|
-
|
39
|
-
def _group_pairs(tokens: list) -> None:
|
40
|
-
i = 0
|
41
|
-
while i < len(tokens) - 2:
|
42
|
-
if (
|
43
|
-
tokens[i][0] == "token"
|
44
|
-
and tokens[i + 1][0] == "equals"
|
45
|
-
and tokens[i + 2][0] == "token"
|
46
|
-
):
|
47
|
-
tokens[i : i + 3] = [("pair", (tokens[i][1], tokens[i + 2][1]))]
|
48
|
-
i += 1
|
49
|
-
|
50
|
-
|
51
|
-
def _group_challenges(tokens: list) -> list:
|
52
|
-
challenges = []
|
53
|
-
while tokens:
|
54
|
-
j = 1
|
55
|
-
if len(tokens) == 1:
|
56
|
-
pass
|
57
|
-
elif tokens[1][0] == "comma":
|
58
|
-
pass
|
59
|
-
elif tokens[1][0] == "token":
|
60
|
-
j = 2
|
61
|
-
else:
|
62
|
-
while j < len(tokens) and tokens[j][0] == "pair":
|
63
|
-
j += 2
|
64
|
-
j -= 1
|
65
|
-
challenges.append((tokens[0][1], tokens[1:j]))
|
66
|
-
tokens[: j + 1] = []
|
67
|
-
return challenges
|
68
|
-
|
69
|
-
|
70
|
-
def parse(value: str) -> CaseFoldedOrderedDict:
|
71
|
-
tokens = []
|
72
|
-
while value:
|
73
|
-
for token_name, pattern in _tokens:
|
74
|
-
match = pattern.match(value)
|
75
|
-
if match:
|
76
|
-
value = value[match.end() :]
|
77
|
-
if token_name:
|
78
|
-
tokens.append((token_name, match.group(1)))
|
79
|
-
break
|
80
|
-
else:
|
81
|
-
raise ValueError("Failed to parse value")
|
82
|
-
_group_pairs(tokens)
|
83
|
-
|
84
|
-
challenges = CaseFoldedOrderedDict()
|
85
|
-
for name, tokens in _group_challenges(tokens): # noqa: B020
|
86
|
-
args, kwargs = [], {}
|
87
|
-
for token_name, value in tokens:
|
88
|
-
if token_name == "token":
|
89
|
-
args.append(value)
|
90
|
-
elif token_name == "pair":
|
91
|
-
kwargs[value[0]] = value[1]
|
92
|
-
challenges[name] = (args and args[0]) or kwargs or None
|
93
|
-
|
94
|
-
return challenges
|
File without changes
|
File without changes
|
File without changes
|