wandb 0.17.0rc2__py3-none-any.whl → 0.17.2__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (163) hide show
  1. wandb/__init__.py +4 -2
  2. wandb/apis/importers/internals/internal.py +0 -1
  3. wandb/apis/importers/wandb.py +12 -7
  4. wandb/apis/internal.py +0 -3
  5. wandb/apis/public/api.py +213 -79
  6. wandb/apis/public/artifacts.py +335 -100
  7. wandb/apis/public/files.py +9 -9
  8. wandb/apis/public/jobs.py +16 -4
  9. wandb/apis/public/projects.py +26 -28
  10. wandb/apis/public/query_generator.py +1 -1
  11. wandb/apis/public/runs.py +163 -65
  12. wandb/apis/public/sweeps.py +2 -2
  13. wandb/apis/reports/__init__.py +1 -7
  14. wandb/apis/reports/v1/__init__.py +5 -27
  15. wandb/apis/reports/v2/__init__.py +7 -19
  16. wandb/apis/workspaces/__init__.py +8 -0
  17. wandb/beta/workflows.py +8 -3
  18. wandb/cli/cli.py +151 -59
  19. wandb/docker/__init__.py +1 -1
  20. wandb/errors/term.py +10 -2
  21. wandb/filesync/step_checksum.py +1 -4
  22. wandb/filesync/step_prepare.py +4 -24
  23. wandb/filesync/step_upload.py +5 -107
  24. wandb/filesync/upload_job.py +0 -76
  25. wandb/integration/gym/__init__.py +35 -15
  26. wandb/integration/openai/fine_tuning.py +21 -3
  27. wandb/integration/prodigy/prodigy.py +1 -1
  28. wandb/jupyter.py +16 -17
  29. wandb/old/summary.py +5 -0
  30. wandb/plot/pr_curve.py +2 -1
  31. wandb/plot/roc_curve.py +2 -1
  32. wandb/{plots → plot}/utils.py +13 -25
  33. wandb/proto/v3/wandb_internal_pb2.py +54 -54
  34. wandb/proto/v3/wandb_settings_pb2.py +2 -2
  35. wandb/proto/v3/wandb_telemetry_pb2.py +10 -10
  36. wandb/proto/v4/wandb_internal_pb2.py +54 -54
  37. wandb/proto/v4/wandb_settings_pb2.py +2 -2
  38. wandb/proto/v4/wandb_telemetry_pb2.py +10 -10
  39. wandb/proto/v5/wandb_base_pb2.py +30 -0
  40. wandb/proto/v5/wandb_internal_pb2.py +355 -0
  41. wandb/proto/v5/wandb_server_pb2.py +63 -0
  42. wandb/proto/v5/wandb_settings_pb2.py +45 -0
  43. wandb/proto/v5/wandb_telemetry_pb2.py +41 -0
  44. wandb/proto/wandb_base_pb2.py +2 -0
  45. wandb/proto/wandb_deprecated.py +9 -1
  46. wandb/proto/wandb_generate_deprecated.py +34 -0
  47. wandb/proto/{wandb_internal_codegen.py → wandb_generate_proto.py} +1 -35
  48. wandb/proto/wandb_internal_pb2.py +2 -0
  49. wandb/proto/wandb_server_pb2.py +2 -0
  50. wandb/proto/wandb_settings_pb2.py +2 -0
  51. wandb/proto/wandb_telemetry_pb2.py +2 -0
  52. wandb/sdk/artifacts/artifact.py +76 -23
  53. wandb/sdk/artifacts/artifact_manifest.py +1 -1
  54. wandb/sdk/artifacts/artifact_manifest_entry.py +6 -3
  55. wandb/sdk/artifacts/artifact_manifests/artifact_manifest_v1.py +1 -1
  56. wandb/sdk/artifacts/artifact_saver.py +1 -10
  57. wandb/sdk/artifacts/storage_handlers/local_file_handler.py +6 -2
  58. wandb/sdk/artifacts/storage_handlers/multi_handler.py +1 -1
  59. wandb/sdk/artifacts/storage_handlers/tracking_handler.py +6 -4
  60. wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py +2 -42
  61. wandb/sdk/artifacts/storage_policy.py +1 -12
  62. wandb/sdk/data_types/_dtypes.py +5 -2
  63. wandb/sdk/data_types/html.py +1 -1
  64. wandb/sdk/data_types/image.py +1 -1
  65. wandb/sdk/data_types/object_3d.py +1 -1
  66. wandb/sdk/data_types/video.py +4 -2
  67. wandb/sdk/interface/interface.py +13 -0
  68. wandb/sdk/interface/interface_shared.py +1 -1
  69. wandb/sdk/internal/file_pusher.py +2 -5
  70. wandb/sdk/internal/file_stream.py +6 -19
  71. wandb/sdk/internal/internal_api.py +160 -138
  72. wandb/sdk/internal/job_builder.py +207 -135
  73. wandb/sdk/internal/progress.py +0 -28
  74. wandb/sdk/internal/sender.py +105 -42
  75. wandb/sdk/internal/settings_static.py +8 -1
  76. wandb/sdk/internal/system/assets/gpu.py +2 -0
  77. wandb/sdk/internal/system/assets/trainium.py +3 -3
  78. wandb/sdk/internal/system/system_info.py +4 -2
  79. wandb/sdk/internal/update.py +1 -1
  80. wandb/sdk/launch/__init__.py +9 -1
  81. wandb/sdk/launch/_launch.py +4 -24
  82. wandb/sdk/launch/_launch_add.py +1 -3
  83. wandb/sdk/launch/_project_spec.py +184 -224
  84. wandb/sdk/launch/agent/agent.py +58 -18
  85. wandb/sdk/launch/agent/config.py +0 -3
  86. wandb/sdk/launch/builder/abstract.py +67 -0
  87. wandb/sdk/launch/builder/build.py +165 -576
  88. wandb/sdk/launch/builder/context_manager.py +235 -0
  89. wandb/sdk/launch/builder/docker_builder.py +7 -23
  90. wandb/sdk/launch/builder/kaniko_builder.py +10 -23
  91. wandb/sdk/launch/builder/templates/dockerfile.py +92 -0
  92. wandb/sdk/launch/create_job.py +51 -45
  93. wandb/sdk/launch/environment/aws_environment.py +26 -1
  94. wandb/sdk/launch/inputs/files.py +148 -0
  95. wandb/sdk/launch/inputs/internal.py +224 -0
  96. wandb/sdk/launch/inputs/manage.py +95 -0
  97. wandb/sdk/launch/runner/abstract.py +2 -2
  98. wandb/sdk/launch/runner/kubernetes_monitor.py +45 -12
  99. wandb/sdk/launch/runner/kubernetes_runner.py +6 -8
  100. wandb/sdk/launch/runner/local_container.py +2 -3
  101. wandb/sdk/launch/runner/local_process.py +8 -29
  102. wandb/sdk/launch/runner/sagemaker_runner.py +20 -14
  103. wandb/sdk/launch/runner/vertex_runner.py +8 -7
  104. wandb/sdk/launch/sweeps/scheduler.py +2 -0
  105. wandb/sdk/launch/sweeps/utils.py +2 -2
  106. wandb/sdk/launch/utils.py +16 -138
  107. wandb/sdk/lib/_settings_toposort_generated.py +2 -5
  108. wandb/sdk/lib/apikey.py +4 -2
  109. wandb/sdk/lib/config_util.py +3 -3
  110. wandb/sdk/lib/proto_util.py +22 -1
  111. wandb/sdk/lib/redirect.py +1 -1
  112. wandb/sdk/service/service.py +2 -1
  113. wandb/sdk/service/streams.py +5 -5
  114. wandb/sdk/wandb_init.py +25 -59
  115. wandb/sdk/wandb_login.py +28 -25
  116. wandb/sdk/wandb_run.py +135 -70
  117. wandb/sdk/wandb_settings.py +33 -64
  118. wandb/sdk/wandb_watch.py +1 -1
  119. wandb/sklearn/plot/classifier.py +4 -6
  120. wandb/sync/sync.py +2 -2
  121. wandb/testing/relay.py +32 -17
  122. wandb/util.py +39 -37
  123. wandb/wandb_agent.py +3 -3
  124. wandb/wandb_controller.py +3 -2
  125. {wandb-0.17.0rc2.dist-info → wandb-0.17.2.dist-info}/METADATA +7 -9
  126. {wandb-0.17.0rc2.dist-info → wandb-0.17.2.dist-info}/RECORD +129 -151
  127. {wandb-0.17.0rc2.dist-info → wandb-0.17.2.dist-info}/WHEEL +1 -1
  128. wandb/apis/reports/v1/_blocks.py +0 -1406
  129. wandb/apis/reports/v1/_helpers.py +0 -70
  130. wandb/apis/reports/v1/_panels.py +0 -1282
  131. wandb/apis/reports/v1/_templates.py +0 -478
  132. wandb/apis/reports/v1/blocks.py +0 -27
  133. wandb/apis/reports/v1/helpers.py +0 -2
  134. wandb/apis/reports/v1/mutations.py +0 -66
  135. wandb/apis/reports/v1/panels.py +0 -17
  136. wandb/apis/reports/v1/report.py +0 -268
  137. wandb/apis/reports/v1/runset.py +0 -144
  138. wandb/apis/reports/v1/templates.py +0 -7
  139. wandb/apis/reports/v1/util.py +0 -406
  140. wandb/apis/reports/v1/validators.py +0 -131
  141. wandb/apis/reports/v2/blocks.py +0 -25
  142. wandb/apis/reports/v2/expr_parsing.py +0 -257
  143. wandb/apis/reports/v2/gql.py +0 -68
  144. wandb/apis/reports/v2/interface.py +0 -1911
  145. wandb/apis/reports/v2/internal.py +0 -867
  146. wandb/apis/reports/v2/metrics.py +0 -6
  147. wandb/apis/reports/v2/panels.py +0 -15
  148. wandb/catboost/__init__.py +0 -9
  149. wandb/fastai/__init__.py +0 -9
  150. wandb/keras/__init__.py +0 -19
  151. wandb/lightgbm/__init__.py +0 -9
  152. wandb/plots/__init__.py +0 -6
  153. wandb/plots/explain_text.py +0 -36
  154. wandb/plots/heatmap.py +0 -81
  155. wandb/plots/named_entity.py +0 -43
  156. wandb/plots/part_of_speech.py +0 -50
  157. wandb/plots/plot_definitions.py +0 -768
  158. wandb/plots/precision_recall.py +0 -121
  159. wandb/plots/roc.py +0 -103
  160. wandb/sacred/__init__.py +0 -3
  161. wandb/xgboost/__init__.py +0 -9
  162. {wandb-0.17.0rc2.dist-info → wandb-0.17.2.dist-info}/entry_points.txt +0 -0
  163. {wandb-0.17.0rc2.dist-info → wandb-0.17.2.dist-info}/licenses/LICENSE +0 -0
