wandb 0.21.4__py3-none-musllinux_1_2_aarch64.whl → 0.22.1__py3-none-musllinux_1_2_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wandb/__init__.py +1 -1
- wandb/__init__.pyi +3 -3
- wandb/_pydantic/__init__.py +12 -11
- wandb/_pydantic/base.py +49 -19
- wandb/apis/__init__.py +2 -0
- wandb/apis/attrs.py +2 -0
- wandb/apis/importers/internals/internal.py +16 -23
- wandb/apis/internal.py +2 -0
- wandb/apis/normalize.py +2 -0
- wandb/apis/public/__init__.py +44 -1
- wandb/apis/public/api.py +215 -164
- wandb/apis/public/artifacts.py +23 -20
- wandb/apis/public/const.py +2 -0
- wandb/apis/public/files.py +33 -24
- wandb/apis/public/history.py +2 -0
- wandb/apis/public/jobs.py +20 -18
- wandb/apis/public/projects.py +4 -2
- wandb/apis/public/query_generator.py +3 -0
- wandb/apis/public/registries/__init__.py +7 -0
- wandb/apis/public/registries/_freezable_list.py +9 -12
- wandb/apis/public/registries/registries_search.py +8 -6
- wandb/apis/public/registries/registry.py +22 -17
- wandb/apis/public/reports.py +2 -0
- wandb/apis/public/runs.py +282 -60
- wandb/apis/public/sweeps.py +10 -9
- wandb/apis/public/teams.py +2 -0
- wandb/apis/public/users.py +2 -0
- wandb/apis/public/utils.py +16 -15
- wandb/automations/_generated/__init__.py +54 -127
- wandb/automations/_generated/create_generic_webhook_integration.py +1 -7
- wandb/automations/_generated/fragments.py +26 -91
- wandb/bin/gpu_stats +0 -0
- wandb/bin/wandb-core +0 -0
- wandb/cli/beta_sync.py +9 -11
- wandb/errors/errors.py +3 -3
- wandb/proto/v3/wandb_internal_pb2.py +234 -224
- wandb/proto/v3/wandb_sync_pb2.py +19 -6
- wandb/proto/v4/wandb_internal_pb2.py +226 -224
- wandb/proto/v4/wandb_sync_pb2.py +10 -6
- wandb/proto/v5/wandb_internal_pb2.py +226 -224
- wandb/proto/v5/wandb_sync_pb2.py +10 -6
- wandb/proto/v6/wandb_base_pb2.py +3 -3
- wandb/proto/v6/wandb_internal_pb2.py +229 -227
- wandb/proto/v6/wandb_server_pb2.py +3 -3
- wandb/proto/v6/wandb_settings_pb2.py +3 -3
- wandb/proto/v6/wandb_sync_pb2.py +13 -9
- wandb/proto/v6/wandb_telemetry_pb2.py +3 -3
- wandb/sdk/artifacts/_factories.py +7 -2
- wandb/sdk/artifacts/_generated/__init__.py +112 -412
- wandb/sdk/artifacts/_generated/fragments.py +65 -0
- wandb/sdk/artifacts/_generated/operations.py +52 -22
- wandb/sdk/artifacts/_generated/run_input_artifacts.py +3 -23
- wandb/sdk/artifacts/_generated/run_output_artifacts.py +3 -23
- wandb/sdk/artifacts/_generated/type_info.py +19 -0
- wandb/sdk/artifacts/_gqlutils.py +47 -0
- wandb/sdk/artifacts/_models/__init__.py +4 -0
- wandb/sdk/artifacts/_models/base_model.py +20 -0
- wandb/sdk/artifacts/_validators.py +40 -12
- wandb/sdk/artifacts/artifact.py +69 -88
- wandb/sdk/artifacts/artifact_file_cache.py +6 -1
- wandb/sdk/artifacts/artifact_manifest_entry.py +61 -2
- wandb/sdk/artifacts/storage_handlers/gcs_handler.py +1 -1
- wandb/sdk/artifacts/storage_handlers/http_handler.py +1 -3
- wandb/sdk/artifacts/storage_handlers/local_file_handler.py +1 -1
- wandb/sdk/artifacts/storage_handlers/s3_handler.py +1 -1
- wandb/sdk/artifacts/storage_policies/_factories.py +63 -0
- wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py +69 -124
- wandb/sdk/data_types/bokeh.py +5 -1
- wandb/sdk/data_types/image.py +17 -6
- wandb/sdk/interface/interface.py +41 -4
- wandb/sdk/interface/interface_queue.py +10 -0
- wandb/sdk/interface/interface_shared.py +9 -7
- wandb/sdk/interface/interface_sock.py +9 -3
- wandb/sdk/internal/_generated/__init__.py +2 -12
- wandb/sdk/internal/sender.py +1 -1
- wandb/sdk/internal/settings_static.py +2 -82
- wandb/sdk/launch/runner/kubernetes_runner.py +25 -20
- wandb/sdk/launch/utils.py +82 -1
- wandb/sdk/lib/progress.py +7 -4
- wandb/sdk/lib/service/service_client.py +5 -9
- wandb/sdk/lib/service/service_connection.py +39 -23
- wandb/sdk/mailbox/mailbox_handle.py +2 -0
- wandb/sdk/projects/_generated/__init__.py +12 -33
- wandb/sdk/wandb_init.py +31 -3
- wandb/sdk/wandb_login.py +53 -27
- wandb/sdk/wandb_run.py +5 -3
- wandb/sdk/wandb_settings.py +50 -13
- wandb/sync/sync.py +7 -2
- wandb/util.py +1 -1
- wandb/wandb_agent.py +35 -4
- {wandb-0.21.4.dist-info → wandb-0.22.1.dist-info}/METADATA +1 -1
- {wandb-0.21.4.dist-info → wandb-0.22.1.dist-info}/RECORD +818 -814
- wandb/sdk/artifacts/_graphql_fragments.py +0 -19
- {wandb-0.21.4.dist-info → wandb-0.22.1.dist-info}/WHEEL +0 -0
- {wandb-0.21.4.dist-info → wandb-0.22.1.dist-info}/entry_points.txt +0 -0
- {wandb-0.21.4.dist-info → wandb-0.22.1.dist-info}/licenses/LICENSE +0 -0
wandb/sdk/wandb_login.py
CHANGED
@@ -77,7 +77,7 @@ def login(
|
|
77
77
|
UsageError: If `api_key` cannot be configured and no tty.
|
78
78
|
"""
|
79
79
|
_handle_host_wandb_setting(host)
|
80
|
-
|
80
|
+
logged_in, _ = _login(
|
81
81
|
anonymous=anonymous,
|
82
82
|
key=key,
|
83
83
|
relogin=relogin,
|
@@ -87,6 +87,7 @@ def login(
|
|
87
87
|
verify=verify,
|
88
88
|
referrer=referrer,
|
89
89
|
)
|
90
|
+
return logged_in
|
90
91
|
|
91
92
|
|
92
93
|
class ApiKeyStatus(enum.Enum):
|
@@ -245,24 +246,6 @@ class _WandbLogin:
|
|
245
246
|
|
246
247
|
return key, status
|
247
248
|
|
248
|
-
def _verify_login(self, key: str) -> None:
|
249
|
-
api = InternalApi(api_key=key)
|
250
|
-
|
251
|
-
try:
|
252
|
-
is_api_key_valid = api.validate_api_key()
|
253
|
-
except ConnectionError:
|
254
|
-
raise AuthenticationError(
|
255
|
-
"Unable to connect to server to verify API token."
|
256
|
-
)
|
257
|
-
except Exception:
|
258
|
-
raise AuthenticationError("An error occurred while verifying the API key.")
|
259
|
-
|
260
|
-
if not is_api_key_valid:
|
261
|
-
raise AuthenticationError(
|
262
|
-
f"API key verification failed for host {self._settings.base_url}."
|
263
|
-
" Make sure your API key is valid."
|
264
|
-
)
|
265
|
-
|
266
249
|
|
267
250
|
def _login(
|
268
251
|
*,
|
@@ -277,11 +260,30 @@ def _login(
|
|
277
260
|
update_api_key: bool = True,
|
278
261
|
_silent: Optional[bool] = None,
|
279
262
|
_disable_warning: Optional[bool] = None,
|
280
|
-
) -> bool:
|
263
|
+
) -> (bool, Optional[str]):
|
264
|
+
"""Logs in to W&B.
|
265
|
+
|
266
|
+
This is the internal implementation of wandb.login(),
|
267
|
+
with many of the same arguments as wandb.login().
|
268
|
+
Additional arguments are documented below.
|
269
|
+
|
270
|
+
Args:
|
271
|
+
update_api_key: If true, the api key will be saved or updated
|
272
|
+
in the users .netrc file.
|
273
|
+
_silent: If true, will not print any messages to the console.
|
274
|
+
_disable_warning: If true, no warning will be displayed
|
275
|
+
when calling wandb.login() after wandb.init().
|
276
|
+
|
277
|
+
Returns:
|
278
|
+
bool: If the login was successful
|
279
|
+
or the user is assumed to be already be logged in.
|
280
|
+
str: The API key used to log in,
|
281
|
+
or None if the api key was not verified during the login process.
|
282
|
+
"""
|
281
283
|
if wandb.run is not None:
|
282
284
|
if not _disable_warning:
|
283
285
|
wandb.termwarn("Calling wandb.login() after wandb.init() has no effect.")
|
284
|
-
return True
|
286
|
+
return True, None
|
285
287
|
|
286
288
|
wlogin = _WandbLogin(
|
287
289
|
anonymous=anonymous,
|
@@ -293,19 +295,19 @@ def _login(
|
|
293
295
|
)
|
294
296
|
|
295
297
|
if wlogin._settings._noop:
|
296
|
-
return True
|
298
|
+
return True, None
|
297
299
|
|
298
300
|
if wlogin._settings._offline and not wlogin._settings.x_cli_only_mode:
|
299
301
|
wandb.termwarn("Unable to verify login in offline mode.")
|
300
|
-
return False
|
302
|
+
return False, None
|
301
303
|
elif wandb.util._is_kaggle() and not wandb.util._has_internet():
|
302
304
|
wandb.termerror(
|
303
305
|
"To use W&B in kaggle you must enable internet in the settings panel on the right."
|
304
306
|
)
|
305
|
-
return False
|
307
|
+
return False, None
|
306
308
|
|
307
309
|
if wlogin._settings.identity_token_file:
|
308
|
-
return True
|
310
|
+
return True, None
|
309
311
|
|
310
312
|
key_is_pre_configured = False
|
311
313
|
key_status = None
|
@@ -318,7 +320,7 @@ def _login(
|
|
318
320
|
key, key_status = wlogin.prompt_api_key(referrer=referrer)
|
319
321
|
|
320
322
|
if verify:
|
321
|
-
|
323
|
+
_verify_login(key, wlogin._settings.base_url)
|
322
324
|
|
323
325
|
if not key_is_pre_configured:
|
324
326
|
if update_api_key:
|
@@ -329,4 +331,28 @@ def _login(
|
|
329
331
|
if key and not _silent:
|
330
332
|
wlogin._print_logged_in_message()
|
331
333
|
|
332
|
-
return key is not None
|
334
|
+
return key is not None, key
|
335
|
+
|
336
|
+
|
337
|
+
def _verify_login(key: str, base_url: str) -> None:
|
338
|
+
api = InternalApi(
|
339
|
+
api_key=key,
|
340
|
+
default_settings={"base_url": base_url},
|
341
|
+
)
|
342
|
+
|
343
|
+
try:
|
344
|
+
is_api_key_valid = api.validate_api_key()
|
345
|
+
except ConnectionError:
|
346
|
+
raise AuthenticationError(
|
347
|
+
"Unable to connect to server to verify API token."
|
348
|
+
) from None
|
349
|
+
except Exception as e:
|
350
|
+
raise AuthenticationError(
|
351
|
+
"An error occurred while verifying the API key."
|
352
|
+
) from e
|
353
|
+
|
354
|
+
if not is_api_key_valid:
|
355
|
+
raise AuthenticationError(
|
356
|
+
f"API key verification failed for host {base_url}."
|
357
|
+
" Make sure your API key is valid."
|
358
|
+
)
|
wandb/sdk/wandb_run.py
CHANGED
@@ -33,6 +33,7 @@ from wandb.errors import CommError, UsageError
|
|
33
33
|
from wandb.errors.links import url_registry
|
34
34
|
from wandb.integration.torch import wandb_torch
|
35
35
|
from wandb.plot import CustomChart, Visualize
|
36
|
+
from wandb.proto import wandb_internal_pb2 as pb
|
36
37
|
from wandb.proto.wandb_deprecated import Deprecated
|
37
38
|
from wandb.proto.wandb_internal_pb2 import (
|
38
39
|
MetricRecord,
|
@@ -41,7 +42,6 @@ from wandb.proto.wandb_internal_pb2 import (
|
|
41
42
|
RunRecord,
|
42
43
|
)
|
43
44
|
from wandb.sdk.artifacts._internal_artifact import InternalArtifact
|
44
|
-
from wandb.sdk.artifacts._validators import is_artifact_registry_project
|
45
45
|
from wandb.sdk.artifacts.artifact import Artifact
|
46
46
|
from wandb.sdk.internal import job_builder
|
47
47
|
from wandb.sdk.lib import asyncio_compat, wb_logging
|
@@ -2698,7 +2698,9 @@ class Run:
|
|
2698
2698
|
assert self._backend and self._backend.interface
|
2699
2699
|
|
2700
2700
|
while True:
|
2701
|
-
handle = self._backend.interface.
|
2701
|
+
handle = await self._backend.interface.deliver_async(
|
2702
|
+
pb.Record(request=pb.Request(poll_exit=pb.PollExitRequest()))
|
2703
|
+
)
|
2702
2704
|
|
2703
2705
|
time_start = time.monotonic()
|
2704
2706
|
last_result = await handle.wait_async(timeout=None)
|
@@ -3004,7 +3006,7 @@ class Run:
|
|
3004
3006
|
# the target entity to the run's entity. Instead, delegate to
|
3005
3007
|
# Artifact.link() to resolve the required org entity.
|
3006
3008
|
target = ArtifactPath.from_str(target_path)
|
3007
|
-
if not
|
3009
|
+
if not target.is_registry_path():
|
3008
3010
|
target = target.with_defaults(prefix=self.entity, project=self.project)
|
3009
3011
|
|
3010
3012
|
return artifact.link(target.to_str(), aliases)
|
wandb/sdk/wandb_settings.py
CHANGED
@@ -160,6 +160,7 @@ CLIENT_ONLY_SETTINGS = (
|
|
160
160
|
"reinit",
|
161
161
|
"max_end_of_run_history_metrics",
|
162
162
|
"max_end_of_run_summary_metrics",
|
163
|
+
"x_sync_dir_suffix",
|
163
164
|
)
|
164
165
|
"""Python-only keys that are not fields on the settings proto."""
|
165
166
|
|
@@ -941,6 +942,13 @@ class Settings(BaseModel, validate_assignment=True):
|
|
941
942
|
<!-- lazydoc-ignore-class-attributes -->
|
942
943
|
"""
|
943
944
|
|
945
|
+
x_sync_dir_suffix: str = ""
|
946
|
+
"""Suffix to add to the run's directory name (sync_dir).
|
947
|
+
|
948
|
+
This is set in wandb.init() to avoid naming conflicts.
|
949
|
+
If set, it is joined to the default name with a dash.
|
950
|
+
"""
|
951
|
+
|
944
952
|
x_update_finish_state: bool = True
|
945
953
|
"""Flag to indicate whether this process can update the run's final state on the server.
|
946
954
|
|
@@ -954,6 +962,8 @@ class Settings(BaseModel, validate_assignment=True):
|
|
954
962
|
"""Check if a private field is provided and assign to the corresponding public one.
|
955
963
|
|
956
964
|
This is a compatibility layer to handle previous versions of the settings.
|
965
|
+
|
966
|
+
<!-- lazydoc-ignore: internal -->
|
957
967
|
"""
|
958
968
|
new_values = {}
|
959
969
|
for key in values:
|
@@ -988,6 +998,10 @@ class Settings(BaseModel, validate_assignment=True):
|
|
988
998
|
|
989
999
|
@model_validator(mode="after")
|
990
1000
|
def validate_skip_transaction_log(self):
|
1001
|
+
"""Validate x_skip_transaction_log.
|
1002
|
+
|
1003
|
+
<!-- lazydoc-ignore: internal -->
|
1004
|
+
"""
|
991
1005
|
if self._offline and self.x_skip_transaction_log:
|
992
1006
|
raise ValueError("Cannot skip transaction log in offline mode")
|
993
1007
|
return self
|
@@ -1445,6 +1459,8 @@ class Settings(BaseModel, validate_assignment=True):
|
|
1445
1459
|
- Converts single string values to tuple format
|
1446
1460
|
- Preserves None values
|
1447
1461
|
|
1462
|
+
<!-- lazydoc-ignore-classmethod: internal -->
|
1463
|
+
|
1448
1464
|
Args:
|
1449
1465
|
value: A string, list, tuple, or None representing tags
|
1450
1466
|
|
@@ -1453,8 +1469,6 @@ class Settings(BaseModel, validate_assignment=True):
|
|
1453
1469
|
|
1454
1470
|
Raises:
|
1455
1471
|
ValueError: If any tag is empty or exceeds 64 characters
|
1456
|
-
|
1457
|
-
<!-- lazydoc-ignore-classmethod: internal -->
|
1458
1472
|
"""
|
1459
1473
|
if value is None:
|
1460
1474
|
return None
|
@@ -1733,10 +1747,12 @@ class Settings(BaseModel, validate_assignment=True):
|
|
1733
1747
|
@property
|
1734
1748
|
def sync_dir(self) -> str:
|
1735
1749
|
"""The directory for storing the run's files."""
|
1736
|
-
|
1737
|
-
|
1738
|
-
|
1739
|
-
|
1750
|
+
name = f"{self.run_mode}-{self.timespec}-{self.run_id}"
|
1751
|
+
|
1752
|
+
if self.x_sync_dir_suffix:
|
1753
|
+
name += f"-{self.x_sync_dir_suffix}"
|
1754
|
+
|
1755
|
+
return _path_convert(self.wandb_dir, name)
|
1740
1756
|
|
1741
1757
|
@computed_field # type: ignore[prop-decorator]
|
1742
1758
|
@property
|
@@ -1776,7 +1792,10 @@ class Settings(BaseModel, validate_assignment=True):
|
|
1776
1792
|
# wandb/sdk/wandb_setup.py::_WandbSetup._settings_setup.
|
1777
1793
|
|
1778
1794
|
def update_from_system_config_file(self):
|
1779
|
-
"""Update settings from the system config file.
|
1795
|
+
"""Update settings from the system config file.
|
1796
|
+
|
1797
|
+
<!-- lazydoc-ignore: internal -->
|
1798
|
+
"""
|
1780
1799
|
if not self.settings_system or not os.path.exists(self.settings_system):
|
1781
1800
|
return
|
1782
1801
|
for key, value in self._load_config_file(self.settings_system).items():
|
@@ -1784,7 +1803,10 @@ class Settings(BaseModel, validate_assignment=True):
|
|
1784
1803
|
setattr(self, key, value)
|
1785
1804
|
|
1786
1805
|
def update_from_workspace_config_file(self):
|
1787
|
-
"""Update settings from the workspace config file.
|
1806
|
+
"""Update settings from the workspace config file.
|
1807
|
+
|
1808
|
+
<!-- lazydoc-ignore: internal -->
|
1809
|
+
"""
|
1788
1810
|
if not self.settings_workspace or not os.path.exists(self.settings_workspace):
|
1789
1811
|
return
|
1790
1812
|
for key, value in self._load_config_file(self.settings_workspace).items():
|
@@ -1792,7 +1814,10 @@ class Settings(BaseModel, validate_assignment=True):
|
|
1792
1814
|
setattr(self, key, value)
|
1793
1815
|
|
1794
1816
|
def update_from_env_vars(self, environ: Dict[str, Any]):
|
1795
|
-
"""Update settings from environment variables.
|
1817
|
+
"""Update settings from environment variables.
|
1818
|
+
|
1819
|
+
<!-- lazydoc-ignore: internal -->
|
1820
|
+
"""
|
1796
1821
|
env_prefix: str = "WANDB_"
|
1797
1822
|
private_env_prefix: str = env_prefix + "_"
|
1798
1823
|
special_env_var_names = {
|
@@ -1829,7 +1854,10 @@ class Settings(BaseModel, validate_assignment=True):
|
|
1829
1854
|
setattr(self, key, value)
|
1830
1855
|
|
1831
1856
|
def update_from_system_environment(self):
|
1832
|
-
"""Update settings from the system environment.
|
1857
|
+
"""Update settings from the system environment.
|
1858
|
+
|
1859
|
+
<!-- lazydoc-ignore: internal -->
|
1860
|
+
"""
|
1833
1861
|
# For code saving, only allow env var override if value from server is true, or
|
1834
1862
|
# if no preference was specified.
|
1835
1863
|
if (self.save_code is True or self.save_code is None) and (
|
@@ -1909,13 +1937,19 @@ class Settings(BaseModel, validate_assignment=True):
|
|
1909
1937
|
self.program = program
|
1910
1938
|
|
1911
1939
|
def update_from_dict(self, settings: Dict[str, Any]) -> None:
|
1912
|
-
"""Update settings from a dictionary.
|
1940
|
+
"""Update settings from a dictionary.
|
1941
|
+
|
1942
|
+
<!-- lazydoc-ignore: internal -->
|
1943
|
+
"""
|
1913
1944
|
for key, value in dict(settings).items():
|
1914
1945
|
if value is not None:
|
1915
1946
|
setattr(self, key, value)
|
1916
1947
|
|
1917
1948
|
def update_from_settings(self, settings: Settings) -> None:
|
1918
|
-
"""Update settings from another instance of `Settings`.
|
1949
|
+
"""Update settings from another instance of `Settings`.
|
1950
|
+
|
1951
|
+
<!-- lazydoc-ignore: internal -->
|
1952
|
+
"""
|
1919
1953
|
d = {field: getattr(settings, field) for field in settings.model_fields_set}
|
1920
1954
|
if d:
|
1921
1955
|
self.update_from_dict(d)
|
@@ -1923,7 +1957,10 @@ class Settings(BaseModel, validate_assignment=True):
|
|
1923
1957
|
# Helper methods.
|
1924
1958
|
|
1925
1959
|
def to_proto(self) -> wandb_settings_pb2.Settings:
|
1926
|
-
"""Generate a protobuf representation of the settings.
|
1960
|
+
"""Generate a protobuf representation of the settings.
|
1961
|
+
|
1962
|
+
<!-- lazydoc-ignore: internal -->
|
1963
|
+
"""
|
1927
1964
|
settings_proto = wandb_settings_pb2.Settings()
|
1928
1965
|
for k, v in self.model_dump(exclude_none=True).items():
|
1929
1966
|
if k in CLIENT_ONLY_SETTINGS:
|
wandb/sync/sync.py
CHANGED
@@ -193,7 +193,7 @@ class SyncThread(threading.Thread):
|
|
193
193
|
x_start_time=time.time(),
|
194
194
|
)
|
195
195
|
|
196
|
-
settings_static = SettingsStatic(settings
|
196
|
+
settings_static = SettingsStatic(dict(settings))
|
197
197
|
|
198
198
|
handle_manager = handler.HandleManager(
|
199
199
|
settings=settings_static,
|
@@ -207,7 +207,12 @@ class SyncThread(threading.Thread):
|
|
207
207
|
|
208
208
|
filesystem.mkdir_exists_ok(settings.files_dir)
|
209
209
|
send_manager.send_run(record, file_dir=settings.files_dir)
|
210
|
-
watcher = tb_watcher.TBWatcher(
|
210
|
+
watcher = tb_watcher.TBWatcher(
|
211
|
+
settings_static,
|
212
|
+
proto_run,
|
213
|
+
new_interface,
|
214
|
+
True,
|
215
|
+
)
|
211
216
|
|
212
217
|
for tb in tb_logdirs:
|
213
218
|
watcher.add(tb, True, tb_root)
|
wandb/util.py
CHANGED
@@ -229,7 +229,7 @@ def import_module_lazy(name: str) -> types.ModuleType:
|
|
229
229
|
|
230
230
|
def get_module(
|
231
231
|
name: str,
|
232
|
-
required: Optional[
|
232
|
+
required: Optional[str] = None,
|
233
233
|
lazy: bool = True,
|
234
234
|
) -> Any:
|
235
235
|
"""Return module or None. Absolute import is required.
|
wandb/wandb_agent.py
CHANGED
@@ -42,11 +42,42 @@ class AgentProcess:
|
|
42
42
|
if command:
|
43
43
|
if platform.system() == "Windows":
|
44
44
|
kwargs = dict(creationflags=subprocess.CREATE_NEW_PROCESS_GROUP)
|
45
|
+
env.pop(wandb.env.SERVICE, None)
|
46
|
+
# TODO: Determine if we need the same stdin workaround as POSIX case below.
|
47
|
+
self._popen = subprocess.Popen(command, env=env, **kwargs)
|
45
48
|
else:
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
49
|
+
if sys.version_info >= (3, 11):
|
50
|
+
# preexec_fn=os.setpgrp is not thread-safe; process_group was introduced in
|
51
|
+
# python 3.11 to replace it, so use that when possible
|
52
|
+
kwargs = dict(process_group=0)
|
53
|
+
else:
|
54
|
+
kwargs = dict(preexec_fn=os.setpgrp)
|
55
|
+
env.pop(wandb.env.SERVICE, None)
|
56
|
+
# Upon spawning the subprocess in a new process group, the child's process group is
|
57
|
+
# not connected to the controlling terminal's stdin. If it tries to access stdin,
|
58
|
+
# it gets a SIGTTIN and blocks until we give it the terminal, which we don't want
|
59
|
+
# to do.
|
60
|
+
#
|
61
|
+
# By using subprocess.PIPE, we give it an independent stdin. However, it will still
|
62
|
+
# block if it tries to read from stdin, because we're not writing anything to it.
|
63
|
+
# We immediately close the subprocess's stdin here so it can fail fast and get an
|
64
|
+
# EOF.
|
65
|
+
#
|
66
|
+
# (One situation that makes this relevant is that importing `readline` even
|
67
|
+
# indirectly can cause the child to attempt to access stdin, which can trigger the
|
68
|
+
# deadlock. In Python 3.13, `import torch` indirectly imports `readline` via `pdb`,
|
69
|
+
# meaning `import torch` in a run script can deadlock unless we override stdin.
|
70
|
+
# See https://github.com/wandb/wandb/pull/10489 description for more details.)
|
71
|
+
#
|
72
|
+
# Also, we avoid spawning a new session because that breaks preempted child process
|
73
|
+
# handling.
|
74
|
+
self._popen = subprocess.Popen(
|
75
|
+
command,
|
76
|
+
env=env,
|
77
|
+
stdin=subprocess.PIPE,
|
78
|
+
**kwargs,
|
79
|
+
)
|
80
|
+
self._popen.stdin.close()
|
50
81
|
elif function:
|
51
82
|
self._proc = multiprocessing.Process(
|
52
83
|
target=self._start,
|