wandb 0.19.0__py3-none-win32.whl → 0.19.1__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 (50) hide show
  1. wandb/__init__.py +1 -7
  2. wandb/__init__.pyi +211 -209
  3. wandb/apis/attrs.py +15 -4
  4. wandb/apis/public/api.py +8 -4
  5. wandb/apis/public/files.py +65 -12
  6. wandb/apis/public/runs.py +52 -7
  7. wandb/apis/public/sweeps.py +1 -1
  8. wandb/bin/gpu_stats.exe +0 -0
  9. wandb/bin/wandb-core +0 -0
  10. wandb/cli/cli.py +2 -1
  11. wandb/env.py +1 -1
  12. wandb/errors/term.py +60 -1
  13. wandb/integration/keras/callbacks/tables_builder.py +3 -1
  14. wandb/integration/kfp/kfp_patch.py +25 -15
  15. wandb/integration/lightning/fabric/logger.py +3 -1
  16. wandb/integration/tensorboard/monkeypatch.py +3 -2
  17. wandb/jupyter.py +4 -5
  18. wandb/plot/bar.py +5 -6
  19. wandb/plot/histogram.py +1 -1
  20. wandb/plot/line_series.py +3 -3
  21. wandb/plot/pr_curve.py +7 -3
  22. wandb/plot/scatter.py +2 -1
  23. wandb/proto/v3/wandb_settings_pb2.py +25 -15
  24. wandb/proto/v4/wandb_settings_pb2.py +17 -15
  25. wandb/proto/v5/wandb_settings_pb2.py +17 -15
  26. wandb/sdk/artifacts/_validators.py +1 -3
  27. wandb/sdk/artifacts/artifact_manifest_entry.py +1 -1
  28. wandb/sdk/data_types/helper_types/bounding_boxes_2d.py +12 -2
  29. wandb/sdk/data_types/helper_types/image_mask.py +8 -2
  30. wandb/sdk/data_types/histogram.py +3 -3
  31. wandb/sdk/data_types/image.py +3 -1
  32. wandb/sdk/interface/interface.py +34 -5
  33. wandb/sdk/interface/interface_sock.py +2 -2
  34. wandb/sdk/internal/file_stream.py +4 -1
  35. wandb/sdk/internal/sender.py +4 -1
  36. wandb/sdk/internal/settings_static.py +17 -4
  37. wandb/sdk/launch/utils.py +1 -0
  38. wandb/sdk/lib/ipython.py +5 -27
  39. wandb/sdk/lib/printer.py +33 -20
  40. wandb/sdk/lib/progress.py +7 -1
  41. wandb/sdk/lib/sparkline.py +1 -2
  42. wandb/sdk/wandb_config.py +2 -2
  43. wandb/sdk/wandb_init.py +236 -243
  44. wandb/sdk/wandb_run.py +172 -231
  45. wandb/sdk/wandb_settings.py +104 -15
  46. {wandb-0.19.0.dist-info → wandb-0.19.1.dist-info}/METADATA +1 -1
  47. {wandb-0.19.0.dist-info → wandb-0.19.1.dist-info}/RECORD +50 -50
  48. {wandb-0.19.0.dist-info → wandb-0.19.1.dist-info}/WHEEL +0 -0
  49. {wandb-0.19.0.dist-info → wandb-0.19.1.dist-info}/entry_points.txt +0 -0
  50. {wandb-0.19.0.dist-info → wandb-0.19.1.dist-info}/licenses/LICENSE +0 -0
wandb/sdk/wandb_run.py CHANGED
@@ -19,7 +19,6 @@ from collections.abc import Mapping
19
19
  from dataclasses import dataclass, field
20
20
  from datetime import datetime, timedelta, timezone
21
21
  from enum import IntEnum
22
- from functools import reduce
23
22
  from types import TracebackType
24
23
  from typing import TYPE_CHECKING, Any, Callable, Literal, NamedTuple, Sequence, TextIO
25
24
 
@@ -83,9 +82,7 @@ from .lib import (
83
82
  telemetry,
84
83
  )
85
84
  from .lib.exit_hooks import ExitHooks
86
- from .lib.gitlib import GitRepo
87
85
  from .lib.mailbox import MailboxError, MailboxHandle, MailboxProbe, MailboxProgress
88
- from .lib.proto_util import message_to_dict
89
86
  from .wandb_alerts import AlertLevel