@@ -6,3 +6,5 @@ if protobuf_version == "3":
6
6
  from wandb.proto.v3.wandb_base_pb2 import *
7
7
  elif protobuf_version == "4":
8
8
  from wandb.proto.v4.wandb_base_pb2 import *
9
+ elif protobuf_version == "5":
10
+ from wandb.proto.v5.wandb_base_pb2 import *
@@ -20,7 +20,11 @@ DEPRECATED_FEATURES = Literal[
20
20
  "init__config_include_keys",
21
21
  "init__config_exclude_keys",
22
22
  "keras_callback__save_model",
23
- "langchain_tracer"
23
+ "langchain_tracer",
24
+ "artifact__get_path",
25
+ "artifactmanifestentry__name",
26
+ "api__artifact_versions",
27
+ "artifact_collection__change_type",
24
28
  ]
25
29
 
26
30
 
@@ -35,3 +39,7 @@ class Deprecated:
35
39
  init__config_exclude_keys: DEPRECATED_FEATURES = "init__config_exclude_keys"
36
40
  keras_callback__save_model: DEPRECATED_FEATURES = "keras_callback__save_model"
37
41
  langchain_tracer: DEPRECATED_FEATURES = "langchain_tracer"
42
+ artifact__get_path: DEPRECATED_FEATURES = "artifact__get_path"
43
+ artifactmanifestentry__name: DEPRECATED_FEATURES = "artifactmanifestentry__name"
44
+ api__artifact_versions: DEPRECATED_FEATURES = "api__artifact_versions"
45
+ artifact_collection__change_type: DEPRECATED_FEATURES = "artifact_collection__change_type"
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env python
2
+
3
+
4
+ def generate_deprecated_class_definition() -> None:
5
+ """Generate a class definition listing the deprecated features.
6
+ This is to allow static checks to ensure that proper field names are used.
7
+ """
8
+ from wandb.proto.wandb_telemetry_pb2 import Deprecated # type: ignore[import]
9
+
10
+ deprecated_features = Deprecated.DESCRIPTOR.fields_by_name.keys()
11
+
12
+ code: str = (
13
+ "# Generated by wandb/proto/wandb_internal_codegen.py. DO NOT EDIT!\n\n\n"
14
+ "import sys\n\n\n"
15
+ "if sys.version_info >= (3, 8):\n"
16
+ " from typing import Literal\n"
17
+ "else:\n"
18
+ " from typing_extensions import Literal\n\n\n"
19
+ "DEPRECATED_FEATURES = Literal[\n"
20
+ + ",\n".join(f' "{feature}"' for feature in deprecated_features)
21
+ + ",\n"
22
+ + "]\n\n\n"
23
+ "class Deprecated:\n"
24
+ + "".join(
25
+ [
26
+ f' {feature}: DEPRECATED_FEATURES = "{feature}"\n'
27
+ for feature in deprecated_features
28
+ ]
29
+ )
30
+ )
31
+ with open("wandb_deprecated.py", "w") as f:
32
+ f.write(code)
33
+
34
+ generate_deprecated_class_definition()
@@ -1,46 +1,14 @@
1
1
  #!/usr/bin/env python
