wandb_settings.py 77 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309
  1. from __future__ import annotations
  2. import json
  3. import logging
  4. import os
  5. import pathlib
  6. import platform
  7. import re
  8. import shutil
  9. import socket
  10. import sys
  11. import traceback
  12. from collections.abc import Sequence
  13. from datetime import datetime
  14. # Optional and Union are used for type hinting instead of | because
  15. # the latter is not supported in pydantic<2.6 and Python<3.10.
  16. # Dict, List, and Tuple are used for backwards compatibility
  17. # with pydantic v1 and Python<3.9.
  18. from typing import Any, Dict, List, Literal, Optional, Tuple, Union
  19. from urllib.parse import quote, unquote
  20. from google.protobuf.wrappers_pb2 import BoolValue, DoubleValue, Int32Value, StringValue
  21. from pydantic import BaseModel, ConfigDict, Field
  22. from typing_extensions import Self
  23. import wandb
  24. from wandb import env, util
  25. from wandb._pydantic import (
  26. IS_PYDANTIC_V2,
  27. AliasChoices,
  28. ValidationError,
  29. computed_field,
  30. field_validator,
  31. model_validator,
  32. )
  33. from wandb.errors import UsageError
  34. from wandb.proto import wandb_settings_pb2
  35. from wandb.sdk.lib import deprecation, settings_file, urls
  36. from .lib import credentials, filesystem, ipython
  37. from .lib.run_moment import RunMoment
  38. if not IS_PYDANTIC_V2:
  39. from pydantic import root_validator
  40. def _path_convert(*args: str) -> str:
  41. """Join path and apply os.path.expanduser to it."""
  42. return os.path.expanduser(os.path.join(*args))
  43. CLIENT_ONLY_SETTINGS = (
  44. "anonymous",
  45. "app_url_override",
  46. "files_dir",
  47. "max_end_of_run_history_metrics",
  48. "max_end_of_run_summary_metrics",
  49. "reinit",
  50. "x_files_dir",
  51. "x_sync_dir_suffix",
  52. )
  53. """Python-only keys that are not fields on the settings proto."""
  54. class Settings(BaseModel, validate_assignment=True):
  55. """Settings for the W&B SDK.
  56. This class manages configuration settings for the W&B SDK,
  57. ensuring type safety and validation of all settings. Settings are accessible
  58. as attributes and can be initialized programmatically, through environment
  59. variables (`WANDB_ prefix`), and with configuration files.
  60. The settings are organized into three categories:
  61. 1. Public settings: Core configuration options that users can safely modify to customize
  62. W&B's behavior for their specific needs.
  63. 2. Internal settings: Settings prefixed with 'x_' that handle low-level SDK behavior.
  64. These settings are primarily for internal use and debugging. While they can be modified,
  65. they are not considered part of the public API and may change without notice in future
  66. versions.
  67. 3. Computed settings: Read-only settings that are automatically derived from other settings or
  68. the environment.
  69. """
  70. # Pydantic Model configuration.
  71. model_config = ConfigDict(
  72. extra="forbid", # throw an error if extra fields are provided
  73. validate_default=True, # validate default values
  74. use_attribute_docstrings=True, # for field descriptions
  75. revalidate_instances="always",
  76. )
  77. # Public settings.
  78. allow_media_symlink: bool = False
  79. """Whether to symlink media files to the run directory.
  80. If true, media files will be symlinked or hardlinked to the
  81. run directory instead of copied. This may result in faster
  82. logging and reduced disk usage. However, deleting or modifying
  83. the original files before upload to the W&B server will be
  84. reflected in the uploaded data.
  85. """
  86. allow_offline_artifacts: bool = True
  87. """Flag to allow table artifacts to be synced in offline mode.
  88. To revert to the old behavior, set this to False.
  89. """
  90. allow_val_change: bool = False
  91. """Flag to allow modification of `Config` values after they've been set."""
  92. anonymous: deprecation.DoNotSet = Field(
  93. default=deprecation.UNSET,
  94. exclude=True,
  95. )
  96. """Deprecated and will be removed."""
  97. api_key: Optional[str] = None
  98. """The W&B API key."""
  99. azure_account_url_to_access_key: Optional[Dict[str, str]] = None
  100. """Mapping of Azure account URLs to their corresponding access keys for Azure integration."""
  101. app_url_override: Optional[str] = None
  102. """Override for the 'app' URL for the W&B UI.
  103. The `app_url` is normally computed based on `base_url`, but this can be
  104. used to set it explicitly.
  105. WANDB_APP_URL is the corresponding environment variable.
  106. """
  107. base_url: str = "https://api.wandb.ai"
  108. """The URL of the W&B backend for data synchronization."""
  109. code_dir: Optional[str] = None
  110. """Directory containing the code to be tracked by W&B."""
  111. config_paths: Optional[Sequence[str]] = None
  112. """Paths to files to load configuration from into the `Config` object."""
  113. console: Literal["auto", "off", "wrap", "redirect", "wrap_raw", "wrap_emu"] = Field(
  114. default="auto",
  115. validate_default=True,
  116. )
  117. """The type of console capture to be applied.
  118. Possible values are:
  119. - "auto" - Automatically selects the console capture method based on the
  120. system environment and settings.
  121. - "off" - Disables console capture.
  122. - "redirect" - Redirects low-level file descriptors for capturing output.
  123. - "wrap" - Overrides the write methods of sys.stdout/sys.stderr. Will be
  124. mapped to either "wrap_raw" or "wrap_emu" based on the state of the system.
  125. - "wrap_raw" - Same as "wrap" but captures raw output directly instead of
  126. through an emulator. Derived from the `wrap` setting and should not be set manually.
  127. - "wrap_emu" - Same as "wrap" but captures output through an emulator.
  128. Derived from the `wrap` setting and should not be set manually.
  129. """
  130. console_multipart: bool = False
  131. """Enable multipart console logging.
  132. When True, the SDK writes console output to timestamped files
  133. under the `logs/` directory instead of a single `output.log`.
  134. Each part is uploaded as soon as it is closed, giving users live
  135. access to logs while the run is active. Rollover cadence is
  136. controlled by `console_chunk_max_bytes` and/or `console_chunk_max_seconds`.
  137. If both limits are `0`, all logs are uploaded once at run finish.
  138. Note: Uploaded chunks are immutable; terminal control sequences
  139. that modify previous lines (e.g., progress bars using carriage returns)
  140. only affect the current chunk.
  141. """
  142. console_chunk_max_bytes: int = 0
  143. """Size-based rollover threshold for multipart console logs, in bytes.
  144. Starts a new console log file when the current part reaches this
  145. size. Has an effect only when `console_multipart` is `True`.
  146. Can be combined with `console_chunk_max_seconds`; whichever limit is
  147. hit first triggers the rollover. A value of `0` disables the
  148. size-based limit.
  149. """
  150. console_chunk_max_seconds: int = 0
  151. """Time-based rollover threshold for multipart console logs, in seconds.
  152. Starts a new console log file after this many seconds have elapsed
  153. since the current part began. Requires `console_multipart` to be
  154. `True`. May be used with `console_chunk_max_bytes`; the first limit
  155. reached closes the part. A value of `0` disables the time-based
  156. limit.
  157. """
  158. credentials_file: str = Field(
  159. default_factory=lambda: str(credentials.DEFAULT_WANDB_CREDENTIALS_FILE)
  160. )
  161. """Path to file for writing temporary access tokens."""
  162. disable_code: bool = False
  163. """Whether to disable capturing the code."""
  164. disable_git: bool = False
  165. """Whether to disable capturing the git state."""
  166. disable_git_fork_point: bool = True
  167. """Whether to disable inferring fork point from remote branches
  168. When set to True, the SDK will use the latest commit from the upstream
  169. branch, if one is set. Otherwise skip generating the diff patch.
  170. When set to False, the SDK will try to use the latest commit from the upstream branch,
  171. if one is set.
  172. Otherwise, it will find the closest commit from all remote branches.
  173. This may impact performance for repos with many upstream branches.
  174. """
  175. disable_job_creation: bool = True
  176. """Whether to disable the creation of a job artifact for W&B Launch."""
  177. docker: Optional[str] = None
  178. """The Docker image used to execute the script."""
  179. email: Optional[str] = None
  180. """The email address of the user."""
  181. entity: Optional[str] = None
  182. """The W&B entity, such as a user or a team."""
  183. organization: Optional[str] = None
  184. """The W&B organization."""
  185. force: bool = False
  186. """Whether to pass the `force` flag to `wandb.login()`."""
  187. fork_from: Optional[RunMoment] = None
  188. """Specifies a point in a previous execution of a run to fork from.
  189. The point is defined by the run ID, a metric, and its value.
  190. Currently, only the metric '_step' is supported.
  191. """
  192. git_commit: Optional[str] = None
  193. """The git commit hash to associate with the run."""
  194. git_remote: str = "origin"
  195. """The git remote to associate with the run."""
  196. git_remote_url: Optional[str] = None
  197. """The URL of the git remote repository."""
  198. git_root: Optional[str] = None
  199. """Root directory of the git repository."""
  200. heartbeat_seconds: int = 30
  201. """Interval in seconds between heartbeat signals sent to the W&B servers.
  202. <!-- lazydoc-ignore-class-attributes -->
  203. """
  204. host: Optional[str] = None
  205. """Hostname of the machine running the script."""
  206. http_proxy: Optional[str] = None
  207. """Custom proxy servers for http requests to W&B."""
  208. https_proxy: Optional[str] = None
  209. """Custom proxy servers for https requests to W&B."""
  210. identity_token_file: Optional[str] = None
  211. """Path to file containing an identity token (JWT) for authentication."""
  212. ignore_globs: Sequence[str] = ()
  213. """Unix glob patterns relative to `files_dir` specifying files to exclude from upload."""
  214. init_timeout: float = 90.0
  215. """Time in seconds to wait for the `wandb.init` call to complete before timing out."""
  216. insecure_disable_ssl: bool = False
  217. """Whether to insecurely disable SSL verification."""
  218. job_name: Optional[str] = None
  219. """Name of the Launch job running the script."""
  220. job_source: Optional[Literal["repo", "artifact", "image"]] = None
  221. """Source type for Launch."""
  222. label_disable: bool = False
  223. """Whether to disable automatic labeling features."""
  224. launch: bool = False
  225. """Flag to indicate if the run is being launched through W&B Launch.
  226. <!-- lazydoc-ignore-class-attributes -->
  227. """
  228. launch_config_path: Optional[str] = None
  229. """Path to the launch configuration file."""
  230. login_timeout: Optional[float] = None
  231. """Time in seconds to wait for login operations before timing out."""
  232. mode: Literal["online", "offline", "shared", "disabled", "dryrun", "run"] = Field(
  233. default="online",
  234. validate_default=True,
  235. )
  236. """The operating mode for W&B logging and synchronization."""
  237. notebook_name: Optional[str] = None
  238. """Name of the notebook if running in a Jupyter-like environment."""
  239. program: Optional[str] = None
  240. """Path to the script that created the run, if available."""
  241. program_abspath: Optional[str] = None
  242. """The absolute path from the root repository directory to the script that
  243. created the run.
  244. Root repository directory is defined as the directory containing the
  245. .git directory, if it exists. Otherwise, it's the current working directory.
  246. """
  247. program_relpath: Optional[str] = None
  248. """The relative path to the script that created the run."""
  249. project: Optional[str] = None
  250. """The W&B project ID."""
  251. quiet: bool = False
  252. """Flag to suppress non-essential output."""
  253. reinit: Union[
  254. Literal[
  255. "default",
  256. "return_previous",
  257. "finish_previous",
  258. "create_new",
  259. ],
  260. bool,
  261. ] = "default"
  262. """What to do when `wandb.init()` is called while a run is active.
  263. Options:
  264. - "default": Use "finish_previous" in notebooks and "return_previous"
  265. otherwise.
  266. - "return_previous": Return the most recently created run
  267. that is not yet finished. This does not update `wandb.run`; see
  268. the "create_new" option.
  269. - "finish_previous": Finish all active runs, then return a new run.
  270. - "create_new": Create a new run without modifying other active runs.
  271. Does not update `wandb.run` and top-level functions like `wandb.log`.
  272. Because of this, some older integrations that rely on the global run
  273. will not work.
  274. Can also be a boolean, but this is deprecated. False is the same as
  275. "return_previous", and True is the same as "finish_previous".
  276. """
  277. relogin: bool = False
  278. """Flag to force a new login attempt."""
  279. resume: Optional[Literal["allow", "must", "never", "auto"]] = None
  280. """Specifies the resume behavior for the run.
  281. Options:
  282. - "must": Resumes from an existing run with the same ID. If no such run exists,
  283. it will result in failure.
  284. - "allow": Attempts to resume from an existing run with the same ID. If none is
  285. found, a new run will be created.
  286. - "never": Always starts a new run. If a run with the same ID already exists,
  287. it will result in failure.
  288. - "auto": Automatically resumes from the most recent failed run on the same
  289. machine.
  290. """
  291. resume_from: Optional[RunMoment] = None
  292. """Specifies a point in a previous execution of a run to resume from.
  293. The point is defined by the run ID, a metric, and its value.
  294. Currently, only the metric '_step' is supported.
  295. """
  296. resumed: bool = False
  297. """Indication from the server about the state of the run.
  298. This is different from resume, a user provided flag.
  299. <!-- lazydoc-ignore-class-attributes -->
  300. """
  301. root_dir: str = Field(default_factory=lambda: os.path.abspath(os.getcwd()))
  302. """The root directory to use as the base for all run-related paths.
  303. In particular, this is used to derive the wandb directory and the run directory.
  304. """
  305. run_group: Optional[str] = None
  306. """Group identifier for related runs.
  307. Used for grouping runs in the UI.
  308. """
  309. run_id: Optional[str] = None
  310. """The ID of the run."""
  311. run_job_type: Optional[str] = None
  312. """Type of job being run (e.g., training, evaluation)."""
  313. run_name: Optional[str] = None
  314. """Human-readable name for the run."""
  315. run_notes: Optional[str] = None
  316. """Additional notes or description for the run."""
  317. run_tags: Optional[Tuple[str, ...]] = None
  318. """Tags to associate with the run for organization and filtering."""
  319. sagemaker_disable: bool = False
  320. """Flag to disable SageMaker-specific functionality."""
  321. save_code: Optional[bool] = None
  322. """Whether to save the code associated with the run."""
  323. settings_system: Optional[str] = None
  324. """Path to the system-wide settings file."""
  325. max_end_of_run_history_metrics: int = 10
  326. """Maximum number of history sparklines to display at the end of a run."""
  327. max_end_of_run_summary_metrics: int = 10
  328. """Maximum number of summary metrics to display at the end of a run."""
  329. show_colors: Optional[bool] = None
  330. """Whether to use colored output in the console.
  331. <!-- lazydoc-ignore-class-attributes -->
  332. """
  333. show_emoji: Optional[bool] = None
  334. """Whether to show emoji in the console output.
  335. <!-- lazydoc-ignore-class-attributes -->
  336. """
  337. show_errors: bool = True
  338. """Whether to display error messages."""
  339. show_info: bool = True
  340. """Whether to display informational messages."""
  341. show_warnings: bool = True
  342. """Whether to display warning messages."""
  343. silent: bool = False
  344. """Flag to suppress all output."""
  345. start_method: Optional[str] = None
  346. """Method to use for starting subprocesses.
  347. This is deprecated and will be removed in a future release.
  348. <!-- lazydoc-ignore-class-attributes -->
  349. """
  350. strict: Optional[bool] = None
  351. """Whether to enable strict mode for validation and error checking."""
  352. summary_timeout: int = 60
  353. """Time in seconds to wait for summary operations before timing out."""
  354. summary_warnings: int = 5
  355. """Maximum number of summary warnings to display.
  356. <!-- lazydoc-ignore-class-attributes -->
  357. """
  358. sweep_id: Optional[str] = None
  359. """Identifier of the sweep this run belongs to."""
  360. sweep_param_path: Optional[str] = None
  361. """Path to the sweep parameters configuration."""
  362. symlink: bool = Field(default_factory=lambda: platform.system() != "Windows")
  363. """Whether to use symlinks (True by default except on Windows)."""
  364. sync_tensorboard: Optional[bool] = None
  365. """Whether to synchronize TensorBoard logs with W&B."""
  366. table_raise_on_max_row_limit_exceeded: bool = False
  367. """Whether to raise an exception when table row limits are exceeded."""
  368. use_dot_wandb: Optional[bool] = None
  369. """Whether to use a hidden `.wandb` or visible `wandb` directory for run data.
  370. If True, the SDK uses `.wandb`. If False, `wandb`.
  371. If not set, defaults to `.wandb` if it already exists, otherwise `wandb`.
  372. """
  373. username: Optional[str] = None
  374. """Username."""
  375. # Internal settings.
  376. #
  377. # These are typically not meant to be set by the user and should not be considered
  378. # a part of the public API as they may change or be removed in future versions.
  379. x_cli_only_mode: bool = False
  380. """Flag to indicate that the SDK is running in CLI-only mode.
  381. <!-- lazydoc-ignore-class-attributes -->
  382. """
  383. x_disable_meta: bool = False
  384. """Flag to disable the collection of system metadata."""
  385. x_disable_stats: bool = False
  386. """Flag to disable the collection of system metrics."""
  387. x_disable_viewer: bool = False
  388. """Flag to disable the early viewer query.
  389. <!-- lazydoc-ignore-class-attributes -->
  390. """
  391. x_disable_machine_info: bool = False
  392. """Flag to disable automatic machine info collection.
  393. <!-- lazydoc-ignore-class-attributes -->
  394. """
  395. x_executable: Optional[str] = None
  396. """Path to the Python executable.
  397. <!-- lazydoc-ignore-class-attributes -->
  398. """
  399. x_extra_http_headers: Optional[Dict[str, str]] = None
  400. """Additional headers to add to all outgoing HTTP requests."""
  401. x_file_stream_max_bytes: Optional[int] = None
  402. """An approximate maximum request size for the filestream API.
  403. Its purpose is to prevent HTTP requests from failing due to
  404. containing too much data. This number is approximate:
  405. requests will be slightly larger.
  406. <!-- lazydoc-ignore-class-attributes -->
  407. """
  408. x_file_stream_max_line_bytes: Optional[int] = None
  409. """Maximum line length for filestream JSONL files.
  410. <!-- lazydoc-ignore-class-attributes -->
  411. """
  412. x_file_stream_transmit_interval: Optional[float] = None
  413. """Interval in seconds between filestream transmissions.
  414. <!-- lazydoc-ignore-class-attributes -->
  415. """
  416. # Filestream retry client configuration.
  417. x_file_stream_retry_max: Optional[int] = None
  418. """Max number of retries for filestream operations.
  419. <!-- lazydoc-ignore-class-attributes -->
  420. """
  421. x_file_stream_retry_wait_min_seconds: Optional[float] = None
  422. """Minimum wait time between retries for filestream operations.
  423. <!-- lazydoc-ignore-class-attributes -->
  424. """
  425. x_file_stream_retry_wait_max_seconds: Optional[float] = None
  426. """Maximum wait time between retries for filestream operations.
  427. <!-- lazydoc-ignore-class-attributes -->
  428. """
  429. x_file_stream_timeout_seconds: Optional[float] = None
  430. """Timeout in seconds for individual filestream HTTP requests.
  431. <!-- lazydoc-ignore-class-attributes -->
  432. """
  433. # file transfer retry client configuration
  434. x_file_transfer_retry_max: Optional[int] = None
  435. """Max number of retries for file transfer operations.
  436. <!-- lazydoc-ignore-class-attributes -->
  437. """
  438. x_file_transfer_retry_wait_min_seconds: Optional[float] = None
  439. """Minimum wait time between retries for file transfer operations.
  440. <!-- lazydoc-ignore-class-attributes -->
  441. """
  442. x_file_transfer_retry_wait_max_seconds: Optional[float] = None
  443. """Maximum wait time between retries for file transfer operations.
  444. <!-- lazydoc-ignore-class-attributes -->
  445. """
  446. x_file_transfer_timeout_seconds: Optional[float] = None
  447. """Timeout in seconds for individual file transfer HTTP requests.
  448. <!-- lazydoc-ignore-class-attributes -->
  449. """
  450. x_files_dir: Optional[str] = None
  451. """Override setting for the computed files_dir.
  452. DEPRECATED, DO NOT USE. This private setting is not respected by wandb-core
  453. but will continue to work for some legacy Python code.
  454. <!-- lazydoc-ignore-class-attributes -->
  455. """
  456. x_flow_control_custom: Optional[bool] = None
  457. """Flag indicating custom flow control for filestream.
  458. TODO: Not implemented in wandb-core.
  459. <!-- lazydoc-ignore-class-attributes -->
  460. """
  461. x_flow_control_disabled: Optional[bool] = None
  462. """Flag indicating flow control is disabled for filestream.
  463. TODO: Not implemented in wandb-core.
  464. <!-- lazydoc-ignore-class-attributes -->
  465. """
  466. # graphql retry client configuration
  467. x_graphql_retry_max: Optional[int] = None
  468. """Max number of retries for GraphQL operations.
  469. <!-- lazydoc-ignore-class-attributes -->
  470. """
  471. x_graphql_retry_wait_min_seconds: Optional[float] = None
  472. """Minimum wait time between retries for GraphQL operations.
  473. <!-- lazydoc-ignore-class-attributes -->
  474. """
  475. x_graphql_retry_wait_max_seconds: Optional[float] = None
  476. """Maximum wait time between retries for GraphQL operations.
  477. <!-- lazydoc-ignore-class-attributes -->
  478. """
  479. x_graphql_timeout_seconds: Optional[float] = None
  480. """Timeout in seconds for individual GraphQL requests.
  481. <!-- lazydoc-ignore-class-attributes -->
  482. """
  483. x_internal_check_process: float = 8.0
  484. """Interval for internal process health checks in seconds.
  485. <!-- lazydoc-ignore-class-attributes -->
  486. """
  487. x_jupyter_name: Optional[str] = None
  488. """Name of the Jupyter notebook.
  489. <!-- lazydoc-ignore-class-attributes -->
  490. """
  491. x_jupyter_path: Optional[str] = None
  492. """Path to the Jupyter notebook.
  493. <!-- lazydoc-ignore-class-attributes -->
  494. """
  495. x_jupyter_root: Optional[str] = None
  496. """Root directory of the Jupyter notebook.
  497. <!-- lazydoc-ignore-class-attributes -->
  498. """
  499. x_label: Optional[str] = None
  500. """Label to assign to system metrics and console logs collected for the run.
  501. This is used to group data by on the frontend and can be used to distinguish data
  502. from different processes in a distributed training job.
  503. """
  504. x_live_policy_rate_limit: Optional[int] = None
  505. """Rate limit for live policy updates in seconds.
  506. <!-- lazydoc-ignore-class-attributes -->
  507. """
  508. x_live_policy_wait_time: Optional[int] = None
  509. """Wait time between live policy updates in seconds.
  510. <!-- lazydoc-ignore-class-attributes -->
  511. """
  512. x_log_level: int = logging.INFO
  513. """Logging level for internal operations.
  514. <!-- lazydoc-ignore-class-attributes -->
  515. """
  516. x_network_buffer: Optional[int] = None
  517. """Size of the network buffer used in flow control.
  518. TODO: Not implemented in wandb-core.
  519. <!-- lazydoc-ignore-class-attributes -->
  520. """
  521. x_primary: bool = Field(
  522. default=True, validation_alias=AliasChoices("x_primary", "x_primary_node")
  523. )
  524. """Determines whether to save internal wandb files and metadata.
  525. In a distributed setting, this is useful for avoiding file overwrites
  526. from secondary processes when only system metrics and logs are needed,
  527. as the primary process handles the main logging.
  528. """
  529. x_proxies: Optional[Dict[str, str]] = None
  530. """Custom proxy servers for requests to W&B.
  531. This is deprecated and will be removed in a future release.
  532. Please use `http_proxy` and `https_proxy` instead.
  533. <!-- lazydoc-ignore-class-attributes -->
  534. """
  535. x_runqueue_item_id: Optional[str] = None
  536. """ID of the Launch run queue item being processed.
  537. <!-- lazydoc-ignore-class-attributes -->
  538. """
  539. x_save_requirements: bool = True
  540. """Flag to save the requirements file."""
  541. x_server_side_derived_summary: bool = False
  542. """Flag to delegate automatic computation of summary from history to the server.
  543. This does not disable user-provided summary updates.
  544. """
  545. x_server_side_expand_glob_metrics: bool = True
  546. """Flag to delegate glob matching of metrics in define_metric to the server.
  547. If the server does not support this, the client will perform the glob matching.
  548. <!-- lazydoc-ignore-class-attributes -->
  549. """
  550. x_service_transport: Optional[str] = None
  551. """Transport method for communication with the wandb service.
  552. <!-- lazydoc-ignore-class-attributes -->
  553. """
  554. x_service_wait: float = 30.0
  555. """Time in seconds to wait for the wandb-core internal service to start."""
  556. x_skip_transaction_log: bool = False
  557. """Whether to skip saving the run events to the transaction log.
  558. This is only relevant for online runs. Can be used to reduce the amount of
  559. data written to disk.
  560. Should be used with caution, as it removes the gurantees about
  561. recoverability.
  562. """
  563. x_start_time: Optional[float] = None
  564. """The start time of the run in seconds since the Unix epoch.
  565. <!-- lazydoc-ignore-class-attributes -->
  566. """
  567. x_stats_pid: int = os.getpid()
  568. """PID of the process that started the wandb-core process to collect system stats for.
  569. <!-- lazydoc-ignore-class-attributes -->
  570. """
  571. x_stats_sampling_interval: float = Field(default=15.0)
  572. """Sampling interval for the system monitor in seconds."""
  573. x_stats_neuron_monitor_config_path: Optional[str] = None
  574. """Path to the default config file for the neuron-monitor tool.
  575. This is used to monitor AWS Trainium devices.
  576. <!-- lazydoc-ignore-class-attributes -->
  577. """
  578. x_stats_dcgm_exporter: Optional[str] = None
  579. """Endpoint to extract Nvidia DCGM metrics from.
  580. Options:
  581. - Extract DCGM-related metrics from a query to the Prometheus `/api/v1/query` endpoint.
  582. It is a common practice to aggregate metrics reported by the instances of the DCGM Exporter
  583. running on different nodes in a cluster using Prometheus.
  584. - TODO: Parse metrics directly from the `/metrics` endpoint of the DCGM Exporter.
  585. Examples:
  586. - `http://localhost:9400/api/v1/query?query=DCGM_FI_DEV_GPU_TEMP{node="l1337", cluster="globular"}`.
  587. - TODO: `http://192.168.0.1:9400/metrics`.
  588. <!-- lazydoc-ignore-class-attributes -->
  589. """
  590. x_stats_open_metrics_endpoints: Optional[Dict[str, str]] = None
  591. """OpenMetrics `/metrics` endpoints to monitor for system metrics."""
  592. x_stats_open_metrics_filters: Union[
  593. Dict[str, Dict[str, str]], Sequence[str], None
  594. ] = None
  595. """Filter to apply to metrics collected from OpenMetrics `/metrics` endpoints.
  596. Supports two formats:
  597. - `{"metric regex pattern, including endpoint name as prefix": {"label": "label value regex pattern"}}`
  598. - `("metric regex pattern 1", "metric regex pattern 2", ...)`
  599. """
  600. x_stats_open_metrics_http_headers: Optional[Dict[str, str]] = None
  601. """HTTP headers to add to OpenMetrics requests."""
  602. x_stats_disk_paths: Optional[Sequence[str]] = ("/",)
  603. """System paths to monitor for disk usage."""
  604. x_stats_cpu_count: Optional[int] = None
  605. """System CPU count.
  606. If set, overrides the auto-detected value in the run metadata.
  607. """
  608. x_stats_cpu_logical_count: Optional[int] = None
  609. """Logical CPU count.
  610. If set, overrides the auto-detected value in the run metadata.
  611. """
  612. x_stats_gpu_count: Optional[int] = None
  613. """GPU device count.
  614. If set, overrides the auto-detected value in the run metadata.
  615. """
  616. x_stats_gpu_type: Optional[str] = None
  617. """GPU device type.
  618. If set, overrides the auto-detected value in the run metadata.
  619. """
  620. x_stats_gpu_device_ids: Optional[Sequence[int]] = None
  621. """GPU device indices to monitor.
  622. If not set, the system monitor captures metrics for all GPUs.
  623. Assumes 0-based indexing matching CUDA/ROCm device enumeration.
  624. """
  625. x_stats_buffer_size: int = 0
  626. """Number of system metric samples to buffer in memory in the wandb-core process.
  627. Can be accessed via run._system_metrics.
  628. <!-- lazydoc-ignore-class-attributes -->
  629. """
  630. x_stats_coreweave_metadata_base_url: str = "http://169.254.169.254"
  631. """The scheme and hostname for contacting the CoreWeave metadata server.
  632. Only accessible from within a CoreWeave cluster.
  633. <!-- lazydoc-ignore-class-attributes -->
  634. """
  635. x_stats_coreweave_metadata_endpoint: str = "/api/v2/cloud-init/meta-data"
  636. """The relative path on the CoreWeave metadata server to which to make requests.
  637. This must not include the schema and hostname prefix.
  638. Only accessible from within a CoreWeave cluster.
  639. <!-- lazydoc-ignore-class-attributes -->
  640. """
  641. x_stats_track_process_tree: bool = False
  642. """Monitor the entire process tree for resource usage, starting from `x_stats_pid`.
  643. When `True`, the system monitor aggregates the RSS, CPU%, and thread count
  644. from the process with PID `x_stats_pid` and all of its descendants.
  645. This can have a performance overhead and is disabled by default.
  646. """
  647. x_sync: bool = False
  648. """Flag to indicate whether we are syncing a run from the transaction log.
  649. <!-- lazydoc-ignore-class-attributes -->
  650. """
  651. x_sync_dir_suffix: str = ""
  652. """Suffix to add to the run's directory name (sync_dir).
  653. This is set in wandb.init() to avoid naming conflicts.
  654. If set, it is joined to the default name with a dash.
  655. """
  656. x_update_finish_state: bool = True
  657. """Flag to indicate whether this process can update the run's final state on the server.
  658. Set to False in distributed training when only the main process should determine the final state.
  659. """
  660. # Model validator to catch legacy settings.
  661. @model_validator(mode="before")
  662. @classmethod
  663. def catch_private_settings(cls, values):
  664. """Check if a private field is provided and assign to the corresponding public one.
  665. This is a compatibility layer to handle previous versions of the settings.
  666. <!-- lazydoc-ignore-classmethod: internal -->
  667. """
  668. new_values = {}
  669. for key in values:
  670. # Internal settings are prefixed with "x_" instead of "_"
  671. # as Pydantic does not allow "_" in field names.
  672. if key.startswith("_"):
  673. new_values["x" + key] = values[key]
  674. else:
  675. new_values[key] = values[key]
  676. return new_values
  677. if IS_PYDANTIC_V2:
  678. @model_validator(mode="after")
  679. def validate_mutual_exclusion_of_branching_args(self) -> Self:
  680. """Check if `fork_from`, `resume`, and `resume_from` are mutually exclusive.
  681. <!-- lazydoc-ignore: internal -->
  682. """
  683. if (
  684. sum(
  685. o is not None
  686. for o in [self.fork_from, self.resume, self.resume_from]
  687. )
  688. > 1
  689. ):
  690. raise ValueError(
  691. "`fork_from`, `resume`, or `resume_from` are mutually exclusive. "
  692. "Please specify only one of them."
  693. )
  694. return self
  695. @model_validator(mode="after")
  696. def validate_skip_transaction_log(self):
  697. """Validate x_skip_transaction_log.
  698. <!-- lazydoc-ignore: internal -->
  699. """
  700. if self._offline and self.x_skip_transaction_log:
  701. raise ValueError("Cannot skip transaction log in offline mode")
  702. return self
  703. else:
  704. @root_validator(pre=False) # type: ignore [call-overload]
  705. @classmethod
  706. def validate_mutual_exclusion_of_branching_args(cls, values):
  707. if (
  708. sum(
  709. values.get(o) is not None
  710. for o in ["fork_from", "resume", "resume_from"]
  711. )
  712. > 1
  713. ):
  714. raise ValueError(
  715. "`fork_from`, `resume`, or `resume_from` are mutually exclusive. "
  716. "Please specify only one of them."
  717. )
  718. return values
  719. @root_validator(pre=False) # type: ignore [call-overload]
  720. @classmethod
  721. def validate_skip_transaction_log(cls, values):
  722. if values.get("_offline") and values.get("x_skip_transaction_log"):
  723. raise ValueError("Cannot skip transaction log in offline mode")
  724. return values
  725. # Field validators.
  726. @field_validator("anonymous", mode="after")
  727. @classmethod
  728. def validate_anonymous(cls, value: object) -> object:
  729. if value is not deprecation.UNSET:
  730. wandb.termwarn(
  731. "The anonymous setting has no effect and will be removed"
  732. + " in a future version.",
  733. repeat=False,
  734. )
  735. return value
  736. @field_validator("api_key", mode="after")
  737. @classmethod
  738. def validate_api_key(cls, value):
  739. """Validate the API key.
  740. <!-- lazydoc-ignore-classmethod: internal -->
  741. """
  742. if value is not None and (len(value) > len(value.strip())):
  743. raise UsageError("API key cannot start or end with whitespace")
  744. return value
  745. @field_validator("base_url", mode="after")
  746. @classmethod
  747. def validate_base_url(cls, value):
  748. """Validate the base URL.
  749. <!-- lazydoc-ignore-classmethod: internal -->
  750. """
  751. urls.validate_url(value)
  752. # wandb.ai-specific checks
  753. if re.match(r".*wandb\.ai[^\.]*$", value) and "api." not in value:
  754. # user might guess app.wandb.ai or wandb.ai is the default cloud server
  755. raise ValueError(
  756. f"{value} is not a valid server address, did you mean https://api.wandb.ai?"
  757. )
  758. elif re.match(r".*wandb\.ai[^\.]*$", value) and not value.startswith("https"):
  759. raise ValueError("http is not secure, please use https://api.wandb.ai")
  760. return value.rstrip("/")
  761. @field_validator("code_dir", mode="before")
  762. @classmethod
  763. def validate_code_dir(cls, value):
  764. """Validate the code directory.
  765. <!-- lazydoc-ignore-classmethod: internal -->
  766. """
  767. # TODO: add native support for pathlib.Path
  768. if isinstance(value, pathlib.Path):
  769. return str(value)
  770. return value
  771. @field_validator("console", mode="after")
  772. @classmethod
  773. def validate_console(cls, value, values):
  774. """Validate the console capture method.
  775. <!-- lazydoc-ignore-classmethod: internal -->
  776. """
  777. if value != "auto":
  778. return value
  779. return "wrap"
  780. @field_validator("console_chunk_max_bytes", mode="after")
  781. @classmethod
  782. def validate_console_chunk_max_bytes(cls, value):
  783. """Validate the console_chunk_max_bytes value.
  784. <!-- lazydoc-ignore-classmethod: internal -->
  785. """
  786. if value < 0:
  787. raise ValueError("console_chunk_max_bytes must be non-negative")
  788. return value
  789. @field_validator("console_chunk_max_seconds", mode="after")
  790. @classmethod
  791. def validate_console_chunk_max_seconds(cls, value):
  792. """Validate the console_chunk_max_seconds value.
  793. <!-- lazydoc-ignore-classmethod: internal -->
  794. """
  795. if value < 0:
  796. raise ValueError("console_chunk_max_seconds must be non-negative")
  797. return value
  798. @field_validator("x_executable", mode="before")
  799. @classmethod
  800. def validate_x_executable(cls, value):
  801. """Validate the Python executable path.
  802. <!-- lazydoc-ignore-classmethod: internal -->
  803. """
  804. # TODO: add native support for pathlib.Path
  805. if isinstance(value, pathlib.Path):
  806. return str(value)
  807. return value
  808. @field_validator("x_extra_http_headers", mode="before")
  809. @classmethod
  810. def validate_x_extra_http_headers(cls, value):
  811. if isinstance(value, str):
  812. return json.loads(value)
  813. return value
  814. @field_validator("x_file_stream_max_line_bytes", mode="after")
  815. @classmethod
  816. def validate_file_stream_max_line_bytes(cls, value):
  817. """Validate the maximum line length for filestream JSONL files.
  818. <!-- lazydoc-ignore-classmethod: internal -->
  819. """
  820. if value is not None and value < 1:
  821. raise ValueError("File stream max line bytes must be greater than 0")
  822. return value
  823. @field_validator("x_files_dir", mode="before")
  824. @classmethod
  825. def validate_x_files_dir(cls, value):
  826. """Validate the files directory.
  827. <!-- lazydoc-ignore-classmethod: internal -->
  828. """
  829. # TODO: add native support for pathlib.Path
  830. if isinstance(value, pathlib.Path):
  831. return str(value)
  832. return value
  833. @field_validator("fork_from", mode="before")
  834. @classmethod
  835. def validate_fork_from(cls, value, values) -> Optional[RunMoment]:
  836. """Validate the fork_from field.
  837. <!-- lazydoc-ignore-classmethod: internal -->
  838. """
  839. run_moment = cls._runmoment_preprocessor(value)
  840. if hasattr(values, "data"):
  841. # pydantic v2
  842. values = values.data
  843. else:
  844. # pydantic v1
  845. values = values
  846. if (
  847. run_moment
  848. and values.get("run_id") is not None
  849. and values.get("run_id") == run_moment.run
  850. ):
  851. raise ValueError(
  852. "Provided `run_id` is the same as the run to `fork_from`. "
  853. "Please provide a different `run_id` or remove the `run_id` argument. "
  854. "If you want to rewind the current run, please use `resume_from` instead."
  855. )
  856. return run_moment
  857. @field_validator("http_proxy", mode="after")
  858. @classmethod
  859. def validate_http_proxy(cls, value):
  860. """Validate the HTTP proxy.
  861. <!-- lazydoc-ignore-classmethod: internal -->
  862. """
  863. if value is None:
  864. return None
  865. urls.validate_url(value)
  866. return value.rstrip("/")
  867. @field_validator("https_proxy", mode="after")
  868. @classmethod
  869. def validate_https_proxy(cls, value):
  870. """Validate the HTTPS proxy.
  871. <!-- lazydoc-ignore-classmethod: internal -->
  872. """
  873. if value is None:
  874. return None
  875. urls.validate_url(value)
  876. return value.rstrip("/")
  877. @field_validator("ignore_globs", mode="after")
  878. @classmethod
  879. def validate_ignore_globs(cls, value):
  880. """Validate the ignore globs.
  881. <!-- lazydoc-ignore-classmethod: internal -->
  882. """
  883. return tuple(value) if not isinstance(value, tuple) else value
  884. @field_validator("program", mode="before")
  885. @classmethod
  886. def validate_program(cls, value):
  887. """Validate the program path.
  888. <!-- lazydoc-ignore-classmethod: internal -->
  889. """
  890. # TODO: add native support for pathlib.Path
  891. if isinstance(value, pathlib.Path):
  892. return str(value)
  893. return value
  894. @field_validator("program_abspath", mode="before")
  895. @classmethod
  896. def validate_program_abspath(cls, value):
  897. """Validate the absolute program path.
  898. <!-- lazydoc-ignore-classmethod: internal -->
  899. """
  900. # TODO: add native support for pathlib.Path
  901. if isinstance(value, pathlib.Path):
  902. return str(value)
  903. return value
  904. @field_validator("program_relpath", mode="before")
  905. @classmethod
  906. def validate_program_relpath(cls, value):
  907. """Validate the relative program path.
  908. <!-- lazydoc-ignore-classmethod: internal -->
  909. """
  910. # TODO: add native support for pathlib.Path
  911. if isinstance(value, pathlib.Path):
  912. return str(value)
  913. return value
  914. @field_validator("project", mode="after")
  915. @classmethod
  916. def validate_project(cls, value, values):
  917. """Validate the project name.
  918. <!-- lazydoc-ignore-classmethod: internal -->
  919. """
  920. if value is None:
  921. return None
  922. invalid_chars_list = list("/\\#?%:")
  923. if len(value) > 128:
  924. raise UsageError(f"Invalid project name {value!r}: exceeded 128 characters")
  925. invalid_chars = {char for char in invalid_chars_list if char in value}
  926. if invalid_chars:
  927. raise UsageError(
  928. f"Invalid project name {value!r}: "
  929. f"cannot contain characters {','.join(invalid_chars_list)!r}, "
  930. f"found {','.join(invalid_chars)!r}"
  931. )
  932. return value
  933. @field_validator("resume", mode="before")
  934. @classmethod
  935. def validate_resume(cls, value):
  936. """Validate the resume behavior.
  937. <!-- lazydoc-ignore-classmethod: internal -->
  938. """
  939. if value is False:
  940. return None
  941. if value is True:
  942. return "auto"
  943. return value
  944. @field_validator("resume_from", mode="before")
  945. @classmethod
  946. def validate_resume_from(cls, value, values) -> Optional[RunMoment]:
  947. """Validate the resume_from field.
  948. <!-- lazydoc-ignore-classmethod: internal -->
  949. """
  950. run_moment = cls._runmoment_preprocessor(value)
  951. if hasattr(values, "data"):
  952. # pydantic v2
  953. values = values.data
  954. else:
  955. # pydantic v1
  956. values = values
  957. if (
  958. run_moment
  959. and values.get("run_id") is not None
  960. and values.get("run_id") != run_moment.run
  961. ):
  962. raise ValueError(
  963. "Both `run_id` and `resume_from` have been specified with different ids."
  964. )
  965. return run_moment
  966. @field_validator("root_dir", mode="before")
  967. @classmethod
  968. def validate_root_dir(cls, value):
  969. """Validate the root directory.
  970. <!-- lazydoc-ignore-classmethod: internal -->
  971. """
  972. # TODO: add native support for pathlib.Path
  973. if isinstance(value, pathlib.Path):
  974. return str(value)
  975. return value
  976. @field_validator("run_id", mode="after")
  977. @classmethod
  978. def validate_run_id(cls, value, values):
  979. """Validate the run ID.
  980. <!-- lazydoc-ignore-classmethod: internal -->
  981. """
  982. if value is None:
  983. return None
  984. if len(value) == 0:
  985. raise UsageError("Run ID cannot be empty")
  986. if len(value) > len(value.strip()):
  987. raise UsageError("Run ID cannot start or end with whitespace")
  988. if not bool(value.strip()):
  989. raise UsageError("Run ID cannot contain only whitespace")
  990. # check if the run id contains any reserved characters
  991. reserved_chars = ":;,#?/'"
  992. if any(char in reserved_chars for char in value):
  993. raise UsageError(f"Run ID cannot contain the characters: {reserved_chars}")
  994. return value
  995. @field_validator("settings_system", mode="after")
  996. @classmethod
  997. def validate_settings_system(cls, value):
  998. """Validate the system settings file path.
  999. <!-- lazydoc-ignore-classmethod: internal -->
  1000. """
  1001. if value is None:
  1002. return None
  1003. elif isinstance(value, pathlib.Path):
  1004. return str(_path_convert(value))
  1005. else:
  1006. return _path_convert(value)
  1007. @field_validator("x_service_wait", mode="after")
  1008. @classmethod
  1009. def validate_service_wait(cls, value):
  1010. """Validate the service wait time.
  1011. <!-- lazydoc-ignore-classmethod: internal -->
  1012. """
  1013. if value < 0:
  1014. raise UsageError("Service wait time cannot be negative")
  1015. return value
  1016. @field_validator("start_method", mode="after")
  1017. @classmethod
  1018. def validate_start_method(cls, value):
  1019. """Validate the start method for subprocesses.
  1020. <!-- lazydoc-ignore-classmethod: internal -->
  1021. """
  1022. if value is None:
  1023. return value
  1024. wandb.termwarn(
  1025. "`start_method` is deprecated and will be removed in a future version "
  1026. "of wandb. This setting is currently non-functional and safely ignored.",
  1027. repeat=False,
  1028. )
  1029. return value
  1030. @field_validator("x_stats_coreweave_metadata_base_url", mode="after")
  1031. @classmethod
  1032. def validate_x_stats_coreweave_metadata_base_url(cls, value):
  1033. urls.validate_url(value)
  1034. return value.rstrip("/")
  1035. @field_validator("x_stats_gpu_device_ids", mode="before")
  1036. @classmethod
  1037. def validate_x_stats_gpu_device_ids(cls, value):
  1038. """Validate the GPU device IDs.
  1039. <!-- lazydoc-ignore-classmethod: internal -->
  1040. """
  1041. if isinstance(value, str):
  1042. return json.loads(value)
  1043. return value
  1044. @field_validator("x_stats_neuron_monitor_config_path", mode="before")
  1045. @classmethod
  1046. def validate_x_stats_neuron_monitor_config_path(cls, value):
  1047. """Validate the path to the neuron-monitor config file.
  1048. <!-- lazydoc-ignore-classmethod: internal -->
  1049. """
  1050. # TODO: add native support for pathlib.Path
  1051. if isinstance(value, pathlib.Path):
  1052. return str(value)
  1053. return value
  1054. @field_validator("x_stats_open_metrics_endpoints", mode="before")
  1055. @classmethod
  1056. def validate_stats_open_metrics_endpoints(cls, value):
  1057. """Validate the OpenMetrics endpoints.
  1058. <!-- lazydoc-ignore-classmethod: internal -->
  1059. """
  1060. if isinstance(value, str):
  1061. return json.loads(value)
  1062. return value
  1063. @field_validator("x_stats_open_metrics_filters", mode="before")
  1064. @classmethod
  1065. def validate_stats_open_metrics_filters(cls, value):
  1066. """Validate the OpenMetrics filters.
  1067. <!-- lazydoc-ignore-classmethod: internal -->
  1068. """
  1069. if isinstance(value, str):
  1070. return json.loads(value)
  1071. return value
  1072. @field_validator("x_stats_open_metrics_http_headers", mode="before")
  1073. @classmethod
  1074. def validate_stats_open_metrics_http_headers(cls, value):
  1075. """Validate the OpenMetrics HTTP headers.
  1076. <!-- lazydoc-ignore-classmethod: internal -->
  1077. """
  1078. if isinstance(value, str):
  1079. return json.loads(value)
  1080. return value
  1081. @field_validator("x_stats_sampling_interval", mode="after")
  1082. @classmethod
  1083. def validate_stats_sampling_interval(cls, value):
  1084. """Validate the stats sampling interval.
  1085. <!-- lazydoc-ignore-classmethod: internal -->
  1086. """
  1087. if value < 0.1:
  1088. raise UsageError("Stats sampling interval cannot be less than 0.1 seconds")
  1089. return value
  1090. @field_validator("sweep_id", mode="after")
  1091. @classmethod
  1092. def validate_sweep_id(cls, value):
  1093. """Validate the sweep ID.
  1094. <!-- lazydoc-ignore-classmethod: internal -->
  1095. """
  1096. if value is None:
  1097. return None
  1098. if len(value) == 0:
  1099. raise UsageError("Sweep ID cannot be empty")
  1100. if len(value) > len(value.strip()):
  1101. raise UsageError("Sweep ID cannot start or end with whitespace")
  1102. if not bool(value.strip()):
  1103. raise UsageError("Sweep ID cannot contain only whitespace")
  1104. return value
  1105. @field_validator("run_tags", mode="before")
  1106. @classmethod
  1107. def validate_run_tags(cls, value):
  1108. """Validate run tags.
  1109. Validates that each tag:
  1110. - Is between 1 and 64 characters in length (inclusive)
  1111. - Converts single string values to tuple format
  1112. - Preserves None values
  1113. <!-- lazydoc-ignore-classmethod: internal -->
  1114. Args:
  1115. value: A string, list, tuple, or None representing tags
  1116. Returns:
  1117. tuple: A tuple of validated tags, or None
  1118. Raises:
  1119. ValueError: If any tag is empty or exceeds 64 characters
  1120. """
  1121. if value is None:
  1122. return None
  1123. # Convert to tuple if needed
  1124. if isinstance(value, str):
  1125. tags = (value,)
  1126. else:
  1127. tags = tuple(value)
  1128. # Validate each tag and accumulate errors
  1129. errors = []
  1130. for i, tag in enumerate(tags):
  1131. tag_str = str(tag)
  1132. if len(tag_str) == 0:
  1133. errors.append(
  1134. f"Tag at index {i} is empty. Tags must be between 1 and 64 characters"
  1135. )
  1136. elif len(tag_str) > 64:
  1137. # Truncate long tags for display
  1138. display_tag = (
  1139. f"{tag_str[:20]}...{tag_str[-20:]}"
  1140. if len(tag_str) > 43
  1141. else tag_str
  1142. )
  1143. errors.append(
  1144. f"Tag '{display_tag}' is {len(tag_str)} characters. Tags must be between 1 and 64 characters"
  1145. )
  1146. # Raise combined error if any validation issues were found
  1147. if errors:
  1148. raise ValueError("; ".join(errors))
  1149. return tags
  1150. @field_validator("sweep_param_path", mode="before")
  1151. @classmethod
  1152. def validate_sweep_param_path(cls, value):
  1153. """Validate the sweep parameter path.
  1154. <!-- lazydoc-ignore-classmethod: internal -->
  1155. """
  1156. # TODO: add native support for pathlib.Path
  1157. if isinstance(value, pathlib.Path):
  1158. return str(value)
  1159. return value
  1160. # Computed fields.
  1161. @computed_field # type: ignore[prop-decorator]
  1162. @property
  1163. def _args(self) -> List[str]:
  1164. if not self._jupyter:
  1165. return sys.argv[1:]
  1166. return []
  1167. @computed_field # type: ignore[prop-decorator]
  1168. @property
  1169. def _aws_lambda(self) -> bool:
  1170. """Check if we are running in a lambda environment."""
  1171. from sentry_sdk.integrations.aws_lambda import ( # type: ignore[import-not-found]
  1172. get_lambda_bootstrap,
  1173. )
  1174. lambda_bootstrap = get_lambda_bootstrap()
  1175. return not (
  1176. not lambda_bootstrap
  1177. or not hasattr(lambda_bootstrap, "handle_event_request")
  1178. )
  1179. @computed_field # type: ignore[prop-decorator]
  1180. @property
  1181. def _code_path_local(self) -> Optional[str]:
  1182. """The relative path from the current working directory to the code path.
  1183. For example, if the code path is /home/user/project/example.py, and the
  1184. current working directory is /home/user/project, then the code path local
  1185. is example.py.
  1186. If couldn't find the relative path, this will be an empty string.
  1187. """
  1188. return self._get_program_relpath(self.program) if self.program else None
  1189. @computed_field # type: ignore[prop-decorator]
  1190. @property
  1191. def _colab(self) -> bool:
  1192. return "google.colab" in sys.modules
  1193. @computed_field # type: ignore[prop-decorator]
  1194. @property
  1195. def _ipython(self) -> bool:
  1196. return ipython.in_ipython()
  1197. @computed_field # type: ignore[prop-decorator]
  1198. @property
  1199. def _jupyter(self) -> bool:
  1200. return ipython.in_jupyter()
  1201. @computed_field # type: ignore[prop-decorator]
  1202. @property
  1203. def _kaggle(self) -> bool:
  1204. return util._is_likely_kaggle()
  1205. @computed_field # type: ignore[prop-decorator]
  1206. @property
  1207. def _noop(self) -> bool:
  1208. return self.mode == "disabled"
  1209. @computed_field # type: ignore[prop-decorator]
  1210. @property
  1211. def _notebook(self) -> bool:
  1212. return self._ipython or self._jupyter or self._colab or self._kaggle
  1213. @computed_field # type: ignore[prop-decorator]
  1214. @property
  1215. def _offline(self) -> bool:
  1216. return self.mode in ("offline", "dryrun")
  1217. @computed_field # type: ignore[prop-decorator]
  1218. @property
  1219. def _os(self) -> str:
  1220. """The operating system of the machine running the script."""
  1221. return platform.platform(aliased=True)
  1222. @computed_field # type: ignore[prop-decorator]
  1223. @property
  1224. def _platform(self) -> str:
  1225. return f"{platform.system()}-{platform.machine()}".lower()
  1226. @computed_field # type: ignore[prop-decorator]
  1227. @property
  1228. def _python(self) -> str:
  1229. return f"{platform.python_implementation()} {platform.python_version()}"
  1230. @computed_field # type: ignore[prop-decorator]
  1231. @property
  1232. def _shared(self) -> bool:
  1233. """Whether we are in shared mode.
  1234. In "shared" mode, multiple processes can write to the same run,
  1235. for example from different machines.
  1236. """
  1237. return self.mode == "shared"
  1238. @computed_field # type: ignore[prop-decorator]
  1239. @property
  1240. def _start_datetime(self) -> str:
  1241. if self.x_start_time is None:
  1242. return ""
  1243. datetime_now = datetime.fromtimestamp(self.x_start_time)
  1244. return datetime_now.strftime("%Y%m%d_%H%M%S")
  1245. @computed_field # type: ignore[prop-decorator]
  1246. @property
  1247. def _tmp_code_dir(self) -> str:
  1248. return _path_convert(self.sync_dir, "tmp", "code")
  1249. @computed_field # type: ignore[prop-decorator]
  1250. @property
  1251. def _windows(self) -> bool:
  1252. return platform.system() == "Windows"
  1253. @computed_field # type: ignore[prop-decorator]
  1254. @property
  1255. def app_url(self) -> str:
  1256. """The URL for the W&B UI, usually https://wandb.ai.
  1257. This is different from `base_url` (like https://api.wandb.ai) which
  1258. is used to access W&B APIs programmatically.
  1259. """
  1260. return self.app_url_override or util.api_to_app_url(self.base_url)
  1261. @computed_field # type: ignore[prop-decorator]
  1262. @property
  1263. def colab_url(self) -> Optional[str]:
  1264. """The URL to the Colab notebook, if running in Colab."""
  1265. if not self._colab:
  1266. return None
  1267. if self.x_jupyter_path and self.x_jupyter_path.startswith("fileId="):
  1268. unescaped = unquote(self.x_jupyter_path)
  1269. return "https://colab.research.google.com/notebook#" + unescaped
  1270. return None
  1271. @computed_field # type: ignore[prop-decorator]
  1272. @property
  1273. def deployment(self) -> Literal["local", "cloud"]:
  1274. return "local" if self.is_local else "cloud"
  1275. @computed_field # type: ignore[prop-decorator]
  1276. @property
  1277. def files_dir(self) -> str:
  1278. """Absolute path to the local directory where the run's files are stored."""
  1279. # Must match the logic in settings.go in the service process.
  1280. return self.x_files_dir or _path_convert(self.sync_dir, "files")
  1281. @computed_field # type: ignore[prop-decorator]
  1282. @property
  1283. def is_local(self) -> bool:
  1284. return str(self.base_url) != "https://api.wandb.ai"
  1285. @computed_field # type: ignore[prop-decorator]
  1286. @property
  1287. def log_dir(self) -> str:
  1288. """The directory for storing log files."""
  1289. return _path_convert(self.sync_dir, "logs")
  1290. @computed_field # type: ignore[prop-decorator]
  1291. @property
  1292. def log_internal(self) -> str:
  1293. """The path to the file to use for internal logs."""
  1294. return _path_convert(self.log_dir, "debug-internal.log")
  1295. @computed_field # type: ignore[prop-decorator]
  1296. @property
  1297. def log_symlink_internal(self) -> str:
  1298. """The path to the symlink to the internal log file of the most recent run."""
  1299. return _path_convert(self.wandb_dir, "debug-internal.log")
  1300. @computed_field # type: ignore[prop-decorator]
  1301. @property
  1302. def log_symlink_user(self) -> str:
  1303. """The path to the symlink to the user-process log file of the most recent run."""
  1304. return _path_convert(self.wandb_dir, "debug.log")
  1305. @computed_field # type: ignore[prop-decorator]
  1306. @property
  1307. def log_user(self) -> str:
  1308. """The path to the file to use for user-process logs."""
  1309. return _path_convert(self.log_dir, "debug.log")
  1310. @computed_field # type: ignore[prop-decorator]
  1311. @property
  1312. def project_url(self) -> str:
  1313. """The W&B URL where the project can be viewed."""
  1314. project_url = self._project_url_base()
  1315. if not project_url:
  1316. return ""
  1317. return project_url
  1318. @computed_field # type: ignore[prop-decorator]
  1319. @property
  1320. def resume_fname(self) -> str:
  1321. """The path to the resume file."""
  1322. return _path_convert(self.wandb_dir, "wandb-resume.json")
  1323. @computed_field # type: ignore[prop-decorator]
  1324. @property
  1325. def run_mode(self) -> Literal["run", "offline-run"]:
  1326. """The mode of the run. Can be either "run" or "offline-run"."""
  1327. return "run" if not self._offline else "offline-run"
  1328. @computed_field # type: ignore[prop-decorator]
  1329. @property
  1330. def run_url(self) -> str:
  1331. """The W&B URL where the run can be viewed."""
  1332. project_url = self._project_url_base()
  1333. if not all([project_url, self.run_id]):
  1334. return ""
  1335. # Exclude specific safe characters from URL encoding to prevent 404 errors
  1336. safe_chars = "=+&$@"
  1337. return f"{project_url}/runs/{quote(self.run_id or '', safe=safe_chars)}"
  1338. @computed_field # type: ignore[prop-decorator]
  1339. @property
  1340. def settings_workspace(self) -> str:
  1341. """The path to the workspace settings file."""
  1342. return _path_convert(self.wandb_dir, "settings")
  1343. @computed_field # type: ignore[prop-decorator]
  1344. @property
  1345. def sweep_url(self) -> str:
  1346. """The W&B URL where the sweep can be viewed."""
  1347. project_url = self._project_url_base()
  1348. if not all([project_url, self.sweep_id]):
  1349. return ""
  1350. return f"{project_url}/sweeps/{quote(self.sweep_id or '')}"
  1351. @computed_field # type: ignore[prop-decorator]
  1352. @property
  1353. def sync_dir(self) -> str:
  1354. """The directory for storing the run's files."""
  1355. name = f"{self.run_mode}-{self.timespec}-{self.run_id}"
  1356. if self.x_sync_dir_suffix:
  1357. name += f"-{self.x_sync_dir_suffix}"
  1358. return _path_convert(self.wandb_dir, name)
  1359. @computed_field # type: ignore[prop-decorator]
  1360. @property
  1361. def sync_file(self) -> str:
  1362. """Path to the append-only binary transaction log file."""
  1363. return _path_convert(self.sync_dir, f"run-{self.run_id}.wandb")
  1364. @computed_field # type: ignore[prop-decorator]
  1365. @property
  1366. def sync_symlink_latest(self) -> str:
  1367. """Path to the symlink to the most recent run's transaction log file."""
  1368. return _path_convert(self.wandb_dir, "latest-run")
  1369. @computed_field # type: ignore[prop-decorator]
  1370. @property
  1371. def timespec(self) -> str:
  1372. """The time specification for the run."""
  1373. return self._start_datetime
  1374. @computed_field # type: ignore[prop-decorator]
  1375. @property
  1376. def wandb_dir(self) -> str:
  1377. """Full path to the wandb directory."""
  1378. if self.use_dot_wandb is None:
  1379. use_dot = pathlib.Path(self.root_dir, ".wandb").exists()
  1380. else:
  1381. use_dot = self.use_dot_wandb
  1382. dirname = ".wandb" if use_dot else "wandb"
  1383. return str(pathlib.Path(self.root_dir, dirname).expanduser())
  1384. # Methods to collect and update settings from different sources.
  1385. #
  1386. # The Settings class does not track the source of the settings,
  1387. # so it is up to the developer to ensure that the settings are applied
  1388. # in the correct order. Most of the updates are done in
  1389. # wandb/sdk/wandb_setup.py::_WandbSetup._settings_setup.
  1390. def read_system_settings(self) -> settings_file.SettingsFiles:
  1391. """Read settings from the workspace and global settings files.
  1392. The files are determined by the settings_system and settings_workspace
  1393. settings.
  1394. The resulting object is a snapshot of the system settings at the time
  1395. this function is used and does not reflect the settings on this Settings
  1396. object. It can be used to update the files, and it should be short-lived
  1397. since it does not reflect external changes to the files.
  1398. Updating the settings files does not update this Settings instance
  1399. and vice versa.
  1400. <!-- lazydoc-ignore: internal -->
  1401. """
  1402. local_settings = pathlib.Path(self.settings_workspace)
  1403. if self.settings_system:
  1404. global_settings = pathlib.Path(self.settings_system)
  1405. else:
  1406. global_settings = None
  1407. return settings_file.SettingsFiles(
  1408. global_settings=global_settings,
  1409. local_settings=local_settings,
  1410. )
  1411. def update_from_system_settings(self) -> None:
  1412. """Load settings from the settings files.
  1413. If settings files contain invalid settings, prints and suppresses
  1414. the error.
  1415. <!-- lazydoc-ignore: internal -->
  1416. """
  1417. system_settings = self.read_system_settings()
  1418. if len(system_settings.sources) == 0:
  1419. return
  1420. elif len(system_settings.sources) == 1:
  1421. source_string = str(system_settings.sources[0])
  1422. else:
  1423. source_string = "\n" + "\n".join(
  1424. f" {source}" for source in system_settings.sources
  1425. )
  1426. # Print at the start so that users can diagnose uncaught exceptions.
  1427. if not self.quiet:
  1428. printed_sources = True
  1429. wandb.termlog(f"Loading settings from {source_string}")
  1430. else:
  1431. printed_sources = False
  1432. try:
  1433. parsed_settings = _parse_system_settings(system_settings)
  1434. except Exception as e:
  1435. if not printed_sources:
  1436. wandb.termerror(f"Failed to load settings from {source_string}")
  1437. if isinstance(e, ValidationError):
  1438. # Pydantic ValidationErrors have detailed messages that we can
  1439. # print without a stack trace.
  1440. wandb.termerror(str(e))
  1441. else:
  1442. # For all other errors, we need to dump a stack trace to make
  1443. # sure they're debuggable.
  1444. tb = traceback.format_exception(type(e), e, e.__traceback__)
  1445. wandb.termerror("".join(tb))
  1446. return
  1447. # We parse and set in different steps so that we do not partially
  1448. # apply a broken settings file.
  1449. #
  1450. # Note that this runs validation functions a second time, but we expect
  1451. # them to succeed.
  1452. self.update_from_settings(parsed_settings)
  1453. def update_from_env_vars(self, environ: Dict[str, Any]):
  1454. """Update settings from environment variables.
  1455. <!-- lazydoc-ignore: internal -->
  1456. """
  1457. env_prefix: str = "WANDB_"
  1458. private_env_prefix: str = env_prefix + "_"
  1459. special_env_var_names = {
  1460. env.APP_URL: "app_url_override",
  1461. "WANDB_SERVICE_TRANSPORT": "x_service_transport",
  1462. env.DIR: "root_dir",
  1463. env.NAME: "run_name",
  1464. env.NOTES: "run_notes",
  1465. env.TAGS: "run_tags",
  1466. env.JOB_TYPE: "run_job_type",
  1467. env.HTTP_TIMEOUT: "x_graphql_timeout_seconds",
  1468. env.FILE_PUSHER_TIMEOUT: "x_file_transfer_timeout_seconds",
  1469. env.USER_EMAIL: "email",
  1470. }
  1471. for setting, value in environ.items():
  1472. if not setting.startswith(env_prefix):
  1473. continue
  1474. if setting in special_env_var_names:
  1475. key = special_env_var_names[setting]
  1476. elif setting.startswith(private_env_prefix):
  1477. key = "x_" + setting[len(private_env_prefix) :].lower()
  1478. else:
  1479. # otherwise, strip the prefix and convert to lowercase
  1480. key = setting[len(env_prefix) :].lower()
  1481. if key not in self.__dict__:
  1482. continue
  1483. if key in ("ignore_globs", "run_tags"):
  1484. value = value.split(",")
  1485. if value is None:
  1486. continue
  1487. setattr(self, key, value)
  1488. def update_from_system_environment(self):
  1489. """Update settings from the system environment.
  1490. <!-- lazydoc-ignore: internal -->
  1491. """
  1492. # For code saving, only allow env var override if value from server is true, or
  1493. # if no preference was specified.
  1494. if (self.save_code is True or self.save_code is None) and (
  1495. os.getenv(env.SAVE_CODE) is not None
  1496. or os.getenv(env.DISABLE_CODE) is not None
  1497. ):
  1498. self.save_code = env.should_save_code()
  1499. if os.getenv(env.DISABLE_GIT) is not None:
  1500. self.disable_git = env.disable_git()
  1501. # Attempt to get notebook information if not already set by the user
  1502. if self._jupyter and (self.notebook_name is None or self.notebook_name == ""):
  1503. meta = wandb.jupyter.notebook_metadata(self.silent) # type: ignore
  1504. self.x_jupyter_path = meta.get("path")
  1505. self.x_jupyter_name = meta.get("name")
  1506. self.x_jupyter_root = meta.get("root")
  1507. elif (
  1508. self._jupyter
  1509. and self.notebook_name is not None
  1510. and os.path.exists(self.notebook_name)
  1511. ):
  1512. self.x_jupyter_path = self.notebook_name
  1513. self.x_jupyter_name = self.notebook_name
  1514. self.x_jupyter_root = os.getcwd()
  1515. elif self._jupyter:
  1516. wandb.termwarn(
  1517. "WANDB_NOTEBOOK_NAME should be a path to a notebook file, "
  1518. f"couldn't find {self.notebook_name}.",
  1519. )
  1520. # host is populated by update_from_env_vars if the corresponding env
  1521. # vars exist -- but if they don't, we'll fill them in here.
  1522. if self.host is None:
  1523. self.host = socket.gethostname() # type: ignore
  1524. _executable = (
  1525. self.x_executable
  1526. or os.environ.get(env._EXECUTABLE)
  1527. or sys.executable
  1528. or shutil.which("python3")
  1529. or "python3"
  1530. )
  1531. self.x_executable = _executable
  1532. if self.docker is None:
  1533. self.docker = env.get_docker(util.image_id_from_k8s())
  1534. # proceed if not in CLI mode
  1535. if self.x_cli_only_mode:
  1536. return
  1537. program = self.program or self._get_program()
  1538. if program is not None:
  1539. self._setup_code_paths(program)
  1540. else:
  1541. program = "<python with no main file>"
  1542. self.program = program
  1543. def update_from_dict(self, settings: Dict[str, Any]) -> None:
  1544. """Update settings from a dictionary.
  1545. <!-- lazydoc-ignore: internal -->
  1546. """
  1547. for key, value in dict(settings).items():
  1548. if value is not None:
  1549. setattr(self, key, value)
  1550. def update_from_settings(self, settings: Settings) -> None:
  1551. """Update settings from another instance of `Settings`.
  1552. <!-- lazydoc-ignore: internal -->
  1553. """
  1554. d = {field: getattr(settings, field) for field in settings.model_fields_set}
  1555. if d:
  1556. self.update_from_dict(d)
  1557. # Helper methods.
  1558. def to_proto(self) -> wandb_settings_pb2.Settings:
  1559. """Generate a protobuf representation of the settings.
  1560. <!-- lazydoc-ignore: internal -->
  1561. """
  1562. settings_proto = wandb_settings_pb2.Settings()
  1563. for k, v in self.model_dump(exclude_none=True).items():
  1564. if k in CLIENT_ONLY_SETTINGS:
  1565. continue
  1566. # Special case for x_stats_open_metrics_filters.
  1567. if k == "x_stats_open_metrics_filters":
  1568. if isinstance(v, (list, set, tuple)):
  1569. setting = getattr(settings_proto, k)
  1570. setting.sequence.value.extend(v)
  1571. elif isinstance(v, dict):
  1572. setting = getattr(settings_proto, k)
  1573. for key, value in v.items():
  1574. for kk, vv in value.items():
  1575. setting.mapping.value[key].value[kk] = vv
  1576. else:
  1577. raise TypeError(f"Unsupported type {type(v)} for setting {k}")
  1578. continue
  1579. # Special case for RunMoment fields.
  1580. if k in ("fork_from", "resume_from"):
  1581. run_moment = (
  1582. v
  1583. if isinstance(v, RunMoment)
  1584. else RunMoment(
  1585. run=v.get("run"),
  1586. value=v.get("value"),
  1587. metric=v.get("metric"),
  1588. )
  1589. )
  1590. getattr(settings_proto, k).CopyFrom(
  1591. wandb_settings_pb2.RunMoment(
  1592. run=run_moment.run,
  1593. value=run_moment.value,
  1594. metric=run_moment.metric,
  1595. )
  1596. )
  1597. continue
  1598. if isinstance(v, bool):
  1599. getattr(settings_proto, k).CopyFrom(BoolValue(value=v))
  1600. elif isinstance(v, int):
  1601. getattr(settings_proto, k).CopyFrom(Int32Value(value=v))
  1602. elif isinstance(v, float):
  1603. getattr(settings_proto, k).CopyFrom(DoubleValue(value=v))
  1604. elif isinstance(v, str):
  1605. getattr(settings_proto, k).CopyFrom(StringValue(value=v))
  1606. elif isinstance(v, (list, set, tuple)):
  1607. # we only support sequences of strings for now
  1608. sequence = getattr(settings_proto, k)
  1609. sequence.value.extend(v)
  1610. elif isinstance(v, dict):
  1611. mapping = getattr(settings_proto, k)
  1612. for key, value in v.items():
  1613. # we only support dicts with string values for now
  1614. mapping.value[key] = value
  1615. elif v is None:
  1616. # None means that the setting value was not set.
  1617. pass
  1618. else:
  1619. raise TypeError(f"Unsupported type {type(v)} for setting {k}")
  1620. return settings_proto
  1621. def _get_program(self) -> Optional[str]:
  1622. """Get the program that started the current process."""
  1623. if self._jupyter:
  1624. # If in a notebook, try to get the program from the notebook metadata.
  1625. if self.notebook_name:
  1626. return self.notebook_name
  1627. if not self.x_jupyter_path:
  1628. return self.program
  1629. if self.x_jupyter_path.startswith("fileId="):
  1630. return self.x_jupyter_name
  1631. return self.x_jupyter_path
  1632. # If not in a notebook, try to get the program from the environment
  1633. # or the __main__ module for scripts run as `python -m ...`.
  1634. program = os.getenv(env.PROGRAM)
  1635. if program is not None:
  1636. return program
  1637. try:
  1638. import __main__
  1639. except ImportError:
  1640. return None
  1641. try:
  1642. if __main__.__spec__ is None:
  1643. python_args = __main__.__file__
  1644. else:
  1645. python_args = f"-m {__main__.__spec__.name}"
  1646. except AttributeError:
  1647. return None
  1648. return python_args
  1649. @staticmethod
  1650. def _get_program_relpath(program: str, root: Optional[str] = None) -> Optional[str]:
  1651. """Get the relative path to the program from the root directory."""
  1652. if not program:
  1653. return None
  1654. root = root or os.getcwd()
  1655. if not root:
  1656. return None
  1657. # For windows, if the root and program are on different drives,
  1658. # os.path.relpath will raise a ValueError.
  1659. if not filesystem.are_paths_on_same_drive(
  1660. pathlib.Path(root), pathlib.Path(program)
  1661. ):
  1662. return None
  1663. full_path_to_program = os.path.join(
  1664. root, os.path.relpath(os.getcwd(), root), program
  1665. )
  1666. if os.path.exists(full_path_to_program):
  1667. relative_path = os.path.relpath(full_path_to_program, start=root)
  1668. if "../" in relative_path:
  1669. return None
  1670. return relative_path
  1671. return None
  1672. def _project_url_base(self) -> str:
  1673. """Construct the base URL for the project."""
  1674. if not all([self.entity, self.project]):
  1675. return ""
  1676. return f"{self.app_url}/{quote(self.entity or '')}/{quote(self.project or '')}"
  1677. @staticmethod
  1678. def _runmoment_preprocessor(
  1679. val: Union[RunMoment, str, None],
  1680. ) -> Optional[RunMoment]:
  1681. """Preprocess the setting for forking or resuming a run."""
  1682. if isinstance(val, RunMoment) or val is None:
  1683. return val
  1684. elif isinstance(val, str):
  1685. return RunMoment.from_uri(val)
  1686. if not IS_PYDANTIC_V2:
  1687. def model_copy(self, *args, **kwargs):
  1688. return self.copy(*args, **kwargs)
  1689. def model_dump(self, **kwargs):
  1690. """Compatibility method for Pydantic v1 to mimic v2's model_dump.
  1691. In v1, this is equivalent to dict() but also includes computed properties.
  1692. Args:
  1693. **kwargs: Options passed to the dict method
  1694. - exclude_none: Whether to exclude fields with None values
  1695. Returns:
  1696. A dictionary of the model's fields and computed properties
  1697. """
  1698. # Handle exclude_none separately since it's named differently in v1
  1699. exclude_none = kwargs.pop("exclude_none", False)
  1700. # Start with regular fields from dict()
  1701. result = self.dict(**kwargs)
  1702. # Get all computed properties
  1703. for name in dir(self.__class__):
  1704. attr = getattr(self.__class__, name, None)
  1705. if isinstance(attr, property):
  1706. try:
  1707. # Only include properties that don't raise errors
  1708. value = getattr(self, name)
  1709. result[name] = value
  1710. except (AttributeError, NotImplementedError, TypeError, ValueError):
  1711. # Skip properties that can't be accessed or raise errors
  1712. pass
  1713. elif isinstance(attr, RunMoment):
  1714. value = getattr(self, name)
  1715. result[name] = value
  1716. # Special Pydantic attributes that should always be excluded
  1717. exclude_fields = {
  1718. "model_config",
  1719. "model_fields",
  1720. "model_fields_set",
  1721. "__fields__",
  1722. "__model_fields_set",
  1723. "__pydantic_self__",
  1724. "__pydantic_initialised__",
  1725. }
  1726. # Remove special Pydantic attributes
  1727. for field in exclude_fields:
  1728. if field in result:
  1729. del result[field]
  1730. if exclude_none:
  1731. # Remove None values from the result
  1732. return {k: v for k, v in result.items() if v is not None}
  1733. return result
  1734. @property
  1735. def model_fields_set(self) -> set:
  1736. """Return a set of fields that have been explicitly set.
  1737. This is a compatibility property for Pydantic v1 to mimic v2's model_fields_set.
  1738. """
  1739. return getattr(self, "__fields_set__", set())
  1740. def _setup_code_paths(self, program: str):
  1741. """Sets the program_abspath and program_relpath settings."""
  1742. if self._jupyter and self.x_jupyter_root:
  1743. self._infer_code_paths_for_jupyter(program)
  1744. else:
  1745. self._infer_code_path_for_program(program)
  1746. def _infer_code_path_for_program(self, program: str):
  1747. """Finds the program's absolute and relative paths."""
  1748. from .lib.gitlib import GitRepo
  1749. try:
  1750. root = (
  1751. GitRepo().root or os.getcwd() if not self.disable_git else os.getcwd()
  1752. )
  1753. except Exception:
  1754. # if the git command fails, fall back to the current working directory
  1755. root = os.getcwd()
  1756. self.program_relpath = self.program_relpath or self._get_program_relpath(
  1757. program, root
  1758. )
  1759. program_abspath = os.path.abspath(
  1760. os.path.join(root, os.path.relpath(os.getcwd(), root), program)
  1761. )
  1762. if os.path.exists(program_abspath):
  1763. self.program_abspath = program_abspath
  1764. def _infer_code_paths_for_jupyter(self, program: str):
  1765. """Find the notebook's absolute and relative paths.
  1766. Since the notebook's execution environment
  1767. is not the same as the current working directory.
  1768. We utilize the metadata provided by the jupyter server.
  1769. """
  1770. if not self.x_jupyter_root or not program:
  1771. return None
  1772. self.program_abspath = os.path.abspath(
  1773. os.path.join(self.x_jupyter_root, program)
  1774. )
  1775. self.program_relpath = program
  1776. def _parse_system_settings(
  1777. system_settings: settings_file.SettingsFiles,
  1778. ) -> Settings:
  1779. """Validate settings from a settings file.
  1780. Returns:
  1781. A validated Settings object.
  1782. Raises:
  1783. ValidationError: on invalid data.
  1784. Exception: arbitrary errors can occur when constructing Settings.
  1785. """
  1786. fields: dict[str, Any] = dict()
  1787. value: object # Can be transformed arbitrarily.
  1788. for key, value in system_settings.all().items():
  1789. if key == "ignore_globs":
  1790. fields[key] = value.split(",")
  1791. elif key == "anonymous":
  1792. wandb.termwarn(
  1793. "Deprecated setting 'anonymous' has no effect and will be"
  1794. + " removed in a future version of wandb."
  1795. + " Please delete it manually or by running `wandb login`"
  1796. + " to avoid errors.",
  1797. repeat=False,
  1798. )
  1799. fields[key] = deprecation.UNSET
  1800. elif key in ("settings_system", "root_dir"):
  1801. wandb.termwarn(
  1802. f"Ignoring setting {key!r} which is not allowed in a settings file."
  1803. + " Please delete it manually to avoid errors in the future."
  1804. )
  1805. else:
  1806. fields[key] = value
  1807. # NOTE: Field validators must raise ValueError for Pydantic to wrap them
  1808. # in a ValidationError. Other kinds of errors will bubble up unaltered.
  1809. #
  1810. # Unfortunately, some validators return a UsageError, which has special
  1811. # handling in the CLI and may require care to change.
  1812. return Settings(**fields)