90
87
  from .wandb_settings import Settings
91
88
  from .wandb_setup import _WandbSetup
@@ -520,17 +517,7 @@ class Run:
520
517
  _telemetry_obj_flushed: bytes
521
518
 
522
519
  _teardown_hooks: list[TeardownHook]
523
- _tags: tuple[Any, ...] | None
524
520
 
525
- _entity: str | None
526
- _project: str | None
527
- _group: str | None
528
- _job_type: str | None
529
- _name: str | None
530
- _notes: str | None
531
- _sweep_id: str | None
532
-
533
- _run_obj: RunRecord | None
534
521
  _backend: wandb.sdk.backend.backend.Backend | None
535
522
  _internal_run_interface: wandb.sdk.interface.interface_queue.InterfaceQueue | None
536
523
  _wl: _WandbSetup | None
@@ -567,6 +554,8 @@ class Run:
567
554
  _is_finished: bool
568
555
  _settings: Settings
569
556
 
557
+ _forked: bool
558
+
570
559
  _launch_artifacts: dict[str, Any] | None
571
560
  _printer: printer.Printer
572
561
 
@@ -605,16 +594,15 @@ class Run:
605
594
  self._config._set_callback(self._config_callback)
606
595
  self._config._set_artifact_callback(self._config_artifact_callback)
607
596
  self._config._set_settings(self._settings)
608
- self._backend = None
609
- self._internal_run_interface = None
597
+
610
598
  # todo: perhaps this should be a property that is a noop on a finished run
611
599
  self.summary = wandb_summary.Summary(
612
600
  self._summary_get_current_summary_callback,
613
601
  )
614
602
  self.summary._set_update_callback(self._summary_update_callback)
615
- self._step = 0
616
- self._torch_history: wandb_torch.TorchHistory | None = None # type: ignore
617
603
 
604
+ self._step = 0
605
+ self._starting_step = 0
618
606
  # todo: eventually would be nice to make this configurable using self._settings._start_time
619
607
  # need to test (jhr): if you set start time to 2 days ago and run a test for 15 minutes,
620
608
  # does the total time get calculated right (not as 2 days and 15 minutes)?
@@ -623,36 +611,27 @@ class Run:
623
611
  _datatypes_set_callback(self._datatypes_callback)
624
612
 
625
613
  self._printer = printer.new_printer()
626
- self._wl = None
627
614
 
628
- self._entity = None
629
- self._project = None
630
- self._group = None
631
- self._job_type = None
632
- self._run_id = self._settings.run_id
633
- self._starting_step = 0
634
- self._name = None
635
- self._notes = None
636
- self._tags = None
637
- self._remote_url = None
638
- self._commit = None
639
- self._sweep_id = None
615
+ self._torch_history: wandb_torch.TorchHistory | None = None # type: ignore
616
+
617
+ self._backend = None
618
+ self._internal_run_interface = None
619
+ self._wl = None
640
620
 
641
621
  self._hooks = None
642
622
  self._teardown_hooks = []
623
+
624
+ self._output_writer = None
643
625
  self._out_redir = None
644
626
  self._err_redir = None
645
627
  self._stdout_slave_fd = None
646
628
  self._stderr_slave_fd = None
629
+
647
630
  self._exit_code = None
648
631
  self._exit_result = None
649
632
 
650
- self._output_writer = None
651
633
  self._used_artifact_slots: dict[str, str] = {}
652
634
 
653
- # Returned from backend request_run(), set from wandb_init?
654
- self._run_obj = None
655
-
656
635
  # Created when the run "starts".
657
636
  self._run_status_checker = None
658
637
 
@@ -670,9 +649,6 @@ class Run:
670
649
 
671
650
  self._atexit_cleanup_called = False
672
651
 
673
- # Pull info from settings
674
- self._init_from_settings(self._settings)
675
-
676
652
  # Initial scope setup for sentry.
677
653
  # This might get updated when the actual run comes back.
