wandb 0.19.0__py3-none-any.whl → 0.19.1__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) 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 +0 -0
  9. wandb/cli/cli.py +2 -1
  10. wandb/env.py +1 -1
  11. wandb/errors/term.py +60 -1
  12. wandb/integration/keras/callbacks/tables_builder.py +3 -1
  13. wandb/integration/kfp/kfp_patch.py +25 -15
  14. wandb/integration/lightning/fabric/logger.py +3 -1
  15. wandb/integration/tensorboard/monkeypatch.py +3 -2
  16. wandb/jupyter.py +4 -5
  17. wandb/plot/bar.py +5 -6
  18. wandb/plot/histogram.py +1 -1
  19. wandb/plot/line_series.py +3 -3
  20. wandb/plot/pr_curve.py +7 -3
  21. wandb/plot/scatter.py +2 -1
  22. wandb/proto/v3/wandb_settings_pb2.py +25 -15
  23. wandb/proto/v4/wandb_settings_pb2.py +17 -15
  24. wandb/proto/v5/wandb_settings_pb2.py +17 -15
  25. wandb/sdk/artifacts/_validators.py +1 -3
  26. wandb/sdk/artifacts/artifact_manifest_entry.py +1 -1
  27. wandb/sdk/data_types/helper_types/bounding_boxes_2d.py +12 -2
  28. wandb/sdk/data_types/helper_types/image_mask.py +8 -2
  29. wandb/sdk/data_types/histogram.py +3 -3
  30. wandb/sdk/data_types/image.py +3 -1
  31. wandb/sdk/interface/interface.py +34 -5
  32. wandb/sdk/interface/interface_sock.py +2 -2
  33. wandb/sdk/internal/file_stream.py +4 -1
  34. wandb/sdk/internal/sender.py +4 -1
  35. wandb/sdk/internal/settings_static.py +17 -4
  36. wandb/sdk/launch/utils.py +1 -0
  37. wandb/sdk/lib/ipython.py +5 -27
  38. wandb/sdk/lib/printer.py +33 -20
  39. wandb/sdk/lib/progress.py +7 -1
  40. wandb/sdk/lib/sparkline.py +1 -2
  41. wandb/sdk/wandb_config.py +2 -2
  42. wandb/sdk/wandb_init.py +236 -243
  43. wandb/sdk/wandb_run.py +172 -231
  44. wandb/sdk/wandb_settings.py +104 -15
  45. {wandb-0.19.0.dist-info → wandb-0.19.1.dist-info}/METADATA +1 -1
  46. {wandb-0.19.0.dist-info → wandb-0.19.1.dist-info}/RECORD +49 -49
  47. {wandb-0.19.0.dist-info → wandb-0.19.1.dist-info}/WHEEL +0 -0
  48. {wandb-0.19.0.dist-info → wandb-0.19.1.dist-info}/entry_points.txt +0 -0
  49. {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))