2
2
 
3
+ import importlib.metadata
3
4
  import os
4
5
  import pathlib
5
6
 
6
7
  import grpc_tools # type: ignore
7
8
  from grpc_tools import protoc # type: ignore
8
- import importlib.metadata
9
9
  from packaging import version
10
10
 
11
11
 
12
- def generate_deprecated_class_definition() -> None:
13
- """
14
- Generate a class definition listing the deprecated features.
15
- This is to allow static checks to ensure that proper field names are used.
16
- """
17
- from wandb.proto.wandb_telemetry_pb2 import Deprecated # type: ignore[import]
18
-
19
- deprecated_features = Deprecated.DESCRIPTOR.fields_by_name.keys()
20
-
21
- code: str = (
22
- "# Generated by wandb/proto/wandb_internal_codegen.py. DO NOT EDIT!\n\n\n"
23
- "import sys\n\n\n"
24
- "if sys.version_info >= (3, 8):\n"
25
- " from typing import Literal\n"
26
- "else:\n"
27
- " from typing_extensions import Literal\n\n\n"
28
- "DEPRECATED_FEATURES = Literal[\n"
29
- + ",\n".join(f' "{feature}"' for feature in deprecated_features)
30
- + "\n"
31
- + "]\n\n\n"
32
- "class Deprecated:\n"
33
- + "".join(
34
- [
35
- f' {feature}: DEPRECATED_FEATURES = "{feature}"\n'
36
- for feature in deprecated_features
37
- ]
38
- )
39
- )
40
- with open("wandb/proto/wandb_deprecated.py", "w") as f:
41
- f.write(code)
42
-
43
-
44
12
  def get_pip_package_version(package_name: str) -> str:
45
13
  try:
46
14
  return importlib.metadata.version(package_name)
@@ -79,5 +47,3 @@ for p in (tmp_out / "wandb" / "proto").glob("*pb2*"):
79
47
  p.rename(tmp_out / p.name)
80
48
  os.rmdir(tmp_out / "wandb" / "proto")
81
49
  os.rmdir(tmp_out / "wandb")
82
-
83
- generate_deprecated_class_definition()
@@ -6,3 +6,5 @@ if protobuf_version == "3":
6
6
  from wandb.proto.v3.wandb_internal_pb2 import *
7
7
  elif protobuf_version == "4":
8
8
  from wandb.proto.v4.wandb_internal_pb2 import *
9
+ elif protobuf_version == "5":
10
+ from wandb.proto.v5.wandb_internal_pb2 import *
@@ -6,3 +6,5 @@ if protobuf_version == "3":
6
6
  from wandb.proto.v3.wandb_server_pb2 import *
7
7
  elif protobuf_version == "4":
8
8
  from wandb.proto.v4.wandb_server_pb2 import *
9
+ elif protobuf_version == "5":
10
+ from wandb.proto.v5.wandb_server_pb2 import *
@@ -6,3 +6,5 @@ if protobuf_version == "3":
6
6
  from wandb.proto.v3.wandb_settings_pb2 import *
7
7
  elif protobuf_version == "4":
8
8
  from wandb.proto.v4.wandb_settings_pb2 import *
9
+ elif protobuf_version == "5":
10
+ from wandb.proto.v5.wandb_settings_pb2 import *
@@ -6,3 +6,5 @@ if protobuf_version == "3":
6
6
  from wandb.proto.v3.wandb_telemetry_pb2 import *
7
7
  elif protobuf_version == "4":
8
8
  from wandb.proto.v4.wandb_telemetry_pb2 import *
9
+ elif protobuf_version == "5":
10
+ from wandb.proto.v5.wandb_telemetry_pb2 import *
@@ -70,6 +70,7 @@ from wandb.sdk.data_types._dtypes import TypeRegistry
70
70
  from wandb.sdk.internal.internal_api import Api as InternalApi
71
71
  from wandb.sdk.internal.thread_local_settings import _thread_local_api_settings
72
72
  from wandb.sdk.lib import filesystem, retry, runid, telemetry
73
+ from wandb.sdk.lib.deprecate import Deprecated, deprecate
73
74
  from wandb.sdk.lib.hashutil import B64MD5, b64_to_hex_id, md5_file_b64
74
75
  from wandb.sdk.lib.mailbox import Mailbox
75
76
  from wandb.sdk.lib.paths import FilePathStr, LogicalPath, StrPath, URIStr
@@ -262,11 +263,12 @@ class Artifact:
262
263
  "name": name,
263
264
  },
264
265
  )
265
- attrs = response.get("project", {}).get("artifact")
266
- if attrs is None:
267
- raise ValueError(
268
- f"Unable to fetch artifact with name {entity}/{project}/{name}"
269
- )
266
+ project_attrs = response.get("project")
267
+ if not project_attrs:
268
+ raise ValueError(f"project '{project}' not found under entity '{entity}'")
269
+ attrs = project_attrs.get("artifact")
270
+ if not attrs:
271
+ raise ValueError(f"artifact '{name}' not found in '{entity}/{project}'")
270
272
  return cls._from_attrs(entity, project, name, attrs, client)
271
273
 
272
274
  @classmethod
@@ -425,7 +427,7 @@ class Artifact:
425
427
 
426
428
  A collection is an ordered group of artifact versions.
427
429
  If this artifact was retrieved from a portfolio / linked collection, that
428
- collection will be returned rather than the the collection
430
+ collection will be returned rather than the collection
429
431
  that an artifact version originated from. The collection
430
432
  that an artifact originates from is known as the source sequence.
431
433
  """
@@ -1180,7 +1182,7 @@ class Artifact:
1180
1182
  """
1181
1183
  self._ensure_can_add()
1182
1184
  if not os.path.isfile(local_path):
1183
- raise ValueError("Path is not a file: %s" % local_path)
1185
+ raise ValueError("Path is not a file: {}".format(local_path))
1184
1186
 
1185
1187
  name = LogicalPath(name or os.path.basename(local_path))
1186
1188
  digest = md5_file_b64(local_path)