678
654
  wandb._sentry.configure_scope(
@@ -737,7 +713,7 @@ class Run:
737
713
  self._is_finished = False
738
714
 
739
715
  self._attach_pid = os.getpid()
740
-
716
+ self._forked = False
741
717
  # for now, use runid as attach id, this could/should be versioned in the future
742
718
  if not self._settings.x_disable_service:
743
719
  self._attach_id = self._settings.run_id
@@ -813,71 +789,6 @@ class Run:
813
789
  raise Exception(f"Attribute {attr} is not supported on Run object.")
814
790
  super().__setattr__(attr, value)
815
791
 
816
- def _update_settings(self, settings: Settings) -> None:
817
- self._settings = settings
818
- self._init_from_settings(settings)
819
-
820
- def _init_from_settings(self, settings: Settings) -> None:
821
- if settings.entity is not None:
822
- self._entity = settings.entity
823
- if settings.project is not None:
824
- self._project = settings.project
825
- if settings.run_group is not None:
826
- self._group = settings.run_group
827
- if settings.run_job_type is not None:
828
- self._job_type = settings.run_job_type
829
- if settings.run_name is not None:
830
- self._name = settings.run_name
831
- if settings.run_notes is not None:
832
- self._notes = settings.run_notes
833
- if settings.run_tags is not None:
834
- self._tags = settings.run_tags
835
- if settings.sweep_id is not None:
836
- self._sweep_id = settings.sweep_id
837
-
838
- def _make_proto_run(self, run: RunRecord) -> None:
839
- """Populate protocol buffer RunData for interface/interface."""
840
- if self._entity is not None:
841
- run.entity = self._entity
842
- if self._project is not None:
843
- run.project = self._project
844
- if self._group is not None:
845
- run.run_group = self._group
846
- if self._job_type is not None:
847
- run.job_type = self._job_type
848
- if self._run_id is not None:
849
- run.run_id = self._run_id
850
- if self._name is not None:
851
- run.display_name = self._name
852
- if self._notes is not None:
853
- run.notes = self._notes
854
- if self._tags is not None:
855
- for tag in self._tags:
856
- run.tags.append(tag)
857
- if self._start_time is not None:
858
- run.start_time.FromMicroseconds(int(self._start_time * 1e6))
859
- if self._remote_url is not None:
860
- run.git.remote_url = self._remote_url
861
- if self._commit is not None:
862
- run.git.commit = self._commit
863
- if self._sweep_id is not None:
864
- run.sweep_id = self._sweep_id
865
- # Note: run.config is set in interface/interface:_make_run()
866
-
867
- def _populate_git_info(self) -> None:
868
- # Use user provided git info if available otherwise resolve it from the environment
869
- try:
870
- repo = GitRepo(
871
- root=self._settings.git_root,
872
- remote=self._settings.git_remote,
873
- remote_url=self._settings.git_remote_url,
874
- commit=self._settings.git_commit,
875
- lazy=False,
876
- )
877
- self._remote_url, self._commit = repo.remote_url, repo.last_commit
878
- except Exception:
879
- wandb.termwarn("Cannot find valid git repo associated with this directory.")
880
-
881
792
  def __deepcopy__(self, memo: dict[int, Any]) -> Run:
882
793
  return self
883
794
 
@@ -948,18 +859,16 @@ class Run:
948
859
  Display names are not guaranteed to be unique and may be descriptive.
949
860
  By default, they are randomly generated.
950
861
  """
951
- if self._name:
952
- return self._name
953
- if not self._run_obj:
954
- return None
955
- return self._run_obj.display_name
862
+ if self._settings.run_name:
863
+ return self._settings.run_name
864
+ return None
956
865
 
957
866
  @name.setter
958
867
  @_run_decorator._noop_on_finish()
959
868
  def name(self, name: str) -> None:
960
869
  with telemetry.context(run=self) as tel:
961
870
  tel.feature.set_run_name = True
962
- self._name = name
871
+ self._settings.run_name = name
963
872
  if self._backend and self._backend.interface:
964
873
  self._backend.interface.publish_run(self)
965
874
 
@@ -971,16 +880,12 @@ class Run:
971
880
  Notes can be a multiline string and can also use markdown and latex equations
972
881
  inside `$$`, like `$x + 3$`.
973
882
  """
974
- if self._notes:
975
- return self._notes
976
- if not self._run_obj:
977
- return None
978
- return self._run_obj.notes
883
+ return self._settings.run_notes
979
884
 
980
885
  @notes.setter
981
886
  @_run_decorator._noop_on_finish()
982
887
  def notes(self, notes: str) -> None:
983
- self._notes = notes
888
+ self._settings.run_notes = notes
984
889
  if self._backend and self._backend.interface:
985
890
  self._backend.interface.publish_run(self)
986
891
 
@@ -988,18 +893,14 @@ class Run:
988
893
  @_run_decorator._attach
989
894
  def tags(self) -> tuple | None:
990
895
  """Tags associated with the run, if there are any."""
991
- if self._tags:
992
- return self._tags
993
- if self._run_obj:
994
- return tuple(self._run_obj.tags)
995
- return None
896
+ return self._settings.run_tags or ()
996
897
 
997
898
  @tags.setter
998
899
  @_run_decorator._noop_on_finish()
999
900
  def tags(self, tags: Sequence) -> None:
1000
901
  with telemetry.context(run=self) as tel:
1001
902
  tel.feature.set_run_tags = True
1002
- self._tags = tuple(tags)
903
+ self._settings.run_tags = tuple(tags)
1003
904
  if self._backend and self._backend.interface:
1004
905
  self._backend.interface.publish_run(self)
1005
906
 
@@ -1008,22 +909,25 @@ class Run:
1008
909
  def id(self) -> str:
1009
910
  """Identifier for this run."""
1010
911
  if TYPE_CHECKING:
1011
- assert self._run_id is not None
1012
- return self._run_id
912
+ assert self._settings.run_id is not None
913
+ return self._settings.run_id
1013
914
 
1014
915
  @property
1015
916
  @_run_decorator._attach
1016
917
  def sweep_id(self) -> str | None:
1017
918
  """ID of the sweep associated with the run, if there is one."""
1018
- if not self._run_obj:
1019
- return None
1020
- return self._run_obj.sweep_id or None
919
+ return self._settings.sweep_id
1021
920
 
1022
921
  def _get_path(self) -> str:
1023
- parts = [
1024
- e for e in [self._entity, self._project, self._run_id] if e is not None
1025
- ]
1026
- return "/".join(parts)
922
+ return "/".join(
923
+ e
924
+ for e in [
925
+ self._settings.entity,
926
+ self._settings.project,
927
+ self._settings.run_id,
928
+ ]
929
+ if e is not None
930
+ )
1027
931
 
1028
932
  @property
1029
933
  @_run_decorator._attach
@@ -1035,33 +939,23 @@ class Run:
1035
939
  """
1036
940
  return self._get_path()
1037
941
 
1038
- def _get_start_time(self) -> float:
1039
- return (
1040
- self._start_time
1041
- if not self._run_obj
1042
- else (self._run_obj.start_time.ToMicroseconds() / 1e6)
1043
- )
1044
-
1045
942
  @property
1046
943
  @_run_decorator._attach
1047
944
  def start_time(self) -> float:
1048
945
  """Unix timestamp (in seconds) of when the run started."""
1049
- return self._get_start_time()
1050
-
1051
- def _get_starting_step(self) -> int:
1052
- return self._starting_step if not self._run_obj else self._run_obj.starting_step
946
+ return self._start_time
1053
947
 
1054
948
  @property
1055
949
  @_run_decorator._attach
1056
950
  def starting_step(self) -> int:
1057
951
  """The first step of the run."""
1058
- return self._get_starting_step()
952
+ return self._starting_step
1059
953
 
1060
954
  @property
1061
955
  @_run_decorator._attach
1062
956
  def resumed(self) -> bool:
1063
957
  """True if the run was resumed, False otherwise."""
1064
- return self._run_obj.resumed if self._run_obj else False
958
+ return self._settings.resumed
1065
959
 
1066
960
  @property
1067
961
  @_run_decorator._attach
@@ -1072,10 +966,6 @@ class Run:
1072
966
  """
1073
967
  return self._step
1074
968
 
1075
- def project_name(self) -> str:
1076
- # TODO: deprecate this in favor of project
1077
- return self._run_obj.project if self._run_obj else ""
1078
-
1079
969
  @property
1080
970
  @_run_decorator._attach
1081
971
  def mode(self) -> str:
@@ -1100,9 +990,6 @@ class Run:
1100
990
  def disabled(self) -> bool:
1101
991
  return self._settings._noop
1102
992
 
1103
- def _get_group(self) -> str:
1104
- return self._run_obj.run_group if self._run_obj else ""
1105
-
1106
993
  @property
1107
994
  @_run_decorator._attach
1108
995
  def group(self) -> str:
@@ -1115,12 +1002,16 @@ class Run:
1115
1002
  If you are doing cross-validation you should give all the cross-validation
1116
1003
  folds the same group.
1117
1004
  """
1118
- return self._get_group()
1005
+ return self._settings.run_group or ""
1119
1006
 
1120
1007
  @property
1121
1008
  @_run_decorator._attach
1122
1009
  def job_type(self) -> str:
1123
- return self._run_obj.job_type if self._run_obj else ""
1010
+ return self._settings.run_job_type or ""
1011
+
1012
+ def project_name(self) -> str:
1013
+ # TODO: deprecate this in favor of project
1014
+ return self._settings.project or ""
1124
1015
 
1125
1016
  @property
1126
1017
  @_run_decorator._attach
@@ -1167,7 +1058,9 @@ class Run:
1167
1058
  run.log_code(
1168
1059
  "../",
1169
1060
  include_fn=lambda path: path.endswith(".py") or path.endswith(".ipynb"),
1170
- exclude_fn=lambda path, root: os.path.relpath(path, root).startswith("cache/"),
1061
+ exclude_fn=lambda path, root: os.path.relpath(path, root).startswith(
1062
+ "cache/"
1063
+ ),
1171
1064
  )
1172
1065
  ```
1173
1066
 
@@ -1184,9 +1077,11 @@ class Run:
1184
1077
  notebook_name = self.settings.x_jupyter_name
1185
1078
  else:
1186
1079
  notebook_name = self.settings.x_jupyter_path
1187
- name_string = f"{self._project}-{notebook_name}"
1080
+ name_string = f"{self._settings.project}-{notebook_name}"
1188
1081
  else:
1189
- name_string = f"{self._project}-{self._settings.program_relpath}"
1082
+ name_string = (
1083
+ f"{self._settings.project}-{self._settings.program_relpath}"
1084
+ )
1190
1085
  name = wandb.util.make_artifact_name_safe(f"source-{name_string}")
1191
1086
  art = wandb.Artifact(name, "code")
1192
1087
  files_added = False
@@ -1211,16 +1106,6 @@ class Run:
1211
1106
 
1212
1107
  return self._log_artifact(art)
1213
1108
 
1214
- def get_url(self) -> str | None:
1215
- """Return the url for the W&B run, if there is one.
1216
-
1217
- Offline runs will not have a url.
1218
- """
1219
- if self._settings._offline:
1220
- wandb.termwarn("URL not available in offline run")
1221
- return None
1222
- return self._settings.run_url
1223
-
1224
1109
  def get_project_url(self) -> str | None:
1225
1110
  """Return the url for the W&B project associated with the run, if there is one.
1226
1111
 
@@ -1238,6 +1123,16 @@ class Run:
1238
1123
  return None
1239
1124
  return self._settings.sweep_url
1240
1125
 
1126
+ def get_url(self) -> str | None:
1127
+ """Return the url for the W&B run, if there is one.
1128
+
1129
+ Offline runs will not have a url.
1130
+ """
1131
+ if self._settings._offline:
1132
+ wandb.termwarn("URL not available in offline run")
1133
+ return None
1134
+ return self._settings.run_url
1135
+
1241
1136
  @property
1242
1137
  @_run_decorator._attach
1243
1138
  def url(self) -> str | None:
@@ -1251,7 +1146,7 @@ class Run:
1251
1146
 
1252
1147
  Entity can be a username or the name of a team or organization.
1253
1148
  """
1254
- return self._entity or ""
1149
+ return self._settings.entity or ""
1255
1150
 
1256
1151
  def _label_internal(
1257
1152
  self,
@@ -1342,10 +1237,19 @@ class Run:
1342
1237
  @_run_decorator._attach
1343
1238
  def display(self, height: int = 420, hidden: bool = False) -> bool:
1344
1239
  """Display this run in jupyter."""
1345
- if self._settings._jupyter:
1346
- ipython.display_html(self.to_html(height, hidden))
1240
+ if self._settings.silent:
1241
+ return False
1242
+
1243
+ if not ipython.in_jupyter():
1244
+ return False
1245
+
1246
+ try:
1247
+ from IPython import display
1248
+
1249
+ display.display(display.HTML(self.to_html(height, hidden)))
1347
1250
  return True
1348
- else:
1251
+
1252
+ except ImportError:
1349
1253
  wandb.termwarn(".display() only works in jupyter environments")
1350
1254
  return False
1351
1255
 
@@ -1548,13 +1452,13 @@ class Run:
1548
1452
  def _set_teardown_hooks(self, hooks: list[TeardownHook]) -> None:
1549
1453
  self._teardown_hooks = hooks
1550
1454
 
1551
- def _set_run_obj(self, run_obj: RunRecord) -> None:
1552
- self._run_obj = run_obj
1553
- if self.settings._offline:
1554
- return
1455
+ def _set_run_obj(self, run_obj: RunRecord) -> None: # noqa: C901
1456
+ if run_obj.starting_step:
1457
+ self._starting_step = run_obj.starting_step
1458
+ self._step = run_obj.starting_step
1555
1459
 
1556
- self._entity = run_obj.entity
1557
- self._project = run_obj.project
1460
+ if run_obj.start_time:
1461
+ self._start_time = run_obj.start_time.ToMicroseconds() / 1e6
1558
1462
 
1559
1463
  # Grab the config from resuming
1560
1464
  if run_obj.config:
@@ -1571,40 +1475,61 @@ class Run:
1571
1475
  summary_dict[orig.key] = json.loads(orig.value_json)
1572
1476
  if summary_dict:
1573
1477
  self.summary.update(summary_dict)
1574
- self._step = self._get_starting_step()
1575
1478
 
1576
1479
  # update settings from run_obj
1577
- run_start_settings = message_to_dict(self._run_obj)
1578
- param_map = {
1579
- "run_id": "run_id",
1580
- "entity": "entity",
1581
- "project": "project",
1582
- "run_group": "run_group",
1583
- "job_type": "run_job_type",
1584
- "display_name": "run_name",
1585
- "notes": "run_notes",
1586
- "tags": "run_tags",
1587
- "sweep_id": "sweep_id",
1588
- "host": "host",
1589
- "resumed": "resumed",
1590
- "git.remote_url": "git_remote_url",
1591
- "git.commit": "git_commit",
1592
- }
1593
- run_settings = {
1594
- name: reduce(lambda d, k: d.get(k, {}), attr.split("."), run_start_settings)
1595
- for attr, name in param_map.items()
1596
- }
1597
- run_settings = {key: value for key, value in run_settings.items() if value}
1598
-
1599
- if run_settings:
1600
- self._settings.update_from_dict(run_settings)
1601
- self._update_settings(self._settings)
1480
+ if run_obj.run_id:
1481
+ self._settings.run_id = run_obj.run_id
1482
+ if run_obj.entity:
1483
+ self._settings.entity = run_obj.entity
1484
+ if run_obj.project:
1485
+ self._settings.project = run_obj.project
1486
+ if run_obj.run_group:
1487
+ self._settings.run_group = run_obj.run_group
1488
+ if run_obj.job_type:
1489
+ self._settings.run_job_type = run_obj.job_type
1490
+ if run_obj.display_name:
1491
+ self._settings.run_name = run_obj.display_name
1492
+ if run_obj.notes:
1493
+ self._settings.run_notes = run_obj.notes
1494
+ if run_obj.tags:
1495
+ self._settings.run_tags = run_obj.tags
1496
+ if run_obj.sweep_id:
1497
+ self._settings.sweep_id = run_obj.sweep_id
1498
+ if run_obj.host:
1499
+ self._settings.host = run_obj.host
1500
+ if run_obj.resumed:
1501
+ self._settings.resumed = run_obj.resumed
1502
+ if run_obj.git:
1503
+ if run_obj.git.remote_url:
1504
+ self._settings.git_remote_url = run_obj.git.remote_url
1505
+ if run_obj.git.commit:
1506
+ self._settings.git_commit = run_obj.git.commit
1507
+
1508
+ if run_obj.forked:
1509
+ self._forked = run_obj.forked
1602
1510
 
1603
1511
  wandb._sentry.configure_scope(
1604
1512
  process_context="user",
1605
1513
  tags=dict(self._settings),
1606
1514
  )
1607
1515
 
1516
+ def _populate_git_info(self) -> None:
1517
+ from .lib.gitlib import GitRepo
1518
+
1519
+ # Use user provided git info if available otherwise resolve it from the environment
1520
+ try:
1521
+ repo = GitRepo(
1522
+ root=self._settings.git_root,
1523
+ remote=self._settings.git_remote,
1524
+ remote_url=self._settings.git_remote_url,
1525
+ commit=self._settings.git_commit,
1526
+ lazy=False,
1527
+ )
1528
+ self._settings.git_remote_url = repo.remote_url
1529
+ self._settings.git_commit = repo.last_commit
1530
+ except Exception:
1531
+ wandb.termwarn("Cannot find valid git repo associated with this directory.")
1532
+
1608
1533
  def _add_singleton(
1609
1534
  self, data_type: str, key: str, value: dict[int | str, str]
1610
1535
  ) -> None:
@@ -1715,12 +1640,14 @@ class Run:
1715
1640
  the following results in two sections named "train" and "validate":
1716
1641
 
1717
1642
  ```
1718
- run.log({
1719
- "train/accuracy": 0.9,
1720
- "train/loss": 30,
1721
- "validate/accuracy": 0.8,
1722
- "validate/loss": 20,
1723
- })
1643
+ run.log(
1644
+ {
1645
+ "train/accuracy": 0.9,
1646
+ "train/loss": 30,
1647
+ "validate/accuracy": 0.8,
1648
+ "validate/loss": 20,
1649
+ }
1650
+ )
1724
1651
  ```
1725
1652
 
1726
1653
  Only one level of nesting is supported; `run.log({"a/b/c": 1})`
@@ -1846,7 +1773,9 @@ class Run:
1846
1773
  run = wandb.init()
1847
1774
  examples = []
1848
1775
  for i in range(3):
1849
- pixels = np.random.randint(low=0, high=256, size=(100, 100, 3), dtype=np.uint8)
1776
+ pixels = np.random.randint(
1777
+ low=0, high=256, size=(100, 100, 3), dtype=np.uint8
1778
+ )
1850
1779
  pil_image = PILImage.fromarray(pixels, mode="RGB")
1851
1780
  image = wandb.Image(pil_image, caption=f"random field {i}")
1852
1781
  examples.append(image)
@@ -1861,7 +1790,9 @@ class Run:
1861
1790
 
1862
1791
  run = wandb.init()
1863
1792
  # axes are (time, channel, height, width)
1864
- frames = np.random.randint(low=0, high=256, size=(10, 3, 100, 100), dtype=np.uint8)
1793
+ frames = np.random.randint(
1794
+ low=0, high=256, size=(10, 3, 100, 100), dtype=np.uint8
1795
+ )
1865
1796
  run.log({"video": wandb.Video(frames, fps=4)})
1866
1797
  ```
1867
1798
 
@@ -2203,8 +2134,8 @@ class Run:
2203
2134
  #
2204
2135
  # TODO: Why not do this in _atexit_cleanup()?
2205
2136
  service = self._wl and self._wl.service
2206
- if service and self._run_id:
2207
- service.inform_finish(run_id=self._run_id)
2137
+ if service and self._settings.run_id:
2138
+ service.inform_finish(run_id=self._settings.run_id)
2208
2139
 
2209
2140
  finally:
2210
2141
  module.unset_globals()
@@ -2517,12 +2448,12 @@ class Run:
2517
2448
 
2518
2449
  import_telemetry_set = telemetry.list_telemetry_imports()
2519
2450
  import_hook_fn = functools.partial(_telemetry_import_hook, self)
2520
- if not self._run_id:
2451
+ if not self._settings.run_id:
2521
2452
  return
2522
2453
  for module_name in import_telemetry_set:
2523
2454
  register_post_import_hook(
2524
2455
  import_hook_fn,
2525
- self._run_id,
2456
+ self._settings.run_id,
2526
2457
  module_name,
2527
2458
  )
2528
2459
 
@@ -2534,7 +2465,10 @@ class Run:
2534
2465
  self._telemetry_obj_active = True
2535
2466
  self._telemetry_flush()
2536
2467
 
2537
- self._detect_and_apply_job_inputs()
2468
+ try:
2469
+ self._detect_and_apply_job_inputs()
2470
+ except Exception as e:
2471
+ logger.error("Problem applying launch job inputs", exc_info=e)
2538
2472
 
2539
2473
  # object is about to be returned to the user, don't let them modify it
2540
2474
  self._freeze()
@@ -2715,8 +2649,8 @@ class Run:
2715
2649
  if self._run_status_checker:
2716
2650
  self._run_status_checker.join()
2717
2651
 
2718
- if self._run_id:
2719
- self._unregister_telemetry_import_hooks(self._run_id)
2652
+ if self._settings.run_id:
2653
+ self._unregister_telemetry_import_hooks(self._settings.run_id)
2720
2654
 
2721
2655
  @staticmethod
2722
2656
  def _unregister_telemetry_import_hooks(run_id: str) -> None:
@@ -3051,10 +2985,14 @@ class Run:
3051
2985
  """
3052
2986
  if self._settings._offline:
3053
2987
  raise TypeError("Cannot use artifact when in offline mode.")
3054
- r = self._run_obj
3055
- assert r is not None
3056
- api = internal.Api(default_settings={"entity": r.entity, "project": r.project})
3057
- api.set_current_run_id(self._run_id)
2988
+
2989
+ api = internal.Api(
2990
+ default_settings={
2991
+ "entity": self._settings.entity,
2992
+ "project": self._settings.project,
2993
+ }
2994
+ )
2995
+ api.set_current_run_id(self._settings.run_id)
3058
2996
 
3059
2997
  if isinstance(artifact_or_name, str):
3060
2998
  if self._launch_artifact_mapping:
@@ -3085,8 +3023,8 @@ class Run:
3085
3023
  self._used_artifact_slots[use_as] = artifact.id
3086
3024
  api.use_artifact(
3087
3025
  artifact.id,
3088
- entity_name=r.entity,
3089
- project_name=r.project,
3026
+ entity_name=self._settings.entity,
3027
+ project_name=self._settings.project,
3090
3028
  use_as=use_as or artifact_or_name,
3091
3029
  )
3092
3030
  else:
@@ -3211,12 +3149,12 @@ class Run:
3211
3149
  Returns:
3212
3150
  An `Artifact` object.
3213
3151
  """
3214
- if self._get_group() == "" and distributed_id is None:
3152
+ if self._settings.run_group is None and distributed_id is None:
3215
3153
  raise TypeError(
3216
3154
  "Cannot upsert artifact unless run is in a group or distributed_id is provided"
3217
3155
  )
3218
3156
  if distributed_id is None:
3219
- distributed_id = self._get_group()
3157
+ distributed_id = self._settings.run_group or ""
3220
3158
  return self._log_artifact(
3221
3159
  artifact_or_path,
3222
3160
  name=name,
@@ -3264,12 +3202,12 @@ class Run:
3264
3202
  Returns:
3265
3203
  An `Artifact` object.
3266
3204
  """
3267
- if self._get_group() == "" and distributed_id is None:
3205
+ if self._settings.run_group is None and distributed_id is None:
3268
3206
  raise TypeError(
3269
3207
  "Cannot finish artifact unless run is in a group or distributed_id is provided"
3270
3208
  )
3271
3209
  if distributed_id is None:
3272
- distributed_id = self._get_group()
3210
+ distributed_id = self._settings.run_group or ""
3273
3211
 
3274
3212
  return self._log_artifact(
3275
3213
  artifact_or_path,
@@ -3349,10 +3287,10 @@ class Run:
3349
3287
  return artifact
3350
3288
 
3351
3289
  def _public_api(self, overrides: dict[str, str] | None = None) -> PublicApi:
3352
- overrides = {"run": self._run_id} # type: ignore
3353
- if not (self._settings._offline or self._run_obj is None):
3354
- overrides["entity"] = self._run_obj.entity
3355
- overrides["project"] = self._run_obj.project
3290
+ overrides = {"run": self._settings.run_id} # type: ignore
3291
+ if not self._settings._offline:
3292
+ overrides["entity"] = self._settings.entity or ""
3293
+ overrides["project"] = self._settings.project or ""
3356
3294
  return public.Api(overrides)
3357
3295
 
3358
3296
  # TODO(jhr): annotate this
@@ -3395,7 +3333,10 @@ class Run:
3395
3333
  aliases: list[str] | None = None,
3396
3334
  ) -> tuple[Artifact, list[str]]:
3397
3335
  if isinstance(artifact_or_path, (str, os.PathLike)):
3398
- name = name or f"run-{self._run_id}-{os.path.basename(artifact_or_path)}"
3336
+ name = (
3337
+ name
3338
+ or f"run-{self._settings.run_id}-{os.path.basename(artifact_or_path)}"
3339
+ )
3399
3340
  artifact = wandb.Artifact(name, type or "unspecified")
3400
3341
  if os.path.isfile(artifact_or_path):
3401
3342
  artifact.add_file(str(artifact_or_path))