wandb 0.17.6__py3-none-win32.whl → 0.17.7__py3-none-win32.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 (39) hide show
  1. wandb/__init__.py +3 -16
  2. wandb/agents/pyagent.py +1 -2
  3. wandb/bin/wandb-core +0 -0
  4. wandb/cli/cli.py +21 -0
  5. wandb/data_types.py +3 -3
  6. wandb/integration/kfp/wandb_logging.py +1 -1
  7. wandb/integration/lightning/fabric/logger.py +1 -1
  8. wandb/integration/openai/fine_tuning.py +13 -5
  9. wandb/integration/ultralytics/pose_utils.py +0 -1
  10. wandb/sdk/artifacts/artifact.py +1 -1
  11. wandb/sdk/data_types/_dtypes.py +5 -5
  12. wandb/sdk/data_types/base_types/media.py +3 -1
  13. wandb/sdk/data_types/helper_types/bounding_boxes_2d.py +3 -1
  14. wandb/sdk/data_types/helper_types/image_mask.py +3 -1
  15. wandb/sdk/data_types/image.py +3 -1
  16. wandb/sdk/data_types/saved_model.py +3 -1
  17. wandb/sdk/interface/interface.py +17 -16
  18. wandb/sdk/interface/interface_shared.py +6 -9
  19. wandb/sdk/internal/datastore.py +1 -1
  20. wandb/sdk/internal/handler.py +0 -2
  21. wandb/sdk/internal/internal.py +1 -1
  22. wandb/sdk/internal/job_builder.py +5 -2
  23. wandb/sdk/internal/tb_watcher.py +2 -2
  24. wandb/sdk/internal/update.py +2 -2
  25. wandb/sdk/launch/builder/kaniko_builder.py +13 -5
  26. wandb/sdk/launch/create_job.py +2 -0
  27. wandb/sdk/lib/apikey.py +1 -1
  28. wandb/sdk/service/streams.py +2 -4
  29. wandb/sdk/wandb_config.py +1 -1
  30. wandb/sdk/wandb_init.py +55 -7
  31. wandb/sdk/wandb_run.py +109 -68
  32. wandb/sdk/wandb_settings.py +1 -1
  33. wandb/sdk/wandb_setup.py +66 -3
  34. wandb/sdk/wandb_sweep.py +5 -2
  35. {wandb-0.17.6.dist-info → wandb-0.17.7.dist-info}/METADATA +1 -1
  36. {wandb-0.17.6.dist-info → wandb-0.17.7.dist-info}/RECORD +39 -39
  37. {wandb-0.17.6.dist-info → wandb-0.17.7.dist-info}/WHEEL +0 -0
  38. {wandb-0.17.6.dist-info → wandb-0.17.7.dist-info}/entry_points.txt +0 -0
  39. {wandb-0.17.6.dist-info → wandb-0.17.7.dist-info}/licenses/LICENSE +0 -0
wandb/__init__.py CHANGED
@@ -1,9 +1,6 @@
1
1
  """Use wandb to track machine learning work.
2
2
 
3
- The most commonly used functions/objects are:
4
- - wandb.init — initialize a new run at the top of your training script
5
- - wandb.config — track hyperparameters and metadata
6
- - wandb.log — log metrics and media over time within your training loop
3
+ Train and fine-tune models, manage models from experimentation to production.
7
4
 
8
5
  For guides and examples, see https://docs.wandb.ai.
9
6
 
@@ -11,11 +8,7 @@ For scripts and interactive notebooks, see https://github.com/wandb/examples.
11
8
 
12
9
  For reference documentation, see https://docs.wandb.com/ref/python.
13
10
  """
14
- __version__ = "0.17.6"
15
-
16
-
17
- # Used with pypi checks and other messages related to pip
18
- _wandb_module = "wandb"
11
+ __version__ = "0.17.7"
19
12
 
20
13
  from typing import Optional
21
14
 
