wandb 0.21.0__py3-none-win_amd64.whl → 0.21.1__py3-none-win_amd64.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 (97) hide show
  1. wandb/__init__.py +16 -14
  2. wandb/__init__.pyi +427 -450
  3. wandb/agents/pyagent.py +41 -12
  4. wandb/analytics/sentry.py +7 -2
  5. wandb/apis/importers/mlflow.py +1 -1
  6. wandb/apis/public/__init__.py +1 -1
  7. wandb/apis/public/api.py +526 -360
  8. wandb/apis/public/artifacts.py +204 -8
  9. wandb/apis/public/automations.py +19 -3
  10. wandb/apis/public/files.py +172 -33
  11. wandb/apis/public/history.py +67 -15
  12. wandb/apis/public/integrations.py +25 -2
  13. wandb/apis/public/jobs.py +90 -2
  14. wandb/apis/public/projects.py +130 -79
  15. wandb/apis/public/query_generator.py +11 -1
  16. wandb/apis/public/registries/registries_search.py +7 -15
  17. wandb/apis/public/reports.py +83 -5
  18. wandb/apis/public/runs.py +299 -105
  19. wandb/apis/public/sweeps.py +222 -22
  20. wandb/apis/public/teams.py +41 -4
  21. wandb/apis/public/users.py +45 -4
  22. wandb/beta/workflows.py +66 -30
  23. wandb/bin/gpu_stats.exe +0 -0
  24. wandb/bin/wandb-core +0 -0
  25. wandb/cli/cli.py +80 -1
  26. wandb/env.py +8 -0
  27. wandb/errors/errors.py +4 -1
  28. wandb/integration/lightning/fabric/logger.py +3 -4
  29. wandb/integration/metaflow/__init__.py +6 -0
  30. wandb/integration/metaflow/data_pandas.py +74 -0
  31. wandb/integration/metaflow/errors.py +13 -0
  32. wandb/integration/metaflow/metaflow.py +205 -190
  33. wandb/integration/openai/fine_tuning.py +1 -2
  34. wandb/jupyter.py +5 -5
  35. wandb/plot/custom_chart.py +30 -7
  36. wandb/proto/v3/wandb_internal_pb2.py +280 -280
  37. wandb/proto/v3/wandb_telemetry_pb2.py +4 -4
  38. wandb/proto/v4/wandb_internal_pb2.py +280 -280
  39. wandb/proto/v4/wandb_telemetry_pb2.py +4 -4
  40. wandb/proto/v5/wandb_internal_pb2.py +280 -280
  41. wandb/proto/v5/wandb_telemetry_pb2.py +4 -4
  42. wandb/proto/v6/wandb_internal_pb2.py +280 -280
  43. wandb/proto/v6/wandb_telemetry_pb2.py +4 -4
  44. wandb/proto/wandb_deprecated.py +6 -0
  45. wandb/sdk/artifacts/_internal_artifact.py +19 -8
  46. wandb/sdk/artifacts/_validators.py +8 -0
  47. wandb/sdk/artifacts/artifact.py +106 -75
  48. wandb/sdk/data_types/audio.py +38 -10
  49. wandb/sdk/data_types/base_types/media.py +6 -56
  50. wandb/sdk/data_types/graph.py +48 -14
  51. wandb/sdk/data_types/helper_types/bounding_boxes_2d.py +1 -3
  52. wandb/sdk/data_types/helper_types/image_mask.py +1 -3
  53. wandb/sdk/data_types/histogram.py +34 -21
  54. wandb/sdk/data_types/html.py +35 -12
  55. wandb/sdk/data_types/image.py +104 -68
  56. wandb/sdk/data_types/molecule.py +32 -19
  57. wandb/sdk/data_types/object_3d.py +36 -17
  58. wandb/sdk/data_types/plotly.py +18 -5
  59. wandb/sdk/data_types/saved_model.py +4 -6
  60. wandb/sdk/data_types/table.py +59 -30
  61. wandb/sdk/data_types/video.py +53 -26
  62. wandb/sdk/integration_utils/auto_logging.py +2 -2
  63. wandb/sdk/internal/internal_api.py +6 -0
  64. wandb/sdk/internal/job_builder.py +6 -0
  65. wandb/sdk/launch/agent/agent.py +8 -1
  66. wandb/sdk/launch/agent/run_queue_item_file_saver.py +2 -2
  67. wandb/sdk/launch/create_job.py +3 -1
  68. wandb/sdk/launch/inputs/internal.py +3 -4
  69. wandb/sdk/launch/inputs/schema.py +1 -0
  70. wandb/sdk/launch/runner/kubernetes_monitor.py +1 -0
  71. wandb/sdk/launch/runner/kubernetes_runner.py +328 -1
  72. wandb/sdk/launch/sweeps/scheduler.py +2 -3
  73. wandb/sdk/lib/asyncio_compat.py +3 -0
  74. wandb/sdk/lib/deprecate.py +1 -7
  75. wandb/sdk/lib/disabled.py +1 -1
  76. wandb/sdk/lib/hashutil.py +14 -1
  77. wandb/sdk/lib/module.py +7 -13
  78. wandb/sdk/lib/progress.py +0 -19
  79. wandb/sdk/lib/sock_client.py +0 -4
  80. wandb/sdk/wandb_init.py +66 -91
  81. wandb/sdk/wandb_login.py +18 -14
  82. wandb/sdk/wandb_metric.py +2 -0
  83. wandb/sdk/wandb_run.py +406 -414
  84. wandb/sdk/wandb_settings.py +130 -2
  85. wandb/sdk/wandb_setup.py +28 -28
  86. wandb/sdk/wandb_sweep.py +14 -13
  87. wandb/sdk/wandb_watch.py +4 -6
  88. wandb/sync/sync.py +10 -0
  89. wandb/util.py +57 -0
  90. wandb/wandb_run.py +1 -2
  91. {wandb-0.21.0.dist-info → wandb-0.21.1.dist-info}/METADATA +1 -1
  92. {wandb-0.21.0.dist-info → wandb-0.21.1.dist-info}/RECORD +95 -95
  93. wandb/vendor/pynvml/__init__.py +0 -0
  94. wandb/vendor/pynvml/pynvml.py +0 -4779
  95. {wandb-0.21.0.dist-info → wandb-0.21.1.dist-info}/WHEEL +0 -0
  96. {wandb-0.21.0.dist-info → wandb-0.21.1.dist-info}/entry_points.txt +0 -0
  97. {wandb-0.21.0.dist-info → wandb-0.21.1.dist-info}/licenses/LICENSE +0 -0
