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.
Files changed (172) hide show
  1. wandb/__init__.py +1 -2
  2. wandb/__init__.pyi +3 -6
  3. wandb/_iterutils.py +26 -7
  4. wandb/_pydantic/__init__.py +2 -1
  5. wandb/_pydantic/utils.py +7 -0
  6. wandb/agents/pyagent.py +9 -15
  7. wandb/analytics/sentry.py +1 -2
  8. wandb/apis/attrs.py +3 -4
  9. wandb/apis/importers/internals/util.py +1 -1
  10. wandb/apis/importers/validation.py +2 -2
  11. wandb/apis/importers/wandb.py +30 -25
  12. wandb/apis/normalize.py +2 -2
  13. wandb/apis/public/__init__.py +1 -0
  14. wandb/apis/public/api.py +37 -33
  15. wandb/apis/public/artifacts.py +103 -72
  16. wandb/apis/public/jobs.py +3 -2
  17. wandb/apis/public/registries/registries_search.py +4 -2
  18. wandb/apis/public/registries/registry.py +1 -1
  19. wandb/apis/public/registries/utils.py +9 -9
  20. wandb/apis/public/runs.py +18 -6
  21. wandb/automations/_filters/expressions.py +1 -1
  22. wandb/automations/_filters/operators.py +1 -1
  23. wandb/automations/_filters/run_metrics.py +1 -1
  24. wandb/beta/workflows.py +6 -5
  25. wandb/bin/gpu_stats +0 -0
  26. wandb/bin/wandb-core +0 -0
  27. wandb/cli/cli.py +54 -73
  28. wandb/docker/__init__.py +21 -74
  29. wandb/docker/names.py +40 -0
  30. wandb/env.py +0 -1
  31. wandb/errors/util.py +1 -1
  32. wandb/filesync/step_checksum.py +1 -1
  33. wandb/filesync/step_upload.py +1 -1
  34. wandb/integration/diffusers/resolvers/multimodal.py +1 -2
  35. wandb/integration/gym/__init__.py +5 -6
  36. wandb/integration/keras/callbacks/model_checkpoint.py +2 -2
  37. wandb/integration/keras/keras.py +13 -19
  38. wandb/integration/kfp/kfp_patch.py +2 -3
  39. wandb/integration/langchain/wandb_tracer.py +1 -1
  40. wandb/integration/metaflow/metaflow.py +13 -13
  41. wandb/integration/openai/fine_tuning.py +3 -2
  42. wandb/integration/sagemaker/auth.py +2 -1
  43. wandb/integration/sklearn/utils.py +2 -1
  44. wandb/integration/tensorboard/__init__.py +1 -1
  45. wandb/integration/tensorboard/log.py +2 -5
  46. wandb/integration/tensorflow/__init__.py +2 -2
  47. wandb/jupyter.py +20 -17
  48. wandb/plot/confusion_matrix.py +1 -1
  49. wandb/plot/utils.py +8 -7
  50. wandb/proto/v3/wandb_internal_pb2.py +355 -335
  51. wandb/proto/v3/wandb_settings_pb2.py +2 -2
  52. wandb/proto/v3/wandb_telemetry_pb2.py +12 -12
  53. wandb/proto/v4/wandb_internal_pb2.py +339 -335
  54. wandb/proto/v4/wandb_settings_pb2.py +2 -2
  55. wandb/proto/v4/wandb_telemetry_pb2.py +12 -12
  56. wandb/proto/v5/wandb_internal_pb2.py +339 -335
  57. wandb/proto/v5/wandb_settings_pb2.py +2 -2
  58. wandb/proto/v5/wandb_telemetry_pb2.py +12 -12
  59. wandb/proto/v6/wandb_internal_pb2.py +339 -335
  60. wandb/proto/v6/wandb_settings_pb2.py +2 -2
  61. wandb/proto/v6/wandb_telemetry_pb2.py +12 -12
  62. wandb/proto/wandb_deprecated.py +6 -8
  63. wandb/sdk/artifacts/_internal_artifact.py +43 -0
  64. wandb/sdk/artifacts/_validators.py +55 -35
  65. wandb/sdk/artifacts/artifact.py +117 -115
  66. wandb/sdk/artifacts/artifact_download_logger.py +2 -0
  67. wandb/sdk/artifacts/artifact_saver.py +1 -3
  68. wandb/sdk/artifacts/artifact_state.py +2 -0
  69. wandb/sdk/artifacts/artifact_ttl.py +2 -0
  70. wandb/sdk/artifacts/exceptions.py +14 -0
  71. wandb/sdk/artifacts/staging.py +2 -0
  72. wandb/sdk/artifacts/storage_handlers/local_file_handler.py +2 -6
  73. wandb/sdk/artifacts/storage_handlers/multi_handler.py +1 -1
  74. wandb/sdk/artifacts/storage_handlers/tracking_handler.py +2 -6
  75. wandb/sdk/artifacts/storage_handlers/wb_artifact_handler.py +1 -5
  76. wandb/sdk/artifacts/storage_handlers/wb_local_artifact_handler.py +1 -1
  77. wandb/sdk/artifacts/storage_layout.py +2 -0
  78. wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py +3 -3
  79. wandb/sdk/backend/backend.py +11 -182
  80. wandb/sdk/data_types/_dtypes.py +2 -6
  81. wandb/sdk/data_types/audio.py +20 -3
  82. wandb/sdk/data_types/base_types/media.py +12 -7
  83. wandb/sdk/data_types/base_types/wb_value.py +8 -18
  84. wandb/sdk/data_types/bokeh.py +19 -2
  85. wandb/sdk/data_types/helper_types/bounding_boxes_2d.py +17 -1
  86. wandb/sdk/data_types/helper_types/image_mask.py +7 -1
  87. wandb/sdk/data_types/html.py +4 -4
  88. wandb/sdk/data_types/image.py +178 -103
  89. wandb/sdk/data_types/molecule.py +6 -6
  90. wandb/sdk/data_types/object_3d.py +10 -5
  91. wandb/sdk/data_types/saved_model.py +11 -6
  92. wandb/sdk/data_types/table.py +313 -83
  93. wandb/sdk/data_types/table_decorators.py +108 -0
  94. wandb/sdk/data_types/utils.py +43 -7
  95. wandb/sdk/data_types/video.py +21 -3
  96. wandb/sdk/interface/interface.py +10 -0
  97. wandb/sdk/internal/datastore.py +2 -6
  98. wandb/sdk/internal/file_pusher.py +1 -5
  99. wandb/sdk/internal/file_stream.py +8 -17
  100. wandb/sdk/internal/handler.py +2 -2
  101. wandb/sdk/internal/incremental_table_util.py +53 -0
  102. wandb/sdk/internal/internal.py +3 -5
  103. wandb/sdk/internal/internal_api.py +66 -89
  104. wandb/sdk/internal/job_builder.py +2 -7
  105. wandb/sdk/internal/profiler.py +2 -2
  106. wandb/sdk/internal/progress.py +1 -3
  107. wandb/sdk/internal/run.py +1 -6
  108. wandb/sdk/internal/sender.py +24 -36
  109. wandb/sdk/internal/system/assets/aggregators.py +1 -7
  110. wandb/sdk/internal/system/assets/disk.py +3 -3
  111. wandb/sdk/internal/system/assets/gpu.py +4 -4
  112. wandb/sdk/internal/system/assets/gpu_amd.py +4 -4
  113. wandb/sdk/internal/system/assets/interfaces.py +6 -6
  114. wandb/sdk/internal/system/assets/tpu.py +1 -1
  115. wandb/sdk/internal/system/assets/trainium.py +6 -6
  116. wandb/sdk/internal/system/system_info.py +5 -7
  117. wandb/sdk/internal/system/system_monitor.py +4 -4
  118. wandb/sdk/internal/tb_watcher.py +5 -7
  119. wandb/sdk/launch/_launch.py +1 -1
  120. wandb/sdk/launch/_project_spec.py +19 -20
  121. wandb/sdk/launch/agent/agent.py +3 -3
  122. wandb/sdk/launch/agent/config.py +1 -1
  123. wandb/sdk/launch/agent/job_status_tracker.py +2 -2
  124. wandb/sdk/launch/builder/build.py +2 -3
  125. wandb/sdk/launch/builder/kaniko_builder.py +5 -4
  126. wandb/sdk/launch/environment/gcp_environment.py +1 -2
  127. wandb/sdk/launch/registry/azure_container_registry.py +2 -2
  128. wandb/sdk/launch/registry/elastic_container_registry.py +2 -2
  129. wandb/sdk/launch/registry/google_artifact_registry.py +3 -3
  130. wandb/sdk/launch/runner/abstract.py +5 -5
  131. wandb/sdk/launch/runner/kubernetes_monitor.py +2 -2
  132. wandb/sdk/launch/runner/kubernetes_runner.py +1 -1
  133. wandb/sdk/launch/runner/sagemaker_runner.py +2 -4
  134. wandb/sdk/launch/runner/vertex_runner.py +2 -7
  135. wandb/sdk/launch/sweeps/__init__.py +1 -1
  136. wandb/sdk/launch/sweeps/scheduler.py +2 -2
  137. wandb/sdk/launch/sweeps/utils.py +3 -3
  138. wandb/sdk/launch/utils.py +3 -4
  139. wandb/sdk/lib/apikey.py +5 -8
  140. wandb/sdk/lib/config_util.py +3 -3
  141. wandb/sdk/lib/fsm.py +3 -18
  142. wandb/sdk/lib/gitlib.py +6 -5
  143. wandb/sdk/lib/ipython.py +2 -2
  144. wandb/sdk/lib/json_util.py +9 -14
  145. wandb/sdk/lib/printer.py +3 -8
  146. wandb/sdk/lib/redirect.py +1 -1
  147. wandb/sdk/lib/retry.py +3 -7
  148. wandb/sdk/lib/run_moment.py +2 -2
  149. wandb/sdk/lib/service_connection.py +3 -1
  150. wandb/sdk/lib/service_token.py +1 -2
  151. wandb/sdk/mailbox/mailbox_handle.py +3 -7
  152. wandb/sdk/mailbox/response_handle.py +2 -6
  153. wandb/sdk/service/streams.py +3 -7
  154. wandb/sdk/verify/verify.py +5 -6
  155. wandb/sdk/wandb_config.py +1 -1
  156. wandb/sdk/wandb_init.py +38 -106
  157. wandb/sdk/wandb_login.py +7 -6
  158. wandb/sdk/wandb_run.py +52 -240
  159. wandb/sdk/wandb_settings.py +71 -60
  160. wandb/sdk/wandb_setup.py +40 -14
  161. wandb/sdk/wandb_watch.py +5 -7
  162. wandb/sync/__init__.py +1 -1
  163. wandb/sync/sync.py +13 -13
  164. wandb/util.py +17 -35
  165. wandb/wandb_agent.py +8 -11
  166. {wandb-0.19.12rc1.dist-info → wandb-0.20.1.dist-info}/METADATA +5 -5
  167. {wandb-0.19.12rc1.dist-info → wandb-0.20.1.dist-info}/RECORD +170 -168
  168. wandb/docker/auth.py +0 -435
  169. wandb/docker/www_authenticate.py +0 -94
  170. {wandb-0.19.12rc1.dist-info → wandb-0.20.1.dist-info}/WHEEL +0 -0
  171. {wandb-0.19.12rc1.dist-info → wandb-0.20.1.dist-info}/entry_points.txt +0 -0
  172. {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 {}
@@ -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