@@ -118,13 +111,6 @@ def _assert_is_user_process():
118
111
  assert not _IS_INTERNAL_PROCESS
119
112
 
120
113
 
121
- # toplevel:
122
- # save()
123
- # restore()
124
- # login()
125
- # sweep()
126
- # agent()
127
-
128
114
  # globals
129
115
  Api = PublicApi
130
116
  api = InternalApi()
@@ -254,4 +240,5 @@ __all__ = (
254
240
  "log_model",
255
241
  "use_model",
256
242
  "link_model",
243
+ "define_metric",
257
244
  )
wandb/agents/pyagent.py CHANGED
@@ -14,7 +14,6 @@ import time
14
14
  import traceback
15
15
 
16
16
  import wandb
17
- from wandb import wandb_sdk
18
17
  from wandb.apis import InternalApi
19
18
  from wandb.sdk.launch.sweeps import utils as sweep_utils
20
19
 
@@ -298,7 +297,7 @@ class Agent:
298
297
  sweep_param_path, job.config
299
298
  )
300
299
  os.environ[wandb.env.SWEEP_ID] = self._sweep_id
301
- wandb_sdk.wandb_setup._setup(_reset=True)
300
+ wandb.sdk.wandb_setup._setup(_reset=True)
302
301
 
303
302
  wandb.termlog(f"Agent Starting Run: {run_id} with config:")
304
303
  for k, v in job.config.items():
wandb/bin/wandb-core CHANGED
Binary file
wandb/cli/cli.py CHANGED
@@ -1417,6 +1417,13 @@ def launch_sweep(
1417
1417
  help="""Specific docker image you'd like to use. In the form name:tag.
1418
1418
  If passed in, will override the docker image value passed in using a config file.""",
1419
1419
  )