@@ -1221,11 +1223,12 @@ class Artifact:
1221
1223
  """
1222
1224
  self._ensure_can_add()
1223
1225
  if not os.path.isdir(local_path):
1224
- raise ValueError("Path is not a directory: %s" % local_path)
1226
+ raise ValueError("Path is not a directory: {}".format(local_path))
1225
1227
 
1226
1228
  termlog(
1227
- "Adding directory to artifact (%s)... "
1228
- % os.path.join(".", os.path.normpath(local_path)),
1229
+ "Adding directory to artifact ({})... ".format(
1230
+ os.path.join(".", os.path.normpath(local_path))
1231
+ ),
1229
1232
  newline=False,
1230
1233
  )
1231
1234
  start_time = time.time()
@@ -1503,8 +1506,9 @@ class Artifact:
1503
1506
 
1504
1507
  def get_path(self, name: StrPath) -> ArtifactManifestEntry:
1505
1508
  """Deprecated. Use `get_entry(name)`."""
1506
- termwarn(
1507
- "Artifact.get_path(name) is deprecated, use Artifact.get_entry(name) instead."
1509
+ deprecate(
1510
+ field_name=Deprecated.artifact__get_path,
1511
+ warning_message="Artifact.get_path(name) is deprecated, use Artifact.get_entry(name) instead.",
1508
1512
  )
1509
1513
  return self.get_entry(name)
1510
1514
 
@@ -1526,7 +1530,7 @@ class Artifact:
1526
1530
  name = LogicalPath(name)
1527
1531
  entry = self.manifest.entries.get(name) or self._get_obj_entry(name)[0]
1528
1532
  if entry is None:
1529
- raise KeyError("Path not contained in artifact: %s" % name)
1533
+ raise KeyError("Path not contained in artifact: {}".format(name))
1530
1534
  entry._parent_artifact = self
1531
1535
  return entry
1532
1536
 
@@ -1937,11 +1941,11 @@ class Artifact:
1937
1941
  for entry in self.manifest.entries.values():
1938
1942
  if entry.ref is None:
1939
1943
  if md5_file_b64(os.path.join(root, entry.path)) != entry.digest:
1940
- raise ValueError("Digest mismatch for file: %s" % entry.path)
1944
+ raise ValueError("Digest mismatch for file: {}".format(entry.path))
1941
1945
  else:
1942
1946
  ref_count += 1
1943
1947
  if ref_count > 0:
1944
- print("Warning: skipped verification of %s refs" % ref_count)
1948
+ print("Warning: skipped verification of {} refs".format(ref_count))
1945
1949
 
1946
1950
  def file(self, root: Optional[str] = None) -> StrPath:
1947
1951
  """Download a single file artifact to the directory you specify with `root`.
@@ -2015,16 +2019,23 @@ class Artifact:
2015
2019
  def delete(self, delete_aliases: bool = False) -> None:
2016
2020
  """Delete an artifact and its files.
2017
2021
 
2022
+ If called on a linked artifact (i.e. a member of a portfolio collection): only the link is deleted, and the
2023
+ source artifact is unaffected.
2024
+
2018
2025
  Arguments:
2019
2026
  delete_aliases: If set to `True`, deletes all aliases associated with the artifact.
2020
2027
  Otherwise, this raises an exception if the artifact has existing
2021
2028
  aliases.
2029
+ This parameter is ignored if the artifact is linked (i.e. a member of a portfolio collection).
2022
2030
 
2023
2031
  Raises:
2024
2032
  ArtifactNotLoggedError: If the artifact is not logged.
2025
2033
  """
2026
2034
  self._ensure_logged("delete")
2027
- self._delete(delete_aliases)
2035
+ if self.collection.is_sequence():
2036
+ self._delete(delete_aliases)
2037
+ else:
2038
+ self._unlink()
2028
2039
 
2029
2040
  @normalize_exceptions
2030
2041
  def _delete(self, delete_aliases: bool = False) -> None:
@@ -2057,13 +2068,13 @@ class Artifact:
2057
2068
 
2058
2069
  Arguments:
2059
2070
  target_path: The path to the portfolio inside a project.
2060
- The target path must adhere to one of the following
2061
- schemas `{portfolio}`, `{project}/{portfolio}` or
2062
- `{entity}/{project}/{portfolio}`.
2063
- To link the artifact to the Model Registry, rather than to a generic
2064
- portfolio inside a project, set `target_path` to the following
2065
- schema `{"model-registry"}/{Registered Model Name}` or
2066
- `{entity}/{"model-registry"}/{Registered Model Name}`.
2071
+ The target path must adhere to one of the following
2072
+ schemas `{portfolio}`, `{project}/{portfolio}` or
2073
+ `{entity}/{project}/{portfolio}`.
2074
+ To link the artifact to the Model Registry, rather than to a generic
2075
+ portfolio inside a project, set `target_path` to the following
2076
+ schema `{"model-registry"}/{Registered Model Name}` or
2077
+ `{entity}/{"model-registry"}/{Registered Model Name}`.
2067
2078
  aliases: A list of strings that uniquely identifies the artifact inside the
2068
2079
  specified portfolio.
