wandb 0.19.1rc1__py3-none-win32.whl → 0.19.2__py3-none-win32.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. wandb/__init__.py +1 -7
  2. wandb/__init__.pyi +3 -5
  3. wandb/agents/pyagent.py +1 -1
  4. wandb/apis/importers/wandb.py +1 -1
  5. wandb/apis/public/files.py +1 -1
  6. wandb/apis/public/jobs.py +1 -1
  7. wandb/apis/public/runs.py +2 -7
  8. wandb/apis/reports/v1/__init__.py +1 -1
  9. wandb/apis/reports/v2/__init__.py +1 -1
  10. wandb/apis/workspaces/__init__.py +1 -1
  11. wandb/bin/gpu_stats.exe +0 -0
  12. wandb/bin/wandb-core +0 -0
  13. wandb/cli/beta.py +7 -4
  14. wandb/cli/cli.py +5 -7
  15. wandb/docker/__init__.py +4 -4
  16. wandb/integration/fastai/__init__.py +4 -6
  17. wandb/integration/keras/keras.py +5 -3
  18. wandb/integration/metaflow/metaflow.py +7 -7
  19. wandb/integration/prodigy/prodigy.py +3 -11
  20. wandb/integration/sagemaker/__init__.py +5 -3
  21. wandb/integration/sagemaker/config.py +17 -8
  22. wandb/integration/sagemaker/files.py +0 -1
  23. wandb/integration/sagemaker/resources.py +47 -18
  24. wandb/integration/torch/wandb_torch.py +1 -1
  25. wandb/proto/v3/wandb_internal_pb2.py +273 -235
  26. wandb/proto/v4/wandb_internal_pb2.py +222 -214
  27. wandb/proto/v5/wandb_internal_pb2.py +222 -214
  28. wandb/sdk/artifacts/artifact.py +3 -9
  29. wandb/sdk/backend/backend.py +1 -1
  30. wandb/sdk/data_types/base_types/wb_value.py +1 -1
  31. wandb/sdk/data_types/graph.py +2 -2
  32. wandb/sdk/data_types/saved_model.py +1 -1
  33. wandb/sdk/data_types/video.py +1 -1
  34. wandb/sdk/interface/interface.py +25 -25
  35. wandb/sdk/interface/interface_shared.py +21 -5
  36. wandb/sdk/internal/handler.py +19 -1
  37. wandb/sdk/internal/internal.py +1 -1
  38. wandb/sdk/internal/internal_api.py +4 -5
  39. wandb/sdk/internal/sample.py +2 -2
  40. wandb/sdk/internal/sender.py +1 -2
  41. wandb/sdk/internal/settings_static.py +3 -1
  42. wandb/sdk/internal/system/assets/disk.py +4 -4
  43. wandb/sdk/internal/system/assets/gpu.py +1 -1
  44. wandb/sdk/internal/system/assets/memory.py +1 -1
  45. wandb/sdk/internal/system/system_info.py +1 -1
  46. wandb/sdk/internal/system/system_monitor.py +3 -1
  47. wandb/sdk/internal/tb_watcher.py +1 -1
  48. wandb/sdk/launch/_project_spec.py +3 -3
  49. wandb/sdk/launch/builder/abstract.py +1 -1
  50. wandb/sdk/lib/apikey.py +2 -3
  51. wandb/sdk/lib/fsm.py +1 -1
  52. wandb/sdk/lib/gitlib.py +1 -1
  53. wandb/sdk/lib/gql_request.py +1 -1
  54. wandb/sdk/lib/interrupt.py +37 -0
  55. wandb/sdk/lib/lazyloader.py +1 -1
  56. wandb/sdk/lib/progress.py +7 -1
  57. wandb/sdk/lib/service_connection.py +1 -1
  58. wandb/sdk/lib/telemetry.py +1 -1
  59. wandb/sdk/service/_startup_debug.py +1 -1
  60. wandb/sdk/service/server_sock.py +3 -2
  61. wandb/sdk/service/service.py +1 -1
  62. wandb/sdk/service/streams.py +19 -17
  63. wandb/sdk/verify/verify.py +13 -13
  64. wandb/sdk/wandb_init.py +95 -104
  65. wandb/sdk/wandb_login.py +1 -1
  66. wandb/sdk/wandb_metadata.py +547 -0
  67. wandb/sdk/wandb_run.py +127 -35
  68. wandb/sdk/wandb_settings.py +6 -37
  69. wandb/sdk/wandb_setup.py +83 -82
  70. wandb/sdk/wandb_sweep.py +2 -2
  71. wandb/sdk/wandb_sync.py +15 -18
  72. wandb/sync/sync.py +10 -10
  73. wandb/util.py +11 -3
  74. wandb/wandb_agent.py +11 -16
  75. wandb/wandb_controller.py +7 -7
  76. {wandb-0.19.1rc1.dist-info → wandb-0.19.2.dist-info}/METADATA +3 -2
  77. {wandb-0.19.1rc1.dist-info → wandb-0.19.2.dist-info}/RECORD +80 -78
  78. {wandb-0.19.1rc1.dist-info → wandb-0.19.2.dist-info}/WHEEL +0 -0
  79. {wandb-0.19.1rc1.dist-info → wandb-0.19.2.dist-info}/entry_points.txt +0 -0
  80. {wandb-0.19.1rc1.dist-info → wandb-0.19.2.dist-info}/licenses/LICENSE +0 -0