1420
+ @click.option(
1421
+ "--base-image",
1422
+ "-B",
1423
+ default=None,
1424
+ metavar="BASE IMAGE",
1425
+ help="""Docker image to run job code in. Incompatible with --docker-image.""",
1426
+ )
1420
1427
  @click.option(
1421
1428
  "--config",
1422
1429
  "-c",
@@ -1508,6 +1515,7 @@ def launch(
1508
1515
  entity,
1509
1516
  project,
1510
1517
  docker_image,
1518
+ base_image,
1511
1519
  config,
1512
1520
  cli_template_vars,
1513
1521
  queue,
@@ -1604,6 +1612,7 @@ def launch(
1604
1612
  git_hash=git_version,
1605
1613
  name=job_name,
1606
1614
  project=project,
1615
+ base_image=base_image,
1607
1616
  build_context=build_context,
1608
1617
  dockerfile=dockerfile,
1609
1618
  entity=entity,
@@ -2001,6 +2010,12 @@ def describe(job):
2001
2010
  help="Path to the build context from the root of the job source code. If "
2002
2011
  "provided, this is used as the base path for the Dockerfile and entrypoint.",
2003
2012
  )
2013
+ @click.option(
2014
+ "--base-image",
2015
+ "-B",
2016
+ type=str,
2017
+ help="Base image to use for the job. Incompatible with image jobs.",
2018
+ )
2004
2019
  @click.option(
2005
2020
  "--dockerfile",
2006
2021
  "-D",
@@ -2025,6 +2040,7 @@ def create(
2025
2040
  git_hash,
2026
2041
  runtime,
2027
2042
  build_context,
2043
+ base_image,
2028
2044
  dockerfile,
2029
2045
  ):
2030
2046
  """Create a job from a source, without a wandb run.
@@ -2056,6 +2072,10 @@ def create(
2056
2072
  )
2057
2073
  entrypoint = "main.py"
2058
2074
 
2075
+ if job_type == "image" and base_image:
2076
+ wandb.termerror("Cannot provide --base-image/-B for an `image` job")
2077
+ return
2078
+
2059
2079
  artifact, action, aliases = _create_job(
2060
2080
  api=api,
2061
2081
  path=path,
@@ -2069,6 +2089,7 @@ def create(
2069
2089
  git_hash=git_hash,
2070
2090
  runtime=runtime,
2071
2091
  build_context=build_context,
2092
+ base_image=base_image,
2072
2093
  dockerfile=dockerfile,
2073
2094
  )
2074
2095
  if not artifact:
wandb/data_types.py CHANGED
@@ -104,7 +104,7 @@ class _TableIndex(int, _TableLinkMixin):
104
104
  def _json_helper(val, artifact):
105
105
  if isinstance(val, WBValue):
106
106
  return val.to_json(artifact)
107
- elif val.__class__ == dict:
107
+ elif val.__class__ is dict:
108
108
  res = {}
109
109
  for key in val:
110
110
  res[key] = _json_helper(val[key], artifact)
@@ -270,10 +270,10 @@ class Table(Media):
270
270
  if dtype is None:
271
271
  dtype = _dtypes.UnknownType()
272
272
 
273
- if optional.__class__ != list:
273
+ if optional.__class__ is not list:
274
274
  optional = [optional for _ in range(len(self.columns))]
275
275
 
276
- if dtype.__class__ != list:
276
+ if dtype.__class__ is not list:
277
277
  dtype = [dtype for _ in range(len(self.columns))]
278
278
 
279
279
  self._column_types = _dtypes.TypedDictType({})
@@ -30,7 +30,7 @@ def wandb_log( # noqa: C901
30
30
  def isinstance_namedtuple(x):
31
31
  t = type(x)
32
32
  b = t.__bases__
33
- if len(b) != 1 or b[0] != tuple:
33
+ if len(b) != 1 or b[0] is not tuple:
34
34
  return False
35
35
  f = getattr(t, "_fields", None)
36
36
  if not isinstance(f, tuple):
@@ -312,7 +312,7 @@ class WandbLogger(Logger):
312
312
  self._prefix = prefix
313
313
  self._experiment = experiment
314
314
  self._logged_model_time: Dict[str, float] = {}
315
- self._checkpoint_callback: Optional["ModelCheckpoint"] = None
315
+ self._checkpoint_callback: Optional[ModelCheckpoint] = None
316
316
 
317
317
  # paths are processed as strings
318
318
  if save_dir is not None:
@@ -1,3 +1,4 @@
1
+ import base64
1
2
  import datetime
2
3
  import io
3
4
  import json
@@ -16,13 +17,13 @@ from wandb.util import parse_version
16
17
 
17
18
  openai = util.get_module(
18
19
  name="openai",
19
- required="`openai` not installed. This integration requires `openai`. To fix, please `pip install openai`",
20
- lazy="False",
20
+ required="This integration requires `openai`. To install, please run `pip install openai`",
21
+ lazy=False,
21
22
  )
22
23
 
23
- if parse_version(openai.__version__) < parse_version("1.0.1"):
24
+ if parse_version(openai.__version__) < parse_version("1.12.0"):
24
25
  raise wandb.Error(
25
- f"This integration requires openai version 1.0.1 and above. Your current version is {openai.__version__} "
26
+ f"This integration requires openai version 1.12.0 and above. Your current version is {openai.__version__} "
26
27
  "To fix, please `pip install -U openai`"
27
28
  )
28
29
 
@@ -228,7 +229,14 @@ class WandbLogger:
228
229
  # check results are present
229
230
  try:
230
231
  results_id = fine_tune.result_files[0]
231
- results = cls.openai_client.files.content(file_id=results_id).text
232
+ try:
233
+ encoded_results = cls.openai_client.files.content(
234
+ file_id=results_id
235
+ ).read()
236
+ results = base64.b64decode(encoded_results).decode("utf-8")
237
+ except Exception:
238
+ # attempt to read as text, works for older jobs
239
+ results = cls.openai_client.files.content(file_id=results_id).text
232
240
  except openai.NotFoundError:
233
241
  if show_individual_warnings:
234
242
  wandb.termwarn(
@@ -23,7 +23,6 @@ def annotate_keypoint_results(result: Results, visualize_skeleton: bool):
23
23
 
24
24
 
25
25
  def annotate_keypoint_batch(image_path: str, keypoints: Any, visualize_skeleton: bool):
26
- original_image = None
27
26
  with Image.open(image_path) as original_image:
28
27
  original_image = np.ascontiguousarray(original_image)
29
28
  annotator = Annotator(original_image)
@@ -751,7 +751,7 @@ class Artifact:
751
751
  def save(
752
752
  self,
753
753
  project: Optional[str] = None,
754
- settings: Optional["wandb.wandb_sdk.wandb_settings.Settings"] = None,
754
+ settings: Optional["wandb.sdk.wandb_settings.Settings"] = None,
755
755
  ) -> None:
756
756
  """Persist any changes made to the artifact.
757
757
 
@@ -56,7 +56,7 @@ class TypeRegistry:
56
56
  # but will be ultimately treated as a None. Ignoring type since
57
57
  # mypy does not trust that py_obj is a float by the time it is
58
58
  # passed to isnan.
59
- if py_obj.__class__ == float and math.isnan(py_obj): # type: ignore
59
+ if py_obj.__class__ is float and math.isnan(py_obj): # type: ignore
60
60
  return NoneType()
61
61
 
62
62
  # TODO: generalize this to handle other config input types
@@ -134,7 +134,7 @@ def _params_obj_to_json_obj(
134
134
  artifact: t.Optional["Artifact"] = None,
135
135
  ) -> t.Any:
136
136
  """Helper method."""
137
- if params_obj.__class__ == dict:
137
+ if params_obj.__class__ is dict:
138
138
  return {
139
139
  key: _params_obj_to_json_obj(params_obj[key], artifact)
140
140
  for key in params_obj
@@ -151,7 +151,7 @@ def _json_obj_to_params_obj(
151
151
  json_obj: t.Any, artifact: t.Optional["Artifact"] = None
152
152
  ) -> t.Any:
153
153
  """Helper method."""
154
- if json_obj.__class__ == dict:
154
+ if json_obj.__class__ is dict:
155
155
  if "wb_type" in json_obj:
156
156
  return TypeRegistry.type_from_dict(json_obj, artifact)
157
157
  else:
@@ -159,7 +159,7 @@ def _json_obj_to_params_obj(
159
159
  key: _json_obj_to_params_obj(json_obj[key], artifact)
160
160
  for key in json_obj
161
161
  }
162
- elif json_obj.__class__ == list:
162
+ elif json_obj.__class__ is list:
163
163
  return [_json_obj_to_params_obj(item, artifact) for item in json_obj]
164
164
  else:
165
165
  return json_obj
@@ -533,7 +533,7 @@ class UnionType(Type):
533
533
  self,
534
534
  allowed_types: t.Optional[t.Sequence[ConvertibleToType]] = None,
535
535
  ):
536
- assert allowed_types is None or (allowed_types.__class__ == list)
536
+ assert allowed_types is None or (allowed_types.__class__ is list)
537
537
  if allowed_types is None:
538
538
  wb_types = []
539
539
  else:
@@ -159,9 +159,11 @@ class Media(WBValue):
159
159
  # into Media itself we should get rid of them
160
160
  from wandb import Image
161
161
  from wandb.data_types import Audio
162
+ from wandb.sdk.wandb_run import Run
162
163
 
163
164
  json_obj = {}
164
- if isinstance(run, wandb.wandb_sdk.wandb_run.Run):
165
+
166
+ if isinstance(run, Run):
165
167
  json_obj.update(
166
168
  {
167
169
  "_type": "file", # TODO(adrian): This isn't (yet) a real media type we support on the frontend.
@@ -276,7 +276,9 @@ class BoundingBoxes2D(JSONMetadata):
276
276
  return True
277
277
 
278
278
  def to_json(self, run_or_artifact: Union["LocalRun", "Artifact"]) -> dict:
279
- if isinstance(run_or_artifact, wandb.wandb_sdk.wandb_run.Run):
279
+ from wandb.sdk.wandb_run import Run
280
+
281
+ if isinstance(run_or_artifact, Run):
280
282
  return super().to_json(run_or_artifact)
281
283
  elif isinstance(run_or_artifact, wandb.Artifact):
282
284
  # TODO (tim): I would like to log out a proper dictionary representing this object, but don't
@@ -191,9 +191,11 @@ class ImageMask(Media):
191
191
  )
192
192
 
193
193
  def to_json(self, run_or_artifact: Union["LocalRun", "Artifact"]) -> dict:
194
+ from wandb.sdk.wandb_run import Run
195
+
194
196
  json_dict = super().to_json(run_or_artifact)
195
197
 
196
- if isinstance(run_or_artifact, wandb.wandb_sdk.wandb_run.Run):
198
+ if isinstance(run_or_artifact, Run):
197
199
  json_dict["_type"] = self.type_name()
198
200
  return json_dict
199
201
  elif isinstance(run_or_artifact, wandb.Artifact):
@@ -418,6 +418,8 @@ class Image(BatchableMedia):
418
418
  )
419
419
 
420
420
  def to_json(self, run_or_artifact: Union["LocalRun", "Artifact"]) -> dict:
421
+ from wandb.sdk.wandb_run import Run
422
+
421
423
  json_dict = super().to_json(run_or_artifact)
422
424
  json_dict["_type"] = Image._log_type
423
425
  json_dict["format"] = self.format
@@ -456,7 +458,7 @@ class Image(BatchableMedia):
456
458
  "digest": classes_entry.digest,
457
459
  }
458
460
 
459
- elif not isinstance(run_or_artifact, wandb.wandb_sdk.wandb_run.Run):
461
+ elif not isinstance(run_or_artifact, Run):
460
462
  raise ValueError("to_json accepts wandb_run.Run or wandb_artifact.Artifact")
461
463
 
462
464
  if self._boxes:
@@ -148,7 +148,9 @@ class _SavedModel(WBValue, Generic[SavedModelObjType]):
148
148
  # bit of tech debt in the other data types which requires the input to `to_json`
149
149
  # to accept a Run or Artifact. However, Run additions should be deprecated in the future.
150
150
  # This check helps ensure we do not add to the debt.
151
- if isinstance(run_or_artifact, wandb.wandb_sdk.wandb_run.Run):
151
+ from wandb.sdk.wandb_run import Run
152
+
153
+ if isinstance(run_or_artifact, Run):
152
154
  raise ValueError("SavedModel cannot be added to run - must use artifact")
153
155
  artifact = run_or_artifact
154
156
  json_obj = {
@@ -54,18 +54,20 @@ MANIFEST_FILE_SIZE_THRESHOLD = 100_000
54
54
 
55
55
  GlobStr = NewType("GlobStr", str)
56
56
 
57
- if TYPE_CHECKING:
58
- from ..wandb_run import Run
57
+ if sys.version_info >= (3, 8):
58
+ from typing import Literal, TypedDict
59
+ else:
60
+ from typing_extensions import Literal, TypedDict
61
+
62
+ PolicyName = Literal["now", "live", "end"]
59
63
 
60
- if sys.version_info >= (3, 8):
61
- from typing import Literal, TypedDict
62
- else:
63
- from typing_extensions import Literal, TypedDict
64
64
 
65
- PolicyName = Literal["now", "live", "end"]
65
+ class FilesDict(TypedDict):
66
+ files: Iterable[Tuple[GlobStr, PolicyName]]
66
67
 
67
- class FilesDict(TypedDict):
68
- files: Iterable[Tuple[GlobStr, PolicyName]]
68
+
69
+ if TYPE_CHECKING:
70
+ from ..wandb_run import Run
69
71
 
70
72
 
71
73
  logger = logging.getLogger("wandb")
@@ -112,15 +114,14 @@ class InterfaceBase:
112
114
  def _publish_header(self, header: pb.HeaderRecord) -> None:
113
115
  raise NotImplementedError
114
116
 
115
- def communicate_status(self) -> Optional[pb.StatusResponse]:
116
- status = pb.StatusRequest()
117
- resp = self._communicate_status(status)
118
- return resp
117
+ def deliver_status(self) -> MailboxHandle:
118
+ return self._deliver_status(pb.StatusRequest())
119
119
 
120
120
  @abstractmethod
121
- def _communicate_status(
122
- self, status: pb.StatusRequest
123
- ) -> Optional[pb.StatusResponse]:
121
+ def _deliver_status(
122
+ self,
123
+ status: pb.StatusRequest,
124
+ ) -> MailboxHandle:
124
125
  raise NotImplementedError
125
126
 
126
127
  def _make_config(
@@ -299,7 +299,7 @@ class InterfaceShared(InterfaceBase):
299
299
  raise NotImplementedError
300
300
 
301
301
  def _communicate(
302
- self, rec: pb.Record, timeout: Optional[int] = 5, local: Optional[bool] = None
302
+ self, rec: pb.Record, timeout: Optional[int] = 30, local: Optional[bool] = None
303
303
  ) -> Optional[pb.Result]:
304
304
  return self._communicate_async(rec, local=local).get(timeout=timeout)
305
305
 
@@ -421,15 +421,12 @@ class InterfaceShared(InterfaceBase):
421
421
  rec = self._make_record(alert=proto_alert)
422
422
  self._publish(rec)
423
423
 
424
- def _communicate_status(
425
- self, status: pb.StatusRequest
426
- ) -> Optional[pb.StatusResponse]:
424
+ def _deliver_status(
425
+ self,
426
+ status: pb.StatusRequest,
427
+ ) -> MailboxHandle:
427
428
  req = self._make_request(status=status)
428
- resp = self._communicate(req, local=True)
429
- if resp is None:
430
- return None
431
- assert resp.response.status_response
432
- return resp.response.status_response
429
+ return self._deliver_record(req)
433
430
 
434
431
  def _publish_exit(self, exit_data: pb.RunExitRecord) -> None:
435
432
  rec = self._make_record(exit=exit_data)
@@ -69,7 +69,7 @@ class DataStore:
69
69
 
70
70
  def __init__(self) -> None:
71
71
  self._opened_for_scan = False
72
- self._fp: Optional["IO[Any]"] = None
72
+ self._fp: Optional[IO[Any]] = None
73
73
  self._index = 0
74
74
  self._flush_offset = 0
75
75
  self._size_bytes = 0
@@ -745,8 +745,6 @@ class HandleManager:
745
745
  self._respond_result(result)
746
746
 
747
747
  def handle_request_status(self, record: Record) -> None:
748
- # TODO(mempressure): do something better?
749
- assert record.control.req_resp
750
748
  result = proto_util._result_from_record(record)
751
749
  self._respond_result(result)
752
750
 
@@ -62,7 +62,7 @@ def wandb_internal(
62
62
 
63
63
  """
64
64
  # mark this process as internal
65
- wandb._set_internal_process()
65
+ wandb._set_internal_process() # type: ignore
66
66
  _setup_tracelog()
67
67
  started = time.time()
68
68
 
@@ -423,15 +423,18 @@ class JobBuilder:
423
423
  api: Api,
424
424
  build_context: Optional[str] = None,
425
425
  dockerfile: Optional[str] = None,
426
+ base_image: Optional[str] = None,
426
427
  ) -> Optional[Artifact]:
427
428
  """Build a job artifact from the current run.
428
429
 
429
430
  Arguments:
431
+ api (Api): The API object to use to create the job artifact.
430
432
  build_context (Optional[str]): Path within the job source code to
431
433
  the image build context. Saved as part of the job for future
432
434
  builds.
433
435
  dockerfile (Optional[str]): Path within the build context the
434
436
  Dockerfile. Saved as part of the job for future builds.
437
+ base_image (Optional[str]): The base image used to run the job code.
435
438
 
436
439
  Returns:
437
440
  Optional[Artifact]: The job artifact if it was successfully built,
@@ -467,8 +470,6 @@ class JobBuilder:
467
470
  "warn",
468
471
  )
469
472
  return None
470
- metadata["dockerfile"] = dockerfile
471
- metadata["build_context"] = build_context
472
473
 
473
474
  runtime: Optional[str] = metadata.get("python")
474
475
  # can't build a job without a python version
@@ -520,6 +521,8 @@ class JobBuilder:
520
521
  source["build_context"] = build_context # type: ignore[typeddict-item]
521
522
  if dockerfile:
522
523
  source["dockerfile"] = dockerfile # type: ignore[typeddict-item]
524
+ if base_image:
525
+ source["base_image"] = base_image # type: ignore[typeddict-item]
523
526
 
524
527
  # Pop any keys that are initialized to None. The current TypedDict
525
528
  # system for source dicts requires all keys to be present, but we
@@ -123,7 +123,7 @@ class TBWatcher:
123
123
  self._force = force
124
124
  # TODO(jhr): do we need locking in this queue?
125
125
  self._watcher_queue = queue.PriorityQueue()
126
- wandb.tensorboard.reset_state()
126
+ wandb.tensorboard.reset_state() # type: ignore
127
127
 
128
128
  def _calculate_namespace(self, logdir: str, rootdir: str) -> Optional[str]:
129
129
  namespace: Optional[str]
@@ -430,7 +430,7 @@ class TBEventConsumer:
430
430
  def _handle_event(
431
431
  self, event: "ProtoEvent", history: Optional["TBHistory"] = None
432
432
  ) -> None:
433
- wandb.tensorboard._log(
433
+ wandb.tensorboard._log( # type: ignore
434
434
  event.event,
435
435
  step=event.event.step,
436
436
  namespace=event.namespace,
@@ -10,7 +10,7 @@ def _find_available(
10
10
  ) -> Optional[Tuple[str, bool, bool, bool, Optional[str]]]:
11
11
  from wandb.util import parse_version
12
12
 
13
- pypi_url = f"https://pypi.org/pypi/{wandb._wandb_module}/json"
13
+ pypi_url = "https://pypi.org/pypi/wandb/json"
14
14
 
15
15
  yanked_dict = {}
16
16
  try:
@@ -78,7 +78,7 @@ def check_available(current_version: str) -> Optional[Dict[str, Optional[str]]]:
78
78
  if not package_info:
79
79
  return None
80
80
 
81
- wandb_module_name = wandb._wandb_module
81
+ wandb_module_name = "wandb"
82
82
 
83
83
  latest_version, pip_prerelease, deleted, yanked, yanked_reason = package_info
84
84
  upgrade_message = (
@@ -63,6 +63,13 @@ else:
63
63
  NAMESPACE = "wandb"
64
64
 
65
65
 
66
+ def get_pod_name_safe(job: client.V1Job):
67
+ try:
68
+ return job.spec.template.metadata.name
69
+ except AttributeError:
70
+ return None
71
+
72
+
66
73
  async def _wait_for_completion(
67
74
  batch_client: client.BatchV1Api, job_name: str, deadline_secs: Optional[int] = None
68
75
  ) -> bool:
@@ -319,17 +326,18 @@ class KanikoBuilder(AbstractBuilder):
319
326
  await self._create_docker_ecr_config_map(
320
327
  build_job_name, core_v1, repo_uri
321
328
  )
322
- await batch_v1.create_namespaced_job(NAMESPACE, build_job)
323
-
329
+ k8s_job = await batch_v1.create_namespaced_job(NAMESPACE, build_job)
324
330
  # wait for double the job deadline since it might take time to schedule
325
331
  if not await _wait_for_completion(
326
332
  batch_v1, build_job_name, 3 * _DEFAULT_BUILD_TIMEOUT_SECS
327
333
  ):
328
334
  if job_tracker:
329
335
  job_tracker.set_err_stage("build")
330
- raise Exception(
331
- f"Failed to build image in kaniko for job {run_id}. View logs with `kubectl logs -n {NAMESPACE} {build_job_name}`."
332
- )
336
+ msg = f"Failed to build image in kaniko for job {run_id}."
337
+ pod_name = get_pod_name_safe(k8s_job)
338
+ if pod_name:
339
+ msg += f" View logs with `kubectl logs -n {NAMESPACE} {pod_name}`."
340
+ raise Exception(msg)
333
341
  try:
334
342
  pods_from_job = await core_v1.list_namespaced_pod(
335
343
  namespace=NAMESPACE, label_selector=f"job-name={build_job_name}"
@@ -114,6 +114,7 @@ def _create_job(
114
114
  git_hash: Optional[str] = None,
115
115
  build_context: Optional[str] = None,
116
116
  dockerfile: Optional[str] = None,
117
+ base_image: Optional[str] = None,
117
118
  ) -> Tuple[Optional[Artifact], str, List[str]]:
118
119
  wandb.termlog(f"Creating launch job of type: {job_type}...")
119
120
 
@@ -188,6 +189,7 @@ def _create_job(
188
189
  api.api,
189
190
  dockerfile=dockerfile,
190
191
  build_context=build_context,
192
+ base_image=base_image,
191
193
  )
192
194
  if not artifact:
193
195
  wandb.termerror("JobBuilder failed to build a job")
wandb/sdk/lib/apikey.py CHANGED
@@ -107,7 +107,7 @@ def prompt_api_key( # noqa: C901
107
107
 
108
108
  if jupyter and "google.colab" in sys.modules:
109
109
  log_string = term.LOG_STRING_NOCOLOR
110
- key = wandb.jupyter.attempt_colab_login(app_url)
110
+ key = wandb.jupyter.attempt_colab_login(app_url) # type: ignore
111
111
  if key is not None:
112
112
  write_key(settings, key, api=api)
113
113
  return key # type: ignore
@@ -81,9 +81,7 @@ class StreamRecord:
81
81
  self._wait_thread_active()
82
82
 
83
83
  def _wait_thread_active(self) -> None:
84
- result = self._iface.communicate_status()
85
- # TODO: using the default communicate timeout, is that enough? retries?
86
- assert result
84
+ self._iface.deliver_status().wait(timeout=-1)
87
85
 
88
86
  def join(self) -> None:
89
87
  self._iface.join()
@@ -212,7 +210,7 @@ class StreamMux:
212
210
  # run_id = action.stream_id # will want to fix if a streamid != runid
213
211
  settings = action._data
214
212
  thread = StreamThread(
215
- target=wandb.wandb_sdk.internal.internal.wandb_internal,
213
+ target=wandb.wandb_sdk.internal.internal.wandb_internal, # type: ignore
216
214
  kwargs=dict(
217
215
  settings=settings,
218
216
  record_q=stream._record_q,
wandb/sdk/wandb_config.py CHANGED
@@ -61,7 +61,7 @@ class Config:
61
61
 
62
62
  Using absl flags
63
63
  ```
64
- flags.DEFINE_string(model’, None, model to run) # name, default, help
64
+ flags.DEFINE_string("model", None, "model to run") # name, default, help
65
65
  wandb.config.update(flags.FLAGS) # adds all absl flags to config
66
66
  ```
67
67