2069
2080
 
@@ -2081,6 +2092,48 @@ class Artifact:
2081
2092
  else:
2082
2093
  wandb.run.link_artifact(self, target_path, aliases)
2083
2094
 
2095
+ def unlink(self) -> None:
2096
+ """Unlink this artifact if it is currently a member of a portfolio (a promoted collection of artifacts).
2097
+
2098
+ Raises:
2099
+ ArtifactNotLoggedError: If the artifact is not logged.
2100
+ ValueError: If the artifact is not linked, i.e. it is not a member of a portfolio collection.
2101
+ """
2102
+ self._ensure_logged("unlink")
2103
+
2104
+ # Fail early if this isn't a linked artifact to begin with
2105
+ if self.collection.is_sequence():
2106
+ raise ValueError(
2107
+ f"Artifact {self.qualified_name!r} is not a linked artifact and cannot be unlinked. "
2108
+ f"To delete it, use {self.delete.__qualname__!r} instead."
2109
+ )
2110
+
2111
+ self._unlink()
2112
+
2113
+ @normalize_exceptions
2114
+ def _unlink(self) -> None:
2115
+ mutation = gql(
2116
+ """
2117
+ mutation UnlinkArtifact($artifactID: ID!, $artifactPortfolioID: ID!) {
2118
+ unlinkArtifact(
2119
+ input: { artifactID: $artifactID, artifactPortfolioID: $artifactPortfolioID }
2120
+ ) {
2121
+ artifactID
2122
+ success
2123
+ clientMutationId
2124
+ }
2125
+ }
2126
+ """
2127
+ )
2128
+ assert self._client is not None
2129
+ self._client.execute(
2130
+ mutation,
2131
+ variable_values={
2132
+ "artifactID": self.id,
2133
+ "artifactPortfolioID": self.collection.id,
2134
+ },
2135
+ )
2136
+
2084
2137
  def used_by(self) -> List[Run]:
2085
2138
  """Get a list of the runs that have used this artifact.
2086
2139
 
@@ -51,7 +51,7 @@ class ArtifactManifest:
51
51
  entry.path in self.entries
52
52
  and entry.digest != self.entries[entry.path].digest
53
53
  ):
54
- raise ValueError("Cannot add the same path twice: %s" % entry.path)
54
+ raise ValueError("Cannot add the same path twice: {}".format(entry.path))
55
55
  self.entries[entry.path] = entry
56
56
 
57
57
  def remove_entry(self, entry: "ArtifactManifestEntry") -> None:
@@ -6,8 +6,8 @@ from pathlib import Path
6
6
  from typing import TYPE_CHECKING, Dict, Optional, Union
7
7
  from urllib.parse import urlparse
8
8
 
9
- from wandb.errors.term import termwarn
10
9
  from wandb.sdk.lib import filesystem
10
+ from wandb.sdk.lib.deprecate import Deprecated, deprecate
11
11
  from wandb.sdk.lib.hashutil import (
12
12
  B64MD5,
13
13
  ETag,
@@ -94,8 +94,11 @@ class ArtifactManifestEntry:
94
94
 
95
95
  @property
96
96
  def name(self) -> LogicalPath:
97
- # TODO(hugh): add telemetry to see if anyone is still using this.
98
- termwarn("ArtifactManifestEntry.name is deprecated, use .path instead")
97
+ """Deprecated; use `path` instead."""
98
+ deprecate(
99
+ field_name=Deprecated.artifactmanifestentry__name,
100
+ warning_message="ArtifactManifestEntry.name is deprecated, use .path instead.",
101
+ )
99
102
  return self.path
100
103
 
101
104
  def parent_artifact(self) -> "Artifact":
@@ -20,7 +20,7 @@ class ArtifactManifestV1(ArtifactManifest):
20
20
  ) -> "ArtifactManifestV1":
21
21
  if manifest_json["version"] != cls.version():
22
22
  raise ValueError(
23
- "Expected manifest version 1, got %s" % manifest_json["version"]
23
+ "Expected manifest version 1, got {}".format(manifest_json["version"])
24
24
  )
25
25
 
26
26
  storage_policy_name = manifest_json["storagePolicy"]
@@ -173,16 +173,7 @@ class ArtifactSaver:
173
173
  self._file_pusher.store_manifest_files(
174
174
  self._manifest,
175
175
  artifact_id,
176
- lambda entry,
177
- progress_callback: self._manifest.storage_policy.store_file_sync(
178
- artifact_id,
179
- artifact_manifest_id,
180
- entry,
181
- step_prepare,
182
- progress_callback=progress_callback,
183
- ),
184
- lambda entry,
185
- progress_callback: self._manifest.storage_policy.store_file_async(
176
+ lambda entry, progress_callback: self._manifest.storage_policy.store_file(
186
177
  artifact_id,
187
178
  artifact_manifest_id,
188
179
  entry,
@@ -43,7 +43,9 @@ class LocalFileHandler(StorageHandler):
43
43
  local_path = util.local_file_uri_to_path(str(manifest_entry.ref))
44
44
  if not os.path.exists(local_path):
45
45
  raise ValueError(
46
- "Local file reference: Failed to find file at path %s" % local_path
46
+ "Local file reference: Failed to find file at path {}".format(
47
+ local_path
48
+ )
47
49
  )
48
50
 
49
51
  path, hit, cache_open = self._cache.check_md5_obj_path(
@@ -131,5 +133,7 @@ class LocalFileHandler(StorageHandler):
131
133
  entries.append(entry)
132
134
  else:
133
135
  # TODO: update error message if we don't allow directories.
134
- raise ValueError('Path "%s" must be a valid file or directory path' % path)
136
+ raise ValueError(
137
+ 'Path "{}" must be a valid file or directory path'.format(path)
138
+ )
135
139
  return entries
@@ -29,7 +29,7 @@ class MultiHandler(StorageHandler):
29
29
  return handler
30
30
  if self._default_handler is not None:
31
31
  return self._default_handler
32
- raise ValueError('No storage handler registered for url "%s"' % str(url))
32
+ raise ValueError('No storage handler registered for url "{}"'.format(str(url)))
33
33
 
34
34
  def load_path(
35
35
  self,
@@ -57,12 +57,14 @@ class TrackingHandler(StorageHandler):
57
57
  url = urlparse(path)
58
58
  if name is None:
59
59
  raise ValueError(
60
- 'You must pass name="<entry_name>" when tracking references with unknown schemes. ref: %s'
61
- % path
60
+ 'You must pass name="<entry_name>" when tracking references with unknown schemes. ref: {}'.format(
61
+ path
62
+ )
62
63
  )
63
64
  termwarn(
64
- "Artifact references with unsupported schemes cannot be checksummed: %s"
65
- % path
65
+ "Artifact references with unsupported schemes cannot be checksummed: {}".format(
66
+ path
67
+ )
66
68
  )
67
69
  name = name or url.path[1:] # strip leading slash
68
70
  return [ArtifactManifestEntry(path=name, ref=path, digest=path)]
@@ -263,7 +263,7 @@ class WandbStoragePolicy(StoragePolicy):
263
263
  return math.ceil(file_size / S3_MAX_PART_NUMBERS)
264
264
  return default_chunk_size
265
265
 
266
- def store_file_sync(
266
+ def store_file(
267
267
  self,
268
268
  artifact_id: str,
269
269
  artifact_manifest_id: str,
@@ -301,7 +301,7 @@ class WandbStoragePolicy(StoragePolicy):
301
301
  hex_digests[part_number] = hex_digest
302
302
  part_number += 1
303
303
 
304
- resp = preparer.prepare_sync(
304
+ resp = preparer.prepare(
305
305
  {
306
306
  "artifactID": artifact_id,
307
307
  "artifactManifestID": artifact_manifest_id,
@@ -347,46 +347,6 @@ class WandbStoragePolicy(StoragePolicy):
347
347
 
348
348
  return False
349
349
 
350
- async def store_file_async(
351
- self,
352
- artifact_id: str,
353
- artifact_manifest_id: str,
354
- entry: "ArtifactManifestEntry",
355
- preparer: "StepPrepare",
356
- progress_callback: Optional["progress.ProgressFn"] = None,
357
- ) -> bool:
358
- """Async equivalent to `store_file_sync`."""
359
- resp = await preparer.prepare_async(
360
- {
361
- "artifactID": artifact_id,
362
- "artifactManifestID": artifact_manifest_id,
363
- "name": entry.path,
364
- "md5": entry.digest,
365
- }
366
- )
367
-
368
- entry.birth_artifact_id = resp.birth_artifact_id
369
- if resp.upload_url is None:
370
- return True
371
- if entry.local_path is None:
372
- return False
373
-
374
- with open(entry.local_path, "rb") as file:
375
- # This fails if we don't send the first byte before the signed URL expires.
376
- await self._api.upload_file_retry_async(
377
- resp.upload_url,
378
- file,
379
- progress_callback,
380
- extra_headers={
381
- header.split(":", 1)[0]: header.split(":", 1)[1]
382
- for header in (resp.upload_headers or {})
383
- },
384
- )
385
-
386
- self._write_cache(entry)
387
-
388
- return False
389
-
390
350
  def _write_cache(self, entry: "ArtifactManifestEntry") -> None:
391
351
  if entry.local_path is None:
392
352
  return
@@ -43,7 +43,7 @@ class StoragePolicy:
43
43
  ) -> FilePathStr:
44
44
  raise NotImplementedError
45
45
 
46
- def store_file_sync(
46
+ def store_file(
47
47
  self,
48
48
  artifact_id: str,
49
49
  artifact_manifest_id: str,
@@ -53,17 +53,6 @@ class StoragePolicy:
53
53
  ) -> bool:
54
54
  raise NotImplementedError
55
55
 
56
- async def store_file_async(
57
- self,
58
- artifact_id: str,
59
- artifact_manifest_id: str,
60
- entry: "ArtifactManifestEntry",
61
- preparer: "StepPrepare",
62
- progress_callback: Optional["ProgressFn"] = None,
63
- ) -> bool:
64
- """Async equivalent to `store_file_sync`."""
65
- raise NotImplementedError
66
-
67
56
  def store_reference(
68
57
  self,
69
58
  artifact: "Artifact",
@@ -390,10 +390,13 @@ if np:
390
390
  NumberType.types.append(np.uintp)
391
391
  NumberType.types.append(np.float32)
392
392
  NumberType.types.append(np.float64)
393
- NumberType.types.append(np.float_)
394
393
  NumberType.types.append(np.complex64)
395
394
  NumberType.types.append(np.complex128)
396
- NumberType.types.append(np.complex_)
395
+
396
+ numpy_major_version = np.__version__.split(".")[0]
397
+ if int(numpy_major_version) < 2:
398
+ NumberType.types.append(np.float_)
399
+ NumberType.types.append(np.complex_)
397
400
 
398
401
 
399
402
  class TimestampType(Type):
@@ -33,7 +33,7 @@ class Html(BatchableMedia):
33
33
  if data_is_path:
34
34
  assert isinstance(data, str)
35
35
  data_path = data
36
- with open(data_path) as file:
36
+ with open(data_path, encoding="utf-8") as file:
37
37
  self.html = file.read()
38
38
  elif isinstance(data, str):
39
39
  self.html = data
@@ -480,7 +480,7 @@ class Image(BatchableMedia):
480
480
  return "RGBA"
481
481
  else:
482
482
  raise ValueError(
483
- "Un-supported shape for image conversion %s" % list(data.shape)
483
+ "Un-supported shape for image conversion {}".format(list(data.shape))
484
484
  )
485
485
 
486
486
  @classmethod
@@ -36,7 +36,7 @@ if TYPE_CHECKING: # pragma: no cover
36
36
 
37
37
  from ..wandb_run import Run as LocalRun
38
38
 
39
- numeric = Union[int, float, np.integer, np.float_]
39
+ numeric = Union[int, float, np.integer, np.float64]
40
40
  FileFormat3D = Literal[
41
41
  "obj",
42
42
  "gltf",
@@ -96,7 +96,9 @@ class Video(BatchableMedia):
96
96
  self._channels = None
97
97
  self._caption = caption
98
98
  if self._format not in Video.EXTS:
99
- raise ValueError("wandb.Video accepts %s formats" % ", ".join(Video.EXTS))
99
+ raise ValueError(
100
+ "wandb.Video accepts {} formats".format(", ".join(Video.EXTS))
101
+ )
100
102
 
101
103
  if isinstance(data_or_path, BytesIO):
102
104
  filename = os.path.join(
@@ -110,7 +112,7 @@ class Video(BatchableMedia):
110
112
  ext = ext[1:].lower()
111
113
  if ext not in Video.EXTS:
112
114
  raise ValueError(
113
- "wandb.Video accepts %s formats" % ", ".join(Video.EXTS)
115
+ "wandb.Video accepts {} formats".format(", ".join(Video.EXTS))
114
116
  )
115
117
  self._set_file(data_or_path, is_tmp=False)
116
118
  # ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of csv=p=0 data_or_path
@@ -394,11 +394,23 @@ class InterfaceBase:
394
394
  source.artifact.artifact = info_source.get("artifact", "")
395
395
  source.artifact.entrypoint.extend(info_source.get("entrypoint", []))
396
396
  source.artifact.notebook = info_source.get("notebook", False)
397
+ build_context = info_source.get("build_context")
398
+ if build_context:
399
+ source.artifact.build_context = build_context
400
+ dockerfile = info_source.get("dockerfile")
401
+ if dockerfile:
402
+ source.artifact.dockerfile = dockerfile
397
403
  elif source_type == "repo":
398
404
  source.git.git_info.remote = metadata.get("git", {}).get("remote", "")
399
405
  source.git.git_info.commit = metadata.get("git", {}).get("commit", "")
400
406
  source.git.entrypoint.extend(metadata.get("entrypoint", []))
401
407
  source.git.notebook = metadata.get("notebook", False)
408
+ build_context = metadata.get("build_context")
409
+ if build_context:
410
+ source.git.build_context = build_context
411
+ dockerfile = metadata.get("dockerfile")
412
+ if dockerfile:
413
+ source.git.dockerfile = dockerfile
402
414
  elif source_type == "image":
403
415
  source.image.image = metadata.get("docker", "")
404
416
  else:
@@ -775,6 +787,7 @@ class InterfaceBase:
775
787
  source.file.CopyFrom(
776
788
  pb.JobInputSource.ConfigFileSource(path=file_path),
777
789
  )
790
+ request.input_source.CopyFrom(source)
778
791
 
779
792
  return self._publish_job_input(request)
780
793
 
@@ -321,7 +321,7 @@ class InterfaceShared(InterfaceBase):
321
321
  if result is None:
322
322
  # TODO: friendlier error message here
323
323
  raise wandb.Error(
324
- "Couldn't communicate with backend after %s seconds" % timeout
324
+ "Couldn't communicate with backend after {} seconds".format(timeout)
325
325
  )
326
326
  login_response = result.response.login_response
327
327
  assert login_response