@@ -162,7 +162,7 @@ class Settings(BaseModel, validate_assignment=True):
162
162
  This class manages configuration settings for the W&B SDK,
163
163
  ensuring type safety and validation of all settings. Settings are accessible
164
164
  as attributes and can be initialized programmatically, through environment
165
- variables (WANDB_ prefix), and via configuration files.
165
+ variables (`WANDB_ prefix`), and with configuration files.
166
166
 
167
167
  The settings are organized into three categories:
168
168
  1. Public settings: Core configuration options that users can safely modify to customize
@@ -230,7 +230,7 @@ class Settings(BaseModel, validate_assignment=True):
230
230
  """The type of console capture to be applied.
231
231
 
232
232
  Possible values are:
233
- "auto" - Automatically selects the console capture method based on the
233
+ "auto" - Automatically selects the console capture method based on the
234
234
  system environment and settings.
235
235
 
236
236
  "off" - Disables console capture.
@@ -955,6 +955,10 @@ class Settings(BaseModel, validate_assignment=True):
955
955
 
956
956
  @model_validator(mode="after")
957
957
  def validate_mutual_exclusion_of_branching_args(self) -> Self:
958
+ """Check if `fork_from`, `resume`, and `resume_from` are mutually exclusive.
959
+
960
+ <!-- lazydoc-ignore: internal -->
961
+ """
958
962
  if (
959
963
  sum(
960
964
  o is not None
@@ -1002,6 +1006,10 @@ class Settings(BaseModel, validate_assignment=True):
1002
1006
  @field_validator("api_key", mode="after")
1003
1007
  @classmethod
1004
1008
  def validate_api_key(cls, value):
1009
+ """Validate the API key.
1010
+
1011
+ <!-- lazydoc-ignore: internal -->
1012
+ """
1005
1013
  if value is not None and (len(value) > len(value.strip())):
1006
1014
  raise UsageError("API key cannot start or end with whitespace")
1007
1015
  return value
@@ -1009,6 +1017,10 @@ class Settings(BaseModel, validate_assignment=True):
1009
1017
  @field_validator("base_url", mode="after")
1010
1018
  @classmethod
1011
1019
  def validate_base_url(cls, value):
1020
+ """Validate the base URL.
1021
+
1022
+ <!-- lazydoc-ignore: internal -->
1023
+ """
1012
1024
  validate_url(value)
1013
1025
  # wandb.ai-specific checks
1014
1026
  if re.match(r".*wandb\.ai[^\.]*$", value) and "api." not in value:
@@ -1023,6 +1035,10 @@ class Settings(BaseModel, validate_assignment=True):
1023
1035
  @field_validator("code_dir", mode="before")
1024
1036
  @classmethod
1025
1037
  def validate_code_dir(cls, value):
1038
+ """Validate the code directory.
1039
+
1040
+ <!-- lazydoc-ignore: internal -->
1041
+ """
1026
1042
  # TODO: add native support for pathlib.Path
1027
1043
  if isinstance(value, pathlib.Path):
1028
1044
  return str(value)
@@ -1031,6 +1047,10 @@ class Settings(BaseModel, validate_assignment=True):
1031
1047
  @field_validator("console", mode="after")
1032
1048
  @classmethod
1033
1049
  def validate_console(cls, value, values):
1050
+ """Validate the console capture method.
1051
+
1052
+ <!-- lazydoc-ignore: internal -->
1053
+ """
1034
1054
  if value != "auto":
1035
1055
  return value
1036
1056
 
@@ -1039,6 +1059,10 @@ class Settings(BaseModel, validate_assignment=True):
1039
1059
  @field_validator("x_executable", mode="before")
1040
1060
  @classmethod
1041
1061
  def validate_x_executable(cls, value):
1062
+ """Validate the Python executable path.
1063
+
1064
+ <!-- lazydoc-ignore: internal -->
1065
+ """
1042
1066
  # TODO: add native support for pathlib.Path
1043
1067
  if isinstance(value, pathlib.Path):
1044
1068
  return str(value)
@@ -1054,6 +1078,10 @@ class Settings(BaseModel, validate_assignment=True):
1054
1078
  @field_validator("x_file_stream_max_line_bytes", mode="after")
1055
1079
  @classmethod
1056
1080
  def validate_file_stream_max_line_bytes(cls, value):
1081
+ """Validate the maximum line length for filestream JSONL files.
1082
+
1083
+ <!-- lazydoc-ignore: internal -->
1084
+ """
1057
1085
  if value is not None and value < 1:
1058
1086
  raise ValueError("File stream max line bytes must be greater than 0")
1059
1087
  return value
@@ -1061,6 +1089,10 @@ class Settings(BaseModel, validate_assignment=True):
1061
1089
  @field_validator("x_files_dir", mode="before")
1062
1090
  @classmethod
1063
1091
  def validate_x_files_dir(cls, value):
1092
+ """Validate the files directory.
1093
+
1094
+ <!-- lazydoc-ignore: internal -->
1095
+ """
1064
1096
  # TODO: add native support for pathlib.Path
1065
1097
  if isinstance(value, pathlib.Path):
1066
1098
  return str(value)
@@ -1069,6 +1101,10 @@ class Settings(BaseModel, validate_assignment=True):
1069
1101
  @field_validator("fork_from", mode="before")
1070
1102
  @classmethod
1071
1103
  def validate_fork_from(cls, value, values) -> Optional[RunMoment]:
1104
+ """Validate the fork_from field.
1105
+
1106
+ <!-- lazydoc-ignore: internal -->
1107
+ """
1072
1108
  run_moment = cls._runmoment_preprocessor(value)
1073
1109
 
1074
1110
  if hasattr(values, "data"):
@@ -1093,6 +1129,10 @@ class Settings(BaseModel, validate_assignment=True):
1093
1129
  @field_validator("http_proxy", mode="after")
1094
1130
  @classmethod
1095
1131
  def validate_http_proxy(cls, value):
1132
+ """Validate the HTTP proxy.
1133
+
1134
+ <!-- lazydoc-ignore: internal -->
1135
+ """
1096
1136
  if value is None:
1097
1137
  return None
1098
1138
  validate_url(value)
@@ -1101,6 +1141,10 @@ class Settings(BaseModel, validate_assignment=True):
1101
1141
  @field_validator("https_proxy", mode="after")
1102
1142
  @classmethod
1103
1143
  def validate_https_proxy(cls, value):
1144
+ """Validate the HTTPS proxy.
1145
+
1146
+ <!-- lazydoc-ignore: internal -->
1147
+ """
1104
1148
  if value is None:
1105
1149
  return None
1106
1150
  validate_url(value)
@@ -1109,11 +1153,19 @@ class Settings(BaseModel, validate_assignment=True):
1109
1153
  @field_validator("ignore_globs", mode="after")
1110
1154
  @classmethod
1111
1155
  def validate_ignore_globs(cls, value):
1156
+ """Validate the ignore globs.
1157
+
1158
+ <!-- lazydoc-ignore: internal -->
1159
+ """
1112
1160
  return tuple(value) if not isinstance(value, tuple) else value
1113
1161
 
1114
1162
  @field_validator("program", mode="before")
1115
1163
  @classmethod
1116
1164
  def validate_program(cls, value):
1165
+ """Validate the program path.
1166
+
1167
+ <!-- lazydoc-ignore: internal -->
1168
+ """
1117
1169
  # TODO: add native support for pathlib.Path
1118
1170
  if isinstance(value, pathlib.Path):
1119
1171
  return str(value)
@@ -1122,6 +1174,10 @@ class Settings(BaseModel, validate_assignment=True):
1122
1174
  @field_validator("program_abspath", mode="before")
1123
1175
  @classmethod
1124
1176
  def validate_program_abspath(cls, value):
1177
+ """Validate the absolute program path.
1178
+
1179
+ <!-- lazydoc-ignore: internal -->
1180
+ """
1125
1181
  # TODO: add native support for pathlib.Path
1126
1182
  if isinstance(value, pathlib.Path):
1127
1183
  return str(value)
@@ -1130,6 +1186,10 @@ class Settings(BaseModel, validate_assignment=True):
1130
1186
  @field_validator("program_relpath", mode="before")
1131
1187
  @classmethod
1132
1188
  def validate_program_relpath(cls, value):
1189
+ """Validate the relative program path.
1190
+
1191
+ <!-- lazydoc-ignore: internal -->
1192
+ """
1133
1193
  # TODO: add native support for pathlib.Path
1134
1194
  if isinstance(value, pathlib.Path):
1135
1195
  return str(value)
@@ -1138,6 +1198,10 @@ class Settings(BaseModel, validate_assignment=True):
1138
1198
  @field_validator("project", mode="after")
1139
1199
  @classmethod
1140
1200
  def validate_project(cls, value, values):
1201
+ """Validate the project name.
1202
+
1203
+ <!-- lazydoc-ignore: internal -->
1204
+ """
1141
1205
  if value is None:
1142
1206
  return None
1143
1207
  invalid_chars_list = list("/\\#?%:")
@@ -1155,6 +1219,10 @@ class Settings(BaseModel, validate_assignment=True):
1155
1219
  @field_validator("resume", mode="before")
1156
1220
  @classmethod
1157
1221
  def validate_resume(cls, value):
1222
+ """Validate the resume behavior.
1223
+
1224
+ <!-- lazydoc-ignore: internal -->
1225
+ """
1158
1226
  if value is False:
1159
1227
  return None
1160
1228
  if value is True:
@@ -1164,6 +1232,10 @@ class Settings(BaseModel, validate_assignment=True):
1164
1232
  @field_validator("resume_from", mode="before")
1165
1233
  @classmethod
1166
1234
  def validate_resume_from(cls, value, values) -> Optional[RunMoment]:
1235
+ """Validate the resume_from field.
1236
+
1237
+ <!-- lazydoc-ignore: internal -->
1238
+ """
1167
1239
  run_moment = cls._runmoment_preprocessor(value)
1168
1240
 
1169
1241
  if hasattr(values, "data"):
@@ -1186,6 +1258,10 @@ class Settings(BaseModel, validate_assignment=True):
1186
1258
  @field_validator("root_dir", mode="before")
1187
1259
  @classmethod
1188
1260
  def validate_root_dir(cls, value):
1261
+ """Validate the root directory.
1262
+
1263
+ <!-- lazydoc-ignore: internal -->
1264
+ """
1189
1265
  # TODO: add native support for pathlib.Path
1190
1266
  if isinstance(value, pathlib.Path):
1191
1267
  return str(value)
@@ -1194,6 +1270,10 @@ class Settings(BaseModel, validate_assignment=True):
1194
1270
  @field_validator("run_id", mode="after")
1195
1271
  @classmethod
1196
1272
  def validate_run_id(cls, value, values):
1273
+ """Validate the run ID.
1274
+
1275
+ <!-- lazydoc-ignore: internal -->
1276
+ """
1197
1277
  if value is None:
1198
1278
  return None
1199
1279
 
@@ -1213,6 +1293,10 @@ class Settings(BaseModel, validate_assignment=True):
1213
1293
  @field_validator("settings_system", mode="after")
1214
1294
  @classmethod
1215
1295
  def validate_settings_system(cls, value):
1296
+ """Validate the system settings file path.
1297
+
1298
+ <!-- lazydoc-ignore: internal -->
1299
+ """
1216
1300
  if value is None:
1217
1301
  return None
1218
1302
  elif isinstance(value, pathlib.Path):
@@ -1223,6 +1307,10 @@ class Settings(BaseModel, validate_assignment=True):
1223
1307
  @field_validator("x_service_wait", mode="after")
1224
1308
  @classmethod
1225
1309
  def validate_service_wait(cls, value):
1310
+ """Validate the service wait time.
1311
+
1312
+ <!-- lazydoc-ignore: internal -->
1313
+ """
1226
1314
  if value < 0:
1227
1315
  raise UsageError("Service wait time cannot be negative")
1228
1316
  return value
@@ -1230,6 +1318,10 @@ class Settings(BaseModel, validate_assignment=True):
1230
1318
  @field_validator("start_method", mode="after")
1231
1319
  @classmethod
1232
1320
  def validate_start_method(cls, value):
1321
+ """Validate the start method for subprocesses.
1322
+
1323
+ <!-- lazydoc-ignore: internal -->
1324
+ """
1233
1325
  if value is None:
1234
1326
  return value
1235
1327
  wandb.termwarn(
@@ -1248,6 +1340,10 @@ class Settings(BaseModel, validate_assignment=True):
1248
1340
  @field_validator("x_stats_gpu_device_ids", mode="before")
1249
1341
  @classmethod
1250
1342
  def validate_x_stats_gpu_device_ids(cls, value):
1343
+ """Validate the GPU device IDs.
1344
+
1345
+ <!-- lazydoc-ignore: internal -->
1346
+ """
1251
1347
  if isinstance(value, str):
1252
1348
  return json.loads(value)
1253
1349
  return value
@@ -1255,6 +1351,10 @@ class Settings(BaseModel, validate_assignment=True):
1255
1351
  @field_validator("x_stats_neuron_monitor_config_path", mode="before")
1256
1352
  @classmethod
1257
1353
  def validate_x_stats_neuron_monitor_config_path(cls, value):
1354
+ """Validate the path to the neuron-monitor config file.
1355
+
1356
+ <!-- lazydoc-ignore: internal -->
1357
+ """
1258
1358
  # TODO: add native support for pathlib.Path
1259
1359
  if isinstance(value, pathlib.Path):
1260
1360
  return str(value)
@@ -1263,6 +1363,10 @@ class Settings(BaseModel, validate_assignment=True):
1263
1363
  @field_validator("x_stats_open_metrics_endpoints", mode="before")
1264
1364
  @classmethod
1265
1365
  def validate_stats_open_metrics_endpoints(cls, value):
1366
+ """Validate the OpenMetrics endpoints.
1367
+
1368
+ <!-- lazydoc-ignore: internal -->
1369
+ """
1266
1370
  if isinstance(value, str):
1267
1371
  return json.loads(value)
1268
1372
  return value
@@ -1270,6 +1374,10 @@ class Settings(BaseModel, validate_assignment=True):
1270
1374
  @field_validator("x_stats_open_metrics_filters", mode="before")
1271
1375
  @classmethod
1272
1376
  def validate_stats_open_metrics_filters(cls, value):
1377
+ """Validate the OpenMetrics filters.
1378
+
1379
+ <!-- lazydoc-ignore: internal -->
1380
+ """
1273
1381
  if isinstance(value, str):
1274
1382
  return json.loads(value)
1275
1383
  return value
@@ -1277,6 +1385,10 @@ class Settings(BaseModel, validate_assignment=True):
1277
1385
  @field_validator("x_stats_open_metrics_http_headers", mode="before")
1278
1386
  @classmethod
1279
1387
  def validate_stats_open_metrics_http_headers(cls, value):
1388
+ """Validate the OpenMetrics HTTP headers.
1389
+
1390
+ <!-- lazydoc-ignore: internal -->
1391
+ """
1280
1392
  if isinstance(value, str):
1281
1393
  return json.loads(value)
1282
1394
  return value
@@ -1284,6 +1396,10 @@ class Settings(BaseModel, validate_assignment=True):
1284
1396
  @field_validator("x_stats_sampling_interval", mode="after")
1285
1397
  @classmethod
1286
1398
  def validate_stats_sampling_interval(cls, value):
1399
+ """Validate the stats sampling interval.
1400
+
1401
+ <!-- lazydoc-ignore: internal -->
1402
+ """
1287
1403
  if value < 0.1:
1288
1404
  raise UsageError("Stats sampling interval cannot be less than 0.1 seconds")
1289
1405
  return value
@@ -1291,6 +1407,10 @@ class Settings(BaseModel, validate_assignment=True):
1291
1407
  @field_validator("sweep_id", mode="after")
1292
1408
  @classmethod
1293
1409
  def validate_sweep_id(cls, value):
1410
+ """Validate the sweep ID.
1411
+
1412
+ <!-- lazydoc-ignore: internal -->
1413
+ """
1294
1414
  if value is None:
1295
1415
  return None
1296
1416
  if len(value) == 0:
@@ -1304,6 +1424,10 @@ class Settings(BaseModel, validate_assignment=True):
1304
1424
  @field_validator("sweep_param_path", mode="before")
1305
1425
  @classmethod
1306
1426
  def validate_sweep_param_path(cls, value):
1427
+ """Validate the sweep parameter path.
1428
+
1429
+ <!-- lazydoc-ignore: internal -->
1430
+ """
1307
1431
  # TODO: add native support for pathlib.Path
1308
1432
  if isinstance(value, pathlib.Path):
1309
1433
  return str(value)
@@ -1503,6 +1627,7 @@ class Settings(BaseModel, validate_assignment=True):
1503
1627
  @computed_field # type: ignore[prop-decorator]
1504
1628
  @property
1505
1629
  def run_mode(self) -> Literal["run", "offline-run"]:
1630
+ """The mode of the run. Can be either "run" or "offline-run"."""
1506
1631
  return "run" if not self._offline else "offline-run"
1507
1632
 
1508
1633
  @computed_field # type: ignore[prop-decorator]
@@ -1538,6 +1663,7 @@ class Settings(BaseModel, validate_assignment=True):
1538
1663
  @computed_field # type: ignore[prop-decorator]
1539
1664
  @property
1540
1665
  def sync_dir(self) -> str:
1666
+ """The directory for storing the run's files."""
1541
1667
  return _path_convert(
1542
1668
  self.wandb_dir,
1543
1669
  f"{self.run_mode}-{self.timespec}-{self.run_id}",
@@ -1552,11 +1678,13 @@ class Settings(BaseModel, validate_assignment=True):
1552
1678
  @computed_field # type: ignore[prop-decorator]
1553
1679
  @property
1554
1680
  def sync_symlink_latest(self) -> str:
1681
+ """Path to the symlink to the most recent run's transaction log file."""
1555
1682
  return _path_convert(self.wandb_dir, "latest-run")
1556
1683
 
1557
1684
  @computed_field # type: ignore[prop-decorator]
1558
1685
  @property
1559
1686
  def timespec(self) -> str:
1687
+ """The time specification for the run."""
1560
1688
  return self._start_datetime
1561
1689
 
1562
1690
  @computed_field # type: ignore[prop-decorator]
wandb/sdk/wandb_setup.py CHANGED
@@ -419,49 +419,49 @@ def setup(settings: Settings | None = None) -> _WandbSetup:
419
419
  overridden by subsequent `wandb.init()` calls.
420
420
 
421
421
  Example:
422
- ```python
423
- import multiprocessing
422
+ ```python
423
+ import multiprocessing
424
424
 
425
- import wandb
425
+ import wandb
426
426
 
427
427
 
428
- def run_experiment(params):
429
- with wandb.init(config=params):
430
- # Run experiment
431
- pass
428
+ def run_experiment(params):
429
+ with wandb.init(config=params):
430
+ # Run experiment
431
+ pass
432
432
 
433
433
 
434
- if __name__ == "__main__":
435
- # Start backend and set global config
436
- wandb.setup(settings={"project": "my_project"})
434
+ if __name__ == "__main__":
435
+ # Start backend and set global config
436
+ wandb.setup(settings={"project": "my_project"})
437
437
 
438
- # Define experiment parameters
439
- experiment_params = [
440
- {"learning_rate": 0.01, "epochs": 10},
441
- {"learning_rate": 0.001, "epochs": 20},
442
- ]
438
+ # Define experiment parameters
439
+ experiment_params = [
440
+ {"learning_rate": 0.01, "epochs": 10},
441
+ {"learning_rate": 0.001, "epochs": 20},
442
+ ]
443
443
 
444
- # Start multiple processes, each running a separate experiment
445
- processes = []
446
- for params in experiment_params:
447
- p = multiprocessing.Process(target=run_experiment, args=(params,))
448
- p.start()
449
- processes.append(p)
444
+ # Start multiple processes, each running a separate experiment
445
+ processes = []
446
+ for params in experiment_params:
447
+ p = multiprocessing.Process(target=run_experiment, args=(params,))
448
+ p.start()
449
+ processes.append(p)
450
450
 
451
- # Wait for all processes to complete
452
- for p in processes:
453
- p.join()
451
+ # Wait for all processes to complete
452
+ for p in processes:
453
+ p.join()
454
454
 
455
- # Optional: Explicitly shut down the backend
456
- wandb.teardown()
457
- ```
455
+ # Optional: Explicitly shut down the backend
456
+ wandb.teardown()
457
+ ```
458
458
  """
459
459
  return _setup(settings=settings)
460
460
 
461
461
 
462
462
  @wb_logging.log_to_all_runs()
463
463
  def teardown(exit_code: int | None = None) -> None:
464
- """Waits for wandb to finish and frees resources.
464
+ """Waits for W&B to finish and frees resources.
465
465
 
466
466
  Completes any runs that were not explicitly finished
467
467
  using `run.finish()` and waits for all data to be uploaded.
wandb/sdk/wandb_sweep.py CHANGED
@@ -45,11 +45,12 @@ def sweep(
45
45
  Make note the unique identifier, `sweep_id`, that is returned.
46
46
  At a later step provide the `sweep_id` to a sweep agent.
47
47
 
48
+ See [Sweep configuration structure](https://docs.wandb.ai/guides/sweeps/define-sweep-configuration)
49
+ for information on how to define your sweep.
50
+
48
51
  Args:
49
52
  sweep: The configuration of a hyperparameter search.
50
- (or configuration generator). See
51
- [Sweep configuration structure](https://docs.wandb.ai/guides/sweeps/define-sweep-configuration)
52
- for information on how to define your sweep.
53
+ (or configuration generator).
53
54
  If you provide a callable, ensure that the callable does
54
55
  not take arguments and that it returns a dictionary that
55
56
  conforms to the W&B sweep config spec.
@@ -64,7 +65,7 @@ def sweep(
64
65
  prior_runs: The run IDs of existing runs to add to this sweep.
65
66
 
66
67
  Returns:
67
- sweep_id: str. A unique identifier for the sweep.
68
+ str: A unique identifier for the sweep.
68
69
  """
69
70
  if callable(sweep):
70
71
  sweep = sweep()
@@ -99,16 +100,16 @@ def controller(
99
100
  ) -> "_WandbController":
100
101
  """Public sweep controller constructor.
101
102
 
102
- Usage:
103
- ```python
104
- import wandb
103
+ Examples:
104
+ ```python
105
+ import wandb
105
106
 
106
- tuner = wandb.controller(...)
107
- print(tuner.sweep_config)
108
- print(tuner.sweep_id)
109
- tuner.configure_search(...)
110
- tuner.configure_stopping(...)
111
- ```
107
+ tuner = wandb.controller(...)
108
+ print(tuner.sweep_config)
109
+ print(tuner.sweep_id)
110
+ tuner.configure_search(...)
111
+ tuner.configure_stopping(...)
112
+ ```
112
113
 
113
114
  """
114
115
  from ..wandb_controller import _WandbController
wandb/sdk/wandb_watch.py CHANGED
@@ -17,15 +17,13 @@ from .lib import telemetry
17
17
  if TYPE_CHECKING:
18
18
  import torch # type: ignore [import-not-found]
19
19
 
20
- from wandb.sdk.wandb_run import Run
21
-
22
20
  logger = logging.getLogger("wandb")
23
21
 
24
22
  _global_watch_idx = 0
25
23
 
26
24
 
27
25
  def _watch(
28
- run: Run,
26
+ run: wandb.Run,
29
27
  models: torch.nn.Module | Sequence[torch.nn.Module],
30
28
  criterion: torch.F | None = None,
31
29
  log: Literal["gradients", "parameters", "all"] | None = "gradients",
@@ -39,7 +37,7 @@ def _watch(
39
37
  extended to support arbitrary machine learning models in the future.
40
38
 
41
39
  Args:
42
- run (wandb.sdk.wandb_run.Run): The run object to log to.
40
+ run (wandb.Run): The run object to log to.
43
41
  models (Union[torch.nn.Module, Sequence[torch.nn.Module]]):
44
42
  A single model or a sequence of models to be monitored.
45
43
  criterion (Optional[torch.F]):
@@ -122,12 +120,12 @@ def _watch(
122
120
 
123
121
 
124
122
  def _unwatch(
125
- run: Run, models: torch.nn.Module | Sequence[torch.nn.Module] | None = None
123
+ run: wandb.Run, models: torch.nn.Module | Sequence[torch.nn.Module] | None = None
126
124
  ) -> None:
127
125
  """Remove pytorch model topology, gradient and parameter hooks.
128
126
 
129
127
  Args:
130
- run (wandb.sdk.wandb_run.Run):
128
+ run (wandb.Run):
131
129
  The run object to log to.
132
130
  models (torch.nn.Module | Sequence[torch.nn.Module]):
133
131
  Optional list of pytorch models that have had watch called on them
wandb/sync/sync.py CHANGED
@@ -54,6 +54,7 @@ class SyncThread(threading.Thread):
54
54
  log_path=None,
55
55
  append=None,
56
56
  skip_console=None,
57
+ replace_tags=None,
57
58
  ):
58
59
  threading.Thread.__init__(self)
59
60
  # mark this process as internal
@@ -71,6 +72,7 @@ class SyncThread(threading.Thread):
71
72
  self._log_path = log_path
72
73
  self._append = append
73
74
  self._skip_console = skip_console
75
+ self._replace_tags = replace_tags or {}
74
76
 
75
77
  self._tmp_dir = tempfile.TemporaryDirectory()
76
78
  atexit.register(self._tmp_dir.cleanup)
@@ -94,6 +96,11 @@ class SyncThread(threading.Thread):
94
96
  pb.run.entity = self._entity
95
97
  if self._job_type:
96
98
  pb.run.job_type = self._job_type
99
+ # Replace tags if specified
100
+ if self._replace_tags:
101
+ new_tags = [self._replace_tags.get(tag, tag) for tag in pb.run.tags]
102
+ pb.run.ClearField("tags")
103
+ pb.run.tags.extend(new_tags)
97
104
  pb.control.req_resp = True
98
105
  elif record_type in ("output", "output_raw") and self._skip_console:
99
106
  return pb, exit_pb, True
@@ -338,6 +345,7 @@ class SyncManager:
338
345
  log_path=None,
339
346
  append=None,
340
347
  skip_console=None,
348
+ replace_tags=None,
341
349
  ):
342
350
  self._sync_list = []
343
351
  self._thread = None
@@ -353,6 +361,7 @@ class SyncManager:
353
361
  self._log_path = log_path
354
362
  self._append = append
355
363
  self._skip_console = skip_console
364
+ self._replace_tags = replace_tags or {}
356
365
 
357
366
  def status(self):
358
367
  pass
@@ -376,6 +385,7 @@ class SyncManager:
376
385
  log_path=self._log_path,
377
386
  append=self._append,
378
387
  skip_console=self._skip_console,
388
+ replace_tags=self._replace_tags,
379
389
  )
380
390
  self._thread.start()
381
391
 
wandb/util.py CHANGED
@@ -341,6 +341,63 @@ def get_local_path_or_none(path_or_uri: str) -> Optional[str]:
341
341
  return None
342
342
 
343
343
 
344
+ def check_windows_valid_filename(path: Union[int, str]) -> bool:
345
+ r"""Verify that the given path does not contain any invalid characters for a Windows filename.
346
+
347
+ Windows filenames cannot contain the following characters:
348
+ < > : " \ / | ? *
349
+
350
+ For more details, refer to the official documentation:
351
+ https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file#naming-conventions
352
+
353
+ Args:
354
+ path: The file path to check, which can be either an integer or a string.
355
+
356
+ Returns:
357
+ bool: True if the path does not contain any invalid characters, False otherwise.
358
+ """
359
+ return not bool(re.search(r'[<>:"\\?*]', path)) # type: ignore
360
+
361
+
362
+ def make_file_path_upload_safe(path: str) -> str:
363
+ r"""Makes the provide path safe for file upload.
364
+
365
+ The filename is made safe by:
366
+ 1. Removing any leading slashes to prevent writing to absolute paths
367
+ 2. Replacing '.' and '..' with underscores to prevent directory traversal attacks
368
+
369
+ Raises:
370
+ ValueError: If running on Windows and the key contains invalid filename characters
371
+ (\, :, *, ?, ", <, >, |)
372
+ """
373
+ sys_platform = platform.system()
374
+ if sys_platform == "Windows" and not check_windows_valid_filename(path):
375
+ raise ValueError(
376
+ f"Path {path} is invalid. Please remove invalid filename characters"
377
+ r' (\, :, *, ?, ", <, >, |)'
378
+ )
379
+
380
+ # On Windows, convert forward slashes to backslashes.
381
+ # This ensures that the key is a valid filename on Windows.
382
+ if sys_platform == "Windows":
383
+ path = str(path).replace("/", os.sep)
384
+
385
+ # Avoid writing to absolute paths by striping any leading slashes.
386
+ # The key has already been validated for windows operating systems in util.check_windows_valid_filename
387
+ # This ensures the key does not contain invalid characters for windows, such as '\' or ':'.
388
+ # So we can check only for '/' in the key.
389
+ path = path.lstrip(os.sep)
390
+
391
+ # Avoid directory traversal by replacing dots with underscores.
392
+ paths = path.split(os.sep)
393
+ safe_paths = [
394
+ p.replace(".", "_") if p in (os.curdir, os.pardir) else p for p in paths
395
+ ]
396
+
397
+ # Recombine the key into a relative path.
398
+ return os.sep.join(safe_paths)
399
+
400
+
344
401
  def make_tarfile(
345
402
  output_filename: str,
346
403
  source_dir: str,
wandb/wandb_run.py CHANGED
@@ -1,7 +1,6 @@
1
1
  """Compatibility wandb_run module.
2
2
 
3
- In the future use:
4
- from wandb.sdk.wandb_run import Run
3
+ Please use `wandb.Run` instead.
5
4
  """
6
5
 
7
6
  from wandb.sdk.wandb_run import Run
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: wandb
3
- Version: 0.21.0
3
+ Version: 0.21.1
4
4
  Summary: A CLI and library for interacting with the Weights & Biases API.
5
5
  Project-URL: Source, https://github.com/wandb/wandb
6
6
  Project-URL: Bug Reports, https://github.com/wandb/wandb/issues