wandb/sdk/wandb_run.py CHANGED
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import _thread as thread
4
3
  import atexit
5
4
  import functools
6
5
  import glob
@@ -37,6 +36,7 @@ from wandb.errors.links import url_registry
37
36
  from wandb.integration.torch import wandb_torch
38
37
  from wandb.plot import CustomChart, Visualize
39
38
  from wandb.proto.wandb_internal_pb2 import (
39
+ MetadataRequest,
40
40
  MetricRecord,
41
41
  PollExitResponse,
42
42
  Result,
@@ -73,6 +73,7 @@ from .lib import (
73
73
  deprecate,
74
74
  filenames,
75
75
  filesystem,
76
+ interrupt,
76
77
  ipython,
77
78
  module,
78
79
  printer,
@@ -84,6 +85,7 @@ from .lib import (
84
85
  from .lib.exit_hooks import ExitHooks
85
86
  from .lib.mailbox import MailboxError, MailboxHandle, MailboxProbe, MailboxProgress
86
87
  from .wandb_alerts import AlertLevel
88
+ from .wandb_metadata import Metadata
87
89
  from .wandb_settings import Settings
88
90
  from .wandb_setup import _WandbSetup
89
91
 
@@ -287,7 +289,7 @@ class RunStatusChecker:
287
289
  # TODO(frz): This check is required
288
290
  # until WB-3606 is resolved on server side.
289
291
  if not wandb.agents.pyagent.is_running(): # type: ignore
290
- thread.interrupt_main()
292
+ interrupt.interrupt_main()
291
293
  return
292
294
 
293
295
  try:
@@ -595,15 +597,17 @@ class Run:
595
597
  self._config._set_artifact_callback(self._config_artifact_callback)
596
598
  self._config._set_settings(self._settings)
597
599
 
598
- # todo: perhaps this should be a property that is a noop on a finished run
600
+ # TODO: perhaps this should be a property that is a noop on a finished run
599
601
  self.summary = wandb_summary.Summary(
600
602
  self._summary_get_current_summary_callback,
601
603
  )
602
604
  self.summary._set_update_callback(self._summary_update_callback)
603
605
 
606
+ self.__metadata: Metadata | None = None
607
+
604
608
  self._step = 0
605
609
  self._starting_step = 0
606
- # todo: eventually would be nice to make this configurable using self._settings._start_time
610
+ # TODO: eventually would be nice to make this configurable using self._settings._start_time
607
611
  # need to test (jhr): if you set start time to 2 days ago and run a test for 15 minutes,
608
612
  # does the total time get calculated right (not as 2 days and 15 minutes)?
609
613
  self._start_time = time.time()
@@ -1359,32 +1363,68 @@ class Run:
1359
1363
  files: FilesDict = dict(files=[(GlobStr(glob.escape(fname)), "now")])
1360
1364
  self._backend.interface.publish_files(files)
1361
1365
 
1362
- def _serialize_custom_charts(self, data: dict[str, Any]) -> dict[str, Any]:
1363
- if not data:
1364
- return data
1366
+ def _pop_all_charts(
1367
+ self,
1368
+ data: dict[str, Any],
1369
+ key_prefix: str | None = None,
1370
+ ) -> dict[str, Any]:
1371
+ """Pops all charts from a dictionary including nested charts.
1365
1372
 
1366
- chart_keys = set()
1373
+ This function will return a mapping of the charts and a dot-separated
1374
+ key for each chart. Indicating the path to the chart in the data dictionary.
1375
+ """
1376
+ keys_to_remove = set()
1377
+ charts: dict[str, Any] = {}
1367
1378
  for k, v in data.items():
1379
+ key = f"{key_prefix}.{k}" if key_prefix else k
1368
1380
  if isinstance(v, Visualize):
1369
- data[k] = v.table
1370
- v.set_key(k)
1371
- self._config_callback(
1372
- val=v.spec.config_value,
1373
- key=v.spec.config_key,
1374
- )
1381
+ keys_to_remove.add(k)
1382
+ charts[key] = v
1375
1383
  elif isinstance(v, CustomChart):
1376
- chart_keys.add(k)
1377
- v.set_key(k)
1378
- self._config_callback(
1379
- key=v.spec.config_key,
1380
- val=v.spec.config_value,
1381
- )
1384
+ keys_to_remove.add(k)
1385
+ charts[key] = v
1386
+ elif isinstance(v, dict):
1387
+ nested_charts = self._pop_all_charts(v, key)
1388
+ charts.update(nested_charts)
1389
+
1390
+ for k in keys_to_remove:
1391
+ data.pop(k)
1392
+
1393
+ return charts
1394
+
1395
+ def _serialize_custom_charts(
1396
+ self,
1397
+ data: dict[str, Any],
1398
+ ) -> dict[str, Any]:
1399
+ """Process and replace chart objects with their underlying table values.
1400
+
1401
+ This processes the chart objects passed to `run.log()`, replacing their entries
1402
+ in the given dictionary (which is saved to the run's history) and adding them
1403
+ to the run's config.
1404
+
1405
+ Args:
1406
+ data: Dictionary containing data that may include plot objects
1407
+ Plot objects can be nested in dictionaries, which will be processed recursively.
1408
+
1409
+ Returns:
1410
+ The processed dictionary with custom charts transformed into tables.
1411
+ """
1412
+ if not data:
1413
+ return data
1414
+
1415
+ charts = self._pop_all_charts(data)
1416
+ for k, v in charts.items():
1417
+ v.set_key(k)
1418
+ self._config_callback(
1419
+ val=v.spec.config_value,
1420
+ key=v.spec.config_key,
1421
+ )
1382
1422
 
1383
- for k in chart_keys:
1384
- # remove the chart key from the row
1385
- v = data.pop(k)
1386
1423
  if isinstance(v, CustomChart):
1387
1424
  data[v.spec.table_key] = v.table
1425
+ elif isinstance(v, Visualize):
1426
+ data[k] = v.table
1427
+
1388
1428
  return data
1389
1429
 
1390
1430
  def _partial_history_callback(
@@ -2133,8 +2173,9 @@ class Run:
2133
2173
  # Inform the service that we're done sending messages for this run.
2134
2174
  #
2135
2175
  # TODO: Why not do this in _atexit_cleanup()?
2136
- service = self._wl and self._wl.service
2137
- if service and self._settings.run_id:
2176
+ if self._settings.run_id:
2177
+ assert self._wl
2178
+ service = self._wl.assert_service()
2138
2179
  service.inform_finish(run_id=self._settings.run_id)
2139
2180
 
2140
2181
  finally:
@@ -2313,7 +2354,7 @@ class Run:
2313
2354
  self._err_redir = err_redir
2314
2355
  logger.info("Redirects installed.")
2315
2356
  except Exception as e:
2316
- print(e)
2357
+ wandb.termwarn(f"Failed to redirect: {e}")
2317
2358
  logger.error("Failed to redirect.", exc_info=e)
2318
2359
  return
2319
2360
 
@@ -2374,8 +2415,7 @@ class Run:
2374
2415
  logger.info("atexit reg")
2375
2416
  self._hooks = ExitHooks()
2376
2417
 
2377
- service = self._wl and self._wl.service
2378
- if not service:
2418
+ if self.settings.x_disable_service:
2379
2419
  self._hooks.hook()
2380
2420
  # NB: manager will perform atexit hook like behavior for outstanding runs
2381
2421
  atexit.register(lambda: self._atexit_cleanup())
@@ -2388,10 +2428,6 @@ class Run:
2388
2428
  self._output_writer.close()
2389
2429
  self._output_writer = None
2390
2430
 
2391
- def _on_init(self) -> None:
2392
- if self._settings._offline:
2393
- return
2394
-
2395
2431
  def _on_start(self) -> None:
2396
2432
  # would like to move _set_global to _on_ready to unify _on_start and _on_attach
2397
2433
  # (we want to do the set globals after attach)
@@ -3231,7 +3267,7 @@ class Run:
3231
3267
  use_after_commit: bool = False,
3232
3268
  ) -> Artifact:
3233
3269
  api = internal.Api()
3234
- if api.settings().get("anonymous") == "true":
3270
+ if api.settings().get("anonymous") in ["allow", "must"]:
3235
3271
  wandb.termwarn(
3236
3272
  "Artifacts logged anonymously cannot be claimed and expire after 7 days."
3237
3273
  )
@@ -3665,6 +3701,62 @@ class Run:
3665
3701
  logger.error("Error getting system metrics: %s", e)
3666
3702
  return {}
3667
3703
 
3704
+ @property
3705
+ @_run_decorator._attach
3706
+ @_run_decorator._noop_on_finish()
3707
+ def _metadata(self) -> Metadata | None:
3708
+ """The metadata associated with this run.
3709
+
3710
+ NOTE: Automatically collected metadata can be overridden by the user.
3711
+ """
3712
+ if not self._backend or not self._backend.interface:
3713
+ return self.__metadata
3714
+
3715
+ # Initialize the metadata object if it doesn't exist.
3716
+ if self.__metadata is None:
3717
+ self.__metadata = Metadata()
3718
+ self.__metadata._set_callback(self._metadata_callback)
3719
+
3720
+ handle = self._backend.interface.deliver_get_system_metadata()
3721
+ result = handle.wait(timeout=1)
3722
+
3723
+ if not result:
3724
+ logger.error("Error getting run metadata: no result")
3725
+ return None
3726
+
3727
+ try:
3728
+ response = result.response.get_system_metadata_response
3729
+
3730
+ # Temporarily disable the callback to prevent triggering
3731
+ # an update call to wandb-core with the callback.
3732
+ with self.__metadata.disable_callback():
3733
+ # Values stored in the metadata object take precedence.
3734
+ self.__metadata.update_from_proto(response.metadata, skip_existing=True)
3735
+
3736
+ return self.__metadata
3737
+ except Exception as e:
3738
+ logger.error("Error getting run metadata: %s", e)
3739
+
3740
+ return None
3741
+
3742
+ @_run_decorator._noop_on_finish()
3743
+ @_run_decorator._attach
3744
+ def _metadata_callback(
3745
+ self,
3746
+ metadata: MetadataRequest,
3747
+ ) -> None:
3748
+ """Callback to publish Metadata to wandb-core upon user updates."""
3749
+ # ignore updates if the attached to another run
3750
+ if self._is_attached:
3751
+ wandb.termwarn(
3752
+ "Metadata updates are ignored when attached to another run.",
3753
+ repeat=False,
3754
+ )
3755
+ return
3756
+
3757
+ if self._backend and self._backend.interface:
3758
+ self._backend.interface.publish_metadata(metadata)
3759
+
3668
3760
  # ------------------------------------------------------------------------------
3669
3761
  # HEADER
3670
3762
  # ------------------------------------------------------------------------------
@@ -3772,8 +3864,8 @@ class Run:
3772
3864
  f'{printer.emoji("rocket")} View run at {printer.link(run_url)}',
3773
3865
  )
3774
3866
 
3775
- # TODO(settings) use `wandb_settings` (if self.settings.anonymous == "true":)
3776
- if run_name and Api().api.settings().get("anonymous") == "true":
3867
+ # TODO(settings) use `wandb_settings` (if self.settings.anonymous in ["allow", "must"]:)
3868
+ if run_name and Api().api.settings().get("anonymous") in ["allow", "must"]:
3777
3869
  printer.display(
3778
3870
  (
3779
3871
  "Do NOT share these links with anyone."
@@ -305,7 +305,7 @@ class Settings(BaseModel, validate_assignment=True):
305
305
  x_save_requirements: bool = True
306
306
  x_service_transport: str | None = None
307
307
  x_service_wait: float = 30.0
308
- x_show_operation_stats: bool = False
308
+ x_show_operation_stats: bool = True
309
309
  # The start time of the run in seconds since the Unix epoch.
310
310
  x_start_time: float | None = None
311
311
  # PID of the process that started the wandb-core process to collect system stats for.
@@ -349,7 +349,7 @@ class Settings(BaseModel, validate_assignment=True):
349
349
  @model_validator(mode="before")
350
350
  @classmethod
351
351
  def catch_private_settings(cls, values):
352
- """Check if a private field is provided and assign to the corrsponding public one.
352
+ """Check if a private field is provided and assign to the corresponding public one.
353
353
 
354
354
  This is a compatibility layer to handle previous versions of the settings.
355
355
  """
@@ -961,7 +961,7 @@ class Settings(BaseModel, validate_assignment=True):
961
961
  # The Settings class does not track the source of the settings,
962
962
  # so it is up to the developer to ensure that the settings are applied
963
963
  # in the correct order. Most of the updates are done in
964
- # wandb/sdk/wandb_setup.py::_WandbSetup__WandbSetup._settings_setup.
964
+ # wandb/sdk/wandb_setup.py::_WandbSetup._settings_setup.
965
965
 
966
966
  def update_from_system_config_file(self):
967
967
  """Update settings from the system config file."""
@@ -1063,7 +1063,8 @@ class Settings(BaseModel, validate_assignment=True):
1063
1063
  )
1064
1064
  self.x_executable = _executable
1065
1065
 
1066
- self.docker = env.get_docker(util.image_id_from_k8s())
1066
+ if self.docker is None:
1067
+ self.docker = env.get_docker(util.image_id_from_k8s())
1067
1068
 
1068
1069
  # proceed if not in CLI mode
1069
1070
  if self.x_cli_only_mode:
@@ -1176,38 +1177,6 @@ class Settings(BaseModel, validate_assignment=True):
1176
1177
  with open(self.resume_fname, "w") as f:
1177
1178
  f.write(json.dumps({"run_id": self.run_id}))
1178
1179
 
1179
- def handle_sweep_logic(self):
1180
- """Update settings based on sweep context.
1181
-
1182
- When running a sweep, the project, entity, and run_id are handled externally,
1183
- and should be ignored if they are set.
1184
- """
1185
- if self.sweep_id is None:
1186
- return
1187
-
1188
- for key in ("project", "entity", "run_id"):
1189
- value = getattr(self, key)
1190
- if value is not None:
1191
- wandb.termwarn(f"Ignoring {key} {value!r} when running a sweep.")
1192
- setattr(self, key, None)
1193
-
1194
- def handle_launch_logic(self):
1195
- """Update settings based on launch context.
1196
-
1197
- When running in a launch context, the project, entity, and run_id are handled
1198
- externally, and should be ignored if they are set.
1199
- """
1200
- if not self.launch:
1201
- return
1202
-
1203
- for key in ("project", "entity", "run_id"):
1204
- value = getattr(self, key)
1205
- if value is not None:
1206
- wandb.termwarn(
1207
- f"Ignoring {key} {value!r} when running from wandb launch context."
1208
- )
1209
- setattr(self, key, None)
1210
-
1211
1180
  @staticmethod
1212
1181
  def validate_url(url: str) -> None:
1213
1182
  """Validate a URL string."""
@@ -1293,7 +1262,7 @@ class Settings(BaseModel, validate_assignment=True):
1293
1262
  def _get_url_query_string(self) -> str:
1294
1263
  """Construct the query string for project, run, and sweep URLs."""
1295
1264
  # TODO: remove dependency on Api()
1296
- if Api().settings().get("anonymous") != "true":
1265
+ if Api().settings().get("anonymous") not in ["allow", "must"]:
1297
1266
  return ""
1298
1267
 
1299
1268
  api_key = apikey.api_key(settings=self)
wandb/sdk/wandb_setup.py CHANGED
@@ -20,6 +20,7 @@ import threading
20
20
  from typing import TYPE_CHECKING, Any, Union
21
21
 
22
22
  import wandb
23
+ import wandb.integration.sagemaker as sagemaker
23
24
  from wandb.sdk.lib import import_hooks
24
25
 
25
26
  from . import wandb_settings
@@ -61,29 +62,19 @@ class _EarlyLogger:
61
62
  def log(self, level: str, msg: str, *args: Any, **kwargs: Any) -> None:
62
63
  self._log.append((level, msg, args, kwargs))
63
64
 
64
- def _flush(self) -> None:
65
- assert self is not logger
66
- assert logger is not None
65
+ def _flush(self, new_logger: Logger) -> None:
66
+ assert self is not new_logger
67
67
  for level, msg, args, kwargs in self._log:
68
- logger.log(level, msg, *args, **kwargs)
68
+ new_logger.log(level, msg, *args, **kwargs)
69
69
  for msg, args, kwargs in self._exception:
70
- logger.exception(msg, *args, **kwargs)
70
+ new_logger.exception(msg, *args, **kwargs)
71
71
 
72
72
 
73
73
  Logger = Union[logging.Logger, _EarlyLogger]
74
74
 
75
- # logger will be configured to be either a standard logger instance or _EarlyLogger
76
- logger: Logger | None = None
77
75
 
78
-
79
- def _set_logger(log_object: Logger) -> None:
80
- """Configure module logger."""
81
- global logger
82
- logger = log_object
83
-
84
-
85
- class _WandbSetup__WandbSetup: # noqa: N801
86
- """Inner class of _WandbSetup."""
76
+ class _WandbSetup:
77
+ """W&B library singleton."""
87
78
 
88
79
  def __init__(
89
80
  self,
@@ -104,48 +95,52 @@ class _WandbSetup__WandbSetup: # noqa: N801
104
95
 
105
96
  # TODO(jhr): defer strict checks until settings are fully initialized
106
97
  # and logging is ready
107
- self._early_logger = _EarlyLogger()
108
- _set_logger(self._early_logger)
98
+ self._logger: Logger = _EarlyLogger()
109
99
 
110
- self._settings = self._settings_setup(settings, self._early_logger)
100
+ self._settings = self._settings_setup(settings)
111
101
 
112
- wandb.termsetup(self._settings, logger)
102
+ wandb.termsetup(self._settings, None)
113
103
 
114
104
  self._check()
115
105
  self._setup()
116
106
 
117
107
  def _settings_setup(
118
108
  self,
119
- settings: Settings | None = None,
120
- early_logger: _EarlyLogger | None = None,
109
+ settings: Settings | None,
121
110
  ) -> wandb_settings.Settings:
122
111
  s = wandb_settings.Settings()
123
112
 
124
113
  # the pid of the process to monitor for system stats
125
114
  pid = os.getpid()
126
- if early_logger:
127
- early_logger.info(f"Current SDK version is {wandb.__version__}")
128
- early_logger.info(f"Configure stats pid to {pid}")
115
+ self._logger.info(f"Current SDK version is {wandb.__version__}")
116
+ self._logger.info(f"Configure stats pid to {pid}")
129
117
  s.x_stats_pid = pid
130
118
 
131
119
  # load settings from the system config
132
- if s.settings_system and early_logger:
133
- early_logger.info(f"Loading settings from {s.settings_system}")
120
+ if s.settings_system:
121
+ self._logger.info(f"Loading settings from {s.settings_system}")
134
122
  s.update_from_system_config_file()
135
123
 
136
124
  # load settings from the workspace config
137
- if s.settings_workspace and early_logger:
138
- early_logger.info(f"Loading settings from {s.settings_workspace}")
125
+ if s.settings_workspace:
126
+ self._logger.info(f"Loading settings from {s.settings_workspace}")
139
127
  s.update_from_workspace_config_file()
140
128
 
141
129
  # load settings from the environment variables
142
- if early_logger:
143
- early_logger.info("Loading settings from environment variables")
130
+ self._logger.info("Loading settings from environment variables")
144
131
  s.update_from_env_vars(self._environ)
145
132
 
146
133
  # infer settings from the system environment
147
134
  s.update_from_system_environment()
148
135
 
136
+ # load SageMaker settings
137
+ check_sagemaker_env = not s.sagemaker_disable
138
+ if settings and settings.sagemaker_disable:
139
+ check_sagemaker_env = False
140
+ if check_sagemaker_env and sagemaker.is_using_sagemaker():
141
+ self._logger.info("Loading SageMaker settings")
142
+ sagemaker.set_global_settings(s)
143
+
149
144
  # load settings from the passed init/setup settings
150
145
  if settings:
151
146
  s.update_from_settings(settings)
@@ -165,13 +160,15 @@ class _WandbSetup__WandbSetup: # noqa: N801
165
160
  self._settings.update_from_dict(user_settings)
166
161
 
167
162
  def _early_logger_flush(self, new_logger: Logger) -> None:
168
- if not self._early_logger:
163
+ if self._logger is new_logger:
169
164
  return
170
- _set_logger(new_logger)
171
- self._early_logger._flush()
172
165
 
173
- def _get_logger(self) -> Logger | None:
174
- return logger
166
+ if isinstance(self._logger, _EarlyLogger):
167
+ self._logger._flush(new_logger)
168
+ self._logger = new_logger
169
+
170
+ def _get_logger(self) -> Logger:
171
+ return self._logger
175
172
 
176
173
  @property
177
174
  def settings(self) -> wandb_settings.Settings:
@@ -236,16 +233,11 @@ class _WandbSetup__WandbSetup: # noqa: N801
236
233
  if threading.current_thread() is not threading.main_thread():
237
234
  pass
238
235
  elif threading.current_thread().name != "MainThread":
239
- print("bad thread2", threading.current_thread().name)
236
+ wandb.termwarn(f"bad thread2: {threading.current_thread().name}")
240
237
  if getattr(sys, "frozen", False):
241
- print("frozen, could be trouble")
238
+ wandb.termwarn("frozen, could be trouble")
242
239
 
243
240
  def _setup(self) -> None:
244
- if not self._settings._noop and not self._settings.x_disable_service:
245
- from wandb.sdk.lib import service_connection
246
-
247
- self._connection = service_connection.connect_to_service(self._settings)
248
-
249
241
  sweep_path = self._settings.sweep_param_path
250
242
  if sweep_path:
251
243
  self._sweep_config = config_util.dict_from_config_file(
@@ -278,53 +270,61 @@ class _WandbSetup__WandbSetup: # noqa: N801
278
270
  if internal_exit_code != 0:
279
271
  sys.exit(internal_exit_code)
280
272
 
281
- @property
282
- def service(self) -> ServiceConnection | None:
283
- """Returns a connection to the service process, if it exists."""
273
+ def ensure_service(self) -> ServiceConnection:
274
+ """Returns a connection to the service process creating it if needed."""
275
+ if self._connection:
276
+ return self._connection
277
+
278
+ from wandb.sdk.lib import service_connection
279
+
280
+ self._connection = service_connection.connect_to_service(self._settings)
284
281
  return self._connection
285
282
 
283
+ def assert_service(self) -> ServiceConnection:
284
+ """Returns a connection to the service process, asserting it exists.
286
285
 
287
- class _WandbSetup:
288
- """Wandb singleton class.
286
+ Unlike ensure_service(), this will not start up a service process
287
+ if it didn't already exist.
288
+ """
289
+ if not self._connection:
290
+ raise AssertionError("Expected service process to exist.")
289
291
 
290
- Note: This is a process local singleton.
291
- (Forked processes will get a new copy of the object)
292
- """
292
+ return self._connection
293
293
 
294
- _instance: _WandbSetup__WandbSetup | None = None
295
294
 
296
- def __init__(self, settings: Settings | None = None) -> None:
297
- pid = os.getpid()
298
- if _WandbSetup._instance and _WandbSetup._instance._pid == pid:
299
- _WandbSetup._instance._update(settings=settings)
300
- return
301
- _WandbSetup._instance = _WandbSetup__WandbSetup(settings=settings, pid=pid)
295
+ _singleton: _WandbSetup | None = None
296
+ """The W&B library singleton, or None if not yet set up.
302
297
 
303
- @property
304
- def service(self) -> ServiceConnection | None:
305
- """Returns a connection to the service process, if it exists."""
306
- if not self._instance:
307
- return None
308
- return self._instance.service
298
+ The value is invalid and must not be used if `os.getpid() != _singleton._pid`.
299
+ """
309
300
 
310
- def __getattr__(self, name: str) -> Any:
311
- return getattr(self._instance, name)
312
301
 
302
+ def singleton() -> _WandbSetup | None:
303
+ """Returns the W&B singleton if it exists for the current process.
313
304
 
314
- def _setup(
315
- settings: Settings | None = None,
316
- _reset: bool = False,
317
- ) -> _WandbSetup | None:
318
- """Set up library context."""
319
- if _reset:
320
- teardown()
305
+ Unlike setup(), this does not create the singleton if it doesn't exist.
306
+ """
307
+ if _singleton and _singleton._pid == os.getpid():
308
+ return _singleton
309
+ else:
321
310
  return None
322
311
 
323
- wl = _WandbSetup(settings=settings)
324
- return wl
325
312
 
313
+ def _setup(settings: Settings | None = None) -> _WandbSetup:
314
+ """Set up library context."""
315
+ global _singleton
316
+
317
+ pid = os.getpid()
326
318
 
327
- def setup(settings: Settings | None = None) -> _WandbSetup | None:
319
+ if _singleton and _singleton._pid == pid:
320
+ _singleton._update(settings=settings)
321
+ return _singleton
322
+ else:
323
+ _singleton = _WandbSetup(settings=settings, pid=pid)
324
+ return _singleton
325
+
326
+
327
+ def setup(settings: Settings | None = None) -> _WandbSetup:
328
328
  """Prepares W&B for use in the current process and its children.
329
329
 
330
330
  You can usually ignore this as it is implicitly called by `wandb.init()`.
@@ -380,8 +380,7 @@ def setup(settings: Settings | None = None) -> _WandbSetup | None:
380
380
  wandb.teardown()
381
381
  ```
382
382
  """
383
- ret = _setup(settings=settings)
384
- return ret
383
+ return _setup(settings=settings)
385
384
 
386
385
 
387
386
  def teardown(exit_code: int | None = None) -> None:
@@ -395,8 +394,10 @@ def teardown(exit_code: int | None = None) -> None:
395
394
  in an `atexit` hook, but this is not reliable in certain setups
396
395
  such as when using Python's `multiprocessing` module.
397
396
  """
398
- setup_instance = _WandbSetup._instance
399
- _WandbSetup._instance = None
397
+ global _singleton
398
+
399
+ orig_singleton = _singleton
400
+ _singleton = None
400
401
 
401
- if setup_instance:
402
- setup_instance._teardown(exit_code=exit_code)
402
+ if orig_singleton:
403
+ orig_singleton._teardown(exit_code=exit_code)
wandb/sdk/wandb_sweep.py CHANGED
@@ -85,10 +85,10 @@ def sweep(
85
85
  api = InternalApi()
86
86
  sweep_id, warnings = api.upsert_sweep(sweep, prior_runs=prior_runs)
87
87
  handle_sweep_config_violations(warnings)
88
- print("Create sweep with ID:", sweep_id)
88
+ print("Create sweep with ID:", sweep_id) # noqa: T201
89
89
  sweep_url = _get_sweep_url(api, sweep_id)
90
90
  if sweep_url:
91
- print("Sweep URL:", sweep_url)
91
+ print("Sweep URL:", sweep_url) # noqa: T201
92
92
  return sweep_id
93
93
 
94
94