wandb 0.19.9__py3-none-win_amd64.whl → 0.19.11__py3-none-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. wandb/__init__.py +1 -1
  2. wandb/__init__.pyi +6 -3
  3. wandb/_pydantic/__init__.py +14 -8
  4. wandb/_pydantic/base.py +51 -36
  5. wandb/_pydantic/utils.py +73 -0
  6. wandb/_pydantic/v1_compat.py +79 -57
  7. wandb/apis/public/__init__.py +2 -2
  8. wandb/apis/public/api.py +684 -4
  9. wandb/apis/public/artifacts.py +377 -677
  10. wandb/apis/public/automations.py +69 -0
  11. wandb/apis/public/integrations.py +180 -0
  12. wandb/apis/public/projects.py +29 -0
  13. wandb/apis/public/registries/__init__.py +0 -0
  14. wandb/apis/public/registries/_freezable_list.py +179 -0
  15. wandb/apis/public/{registries.py → registries/registries_search.py} +22 -129
  16. wandb/apis/public/registries/registry.py +357 -0
  17. wandb/apis/public/registries/utils.py +140 -0
  18. wandb/apis/public/runs.py +58 -56
  19. wandb/apis/public/utils.py +107 -1
  20. wandb/automations/__init__.py +73 -0
  21. wandb/automations/_filters/__init__.py +40 -0
  22. wandb/automations/_filters/expressions.py +181 -0
  23. wandb/automations/_filters/operators.py +258 -0
  24. wandb/automations/_filters/run_metrics.py +332 -0
  25. wandb/automations/_generated/__init__.py +177 -0
  26. wandb/automations/_generated/create_automation.py +17 -0
  27. wandb/automations/_generated/create_generic_webhook_integration.py +43 -0
  28. wandb/automations/_generated/delete_automation.py +17 -0
  29. wandb/automations/_generated/enums.py +33 -0
  30. wandb/automations/_generated/fragments.py +358 -0
  31. wandb/automations/_generated/generic_webhook_integrations_by_entity.py +22 -0
  32. wandb/automations/_generated/get_automations.py +24 -0
  33. wandb/automations/_generated/get_automations_by_entity.py +26 -0
  34. wandb/automations/_generated/input_types.py +104 -0
  35. wandb/automations/_generated/integrations_by_entity.py +22 -0
  36. wandb/automations/_generated/operations.py +647 -0
  37. wandb/automations/_generated/slack_integrations_by_entity.py +22 -0
  38. wandb/automations/_generated/update_automation.py +17 -0
  39. wandb/automations/_utils.py +237 -0
  40. wandb/automations/_validators.py +165 -0
  41. wandb/automations/actions.py +220 -0
  42. wandb/automations/automations.py +87 -0
  43. wandb/automations/events.py +287 -0
  44. wandb/automations/integrations.py +45 -0
  45. wandb/automations/scopes.py +78 -0
  46. wandb/beta/workflows.py +9 -10
  47. wandb/bin/gpu_stats.exe +0 -0
  48. wandb/bin/wandb-core +0 -0
  49. wandb/cli/cli.py +3 -3
  50. wandb/env.py +11 -0
  51. wandb/integration/keras/keras.py +2 -1
  52. wandb/integration/langchain/wandb_tracer.py +2 -1
  53. wandb/jupyter.py +137 -118
  54. wandb/old/settings.py +4 -1
  55. wandb/old/summary.py +0 -2
  56. wandb/proto/v3/wandb_internal_pb2.py +297 -292
  57. wandb/proto/v3/wandb_settings_pb2.py +2 -2
  58. wandb/proto/v3/wandb_telemetry_pb2.py +10 -10
  59. wandb/proto/v4/wandb_internal_pb2.py +292 -292
  60. wandb/proto/v4/wandb_settings_pb2.py +2 -2
  61. wandb/proto/v4/wandb_telemetry_pb2.py +10 -10
  62. wandb/proto/v5/wandb_internal_pb2.py +292 -292
  63. wandb/proto/v5/wandb_settings_pb2.py +2 -2
  64. wandb/proto/v5/wandb_telemetry_pb2.py +10 -10
  65. wandb/proto/v6/wandb_base_pb2.py +41 -0
  66. wandb/proto/v6/wandb_internal_pb2.py +393 -0
  67. wandb/proto/v6/wandb_server_pb2.py +78 -0
  68. wandb/proto/v6/wandb_settings_pb2.py +58 -0
  69. wandb/proto/v6/wandb_telemetry_pb2.py +52 -0
  70. wandb/proto/wandb_base_pb2.py +2 -0
  71. wandb/proto/wandb_deprecated.py +8 -0
  72. wandb/proto/wandb_internal_pb2.py +3 -1
  73. wandb/proto/wandb_server_pb2.py +2 -0
  74. wandb/proto/wandb_settings_pb2.py +2 -0
  75. wandb/proto/wandb_telemetry_pb2.py +2 -0
  76. wandb/sdk/artifacts/_generated/__init__.py +289 -0
  77. wandb/sdk/artifacts/_generated/add_aliases.py +21 -0
  78. wandb/sdk/artifacts/_generated/artifact_collection_membership_files.py +43 -0
  79. wandb/sdk/artifacts/_generated/artifact_version_files.py +36 -0
  80. wandb/sdk/artifacts/_generated/create_artifact_collection_tag_assignments.py +36 -0
  81. wandb/sdk/artifacts/_generated/delete_aliases.py +21 -0
  82. wandb/sdk/artifacts/_generated/delete_artifact_collection_tag_assignments.py +25 -0
  83. wandb/sdk/artifacts/_generated/delete_artifact_portfolio.py +35 -0
  84. wandb/sdk/artifacts/_generated/delete_artifact_sequence.py +35 -0
  85. wandb/sdk/artifacts/_generated/enums.py +17 -0
  86. wandb/sdk/artifacts/_generated/fetch_linked_artifacts.py +67 -0
  87. wandb/sdk/artifacts/_generated/fragments.py +221 -0
  88. wandb/sdk/artifacts/_generated/input_types.py +28 -0
  89. wandb/sdk/artifacts/_generated/move_artifact_collection.py +35 -0
  90. wandb/sdk/artifacts/_generated/operations.py +611 -0
  91. wandb/sdk/artifacts/_generated/project_artifact_collection.py +101 -0
  92. wandb/sdk/artifacts/_generated/project_artifact_collections.py +33 -0
  93. wandb/sdk/artifacts/_generated/project_artifact_type.py +24 -0
  94. wandb/sdk/artifacts/_generated/project_artifact_types.py +24 -0
  95. wandb/sdk/artifacts/_generated/project_artifacts.py +42 -0
  96. wandb/sdk/artifacts/_generated/run_input_artifacts.py +51 -0
  97. wandb/sdk/artifacts/_generated/run_output_artifacts.py +51 -0
  98. wandb/sdk/artifacts/_generated/update_artifact.py +26 -0
  99. wandb/sdk/artifacts/_generated/update_artifact_portfolio.py +35 -0
  100. wandb/sdk/artifacts/_generated/update_artifact_sequence.py +35 -0
  101. wandb/sdk/artifacts/_graphql_fragments.py +57 -79
  102. wandb/sdk/artifacts/_validators.py +120 -1
  103. wandb/sdk/artifacts/artifact.py +419 -215
  104. wandb/sdk/artifacts/artifact_file_cache.py +4 -6
  105. wandb/sdk/artifacts/artifact_manifest_entry.py +13 -3
  106. wandb/sdk/artifacts/storage_handlers/azure_handler.py +1 -0
  107. wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py +182 -1
  108. wandb/sdk/artifacts/storage_policy.py +3 -0
  109. wandb/sdk/data_types/base_types/media.py +2 -3
  110. wandb/sdk/data_types/base_types/wb_value.py +34 -11
  111. wandb/sdk/data_types/html.py +36 -9
  112. wandb/sdk/data_types/image.py +12 -12
  113. wandb/sdk/data_types/table.py +5 -0
  114. wandb/sdk/data_types/trace_tree.py +2 -0
  115. wandb/sdk/data_types/utils.py +1 -1
  116. wandb/sdk/data_types/video.py +59 -57
  117. wandb/sdk/interface/interface.py +4 -3
  118. wandb/sdk/internal/internal_api.py +21 -31
  119. wandb/sdk/internal/profiler.py +6 -5
  120. wandb/sdk/internal/run.py +13 -6
  121. wandb/sdk/internal/sender.py +5 -2
  122. wandb/sdk/launch/sweeps/utils.py +8 -0
  123. wandb/sdk/lib/apikey.py +25 -4
  124. wandb/sdk/lib/asyncio_compat.py +1 -1
  125. wandb/sdk/lib/deprecate.py +13 -22
  126. wandb/sdk/lib/disabled.py +2 -1
  127. wandb/sdk/lib/printer.py +37 -8
  128. wandb/sdk/lib/printer_asyncio.py +46 -0
  129. wandb/sdk/lib/redirect.py +10 -5
  130. wandb/sdk/projects/_generated/__init__.py +47 -0
  131. wandb/sdk/projects/_generated/delete_project.py +22 -0
  132. wandb/sdk/projects/_generated/enums.py +4 -0
  133. wandb/sdk/projects/_generated/fetch_registry.py +22 -0
  134. wandb/sdk/projects/_generated/fragments.py +41 -0
  135. wandb/sdk/projects/_generated/input_types.py +13 -0
  136. wandb/sdk/projects/_generated/operations.py +88 -0
  137. wandb/sdk/projects/_generated/rename_project.py +27 -0
  138. wandb/sdk/projects/_generated/upsert_registry_project.py +27 -0
  139. wandb/sdk/service/server_sock.py +19 -14
  140. wandb/sdk/service/service.py +18 -8
  141. wandb/sdk/service/streams.py +5 -0
  142. wandb/sdk/verify/verify.py +6 -3
  143. wandb/sdk/wandb_init.py +217 -70
  144. wandb/sdk/wandb_login.py +13 -4
  145. wandb/sdk/wandb_run.py +419 -295
  146. wandb/sdk/wandb_settings.py +27 -10
  147. wandb/sdk/wandb_setup.py +61 -0
  148. wandb/util.py +33 -29
  149. {wandb-0.19.9.dist-info → wandb-0.19.11.dist-info}/METADATA +5 -5
  150. {wandb-0.19.9.dist-info → wandb-0.19.11.dist-info}/RECORD +153 -83
  151. wandb/_globals.py +0 -19
  152. wandb/sdk/internal/_generated/base.py +0 -226
  153. wandb/sdk/internal/_generated/typing_compat.py +0 -14
  154. {wandb-0.19.9.dist-info → wandb-0.19.11.dist-info}/WHEEL +0 -0
  155. {wandb-0.19.9.dist-info → wandb-0.19.11.dist-info}/entry_points.txt +0 -0
  156. {wandb-0.19.9.dist-info → wandb-0.19.11.dist-info}/licenses/LICENSE +0 -0
wandb/__init__.py CHANGED
@@ -10,7 +10,7 @@ For reference documentation, see https://docs.wandb.com/ref/python.
10
10
  """
11
11
  from __future__ import annotations
12
12
 
13
- __version__ = "0.19.9"
13
+ __version__ = "0.19.11"
14
14
 
15
15
 
16
16
  from wandb.errors import Error
wandb/__init__.pyi CHANGED
@@ -106,7 +106,7 @@ if TYPE_CHECKING:
106
106
  import wandb
107
107
  from wandb.plot import CustomChart
108
108
 
109
- __version__: str = "0.19.9"
109
+ __version__: str = "0.19.11"
110
110
 
111
111
  run: Run | None
112
112
  config: wandb_config.Config
@@ -229,6 +229,7 @@ def init(
229
229
  "default",
230
230
  "return_previous",
231
231
  "finish_previous",
232
+ "create_new",
232
233
  ]
233
234
  ) = None,
234
235
  resume: bool | Literal["allow", "never", "must", "auto"] | None = None,
@@ -476,6 +477,7 @@ def login(
476
477
  force: Optional[bool] = None,
477
478
  timeout: Optional[int] = None,
478
479
  verify: bool = False,
480
+ referrer: Optional[str] = None,
479
481
  ) -> bool:
480
482
  """Set up W&B login credentials.
481
483
 
@@ -495,6 +497,7 @@ def login(
495
497
  force: (bool, optional) If true, will force a relogin.
496
498
  timeout: (int, optional) Number of seconds to wait for user input.
497
499
  verify: (bool) Verify the credentials with the W&B server.
500
+ referrer: (string, optional) The referrer to use in the URL login request.
498
501
 
499
502
  Returns:
500
503
  bool: if key is configured
@@ -1075,7 +1078,7 @@ def link_model(
1075
1078
  registered_model_name: str,
1076
1079
  name: str | None = None,
1077
1080
  aliases: list[str] | None = None,
1078
- ) -> None:
1081
+ ) -> Artifact | None:
1079
1082
  """Log a model artifact version and link it to a registered model in the model registry.
1080
1083
 
1081
1084
  The linked model version will be visible in the UI for the specified registered model.
@@ -1137,7 +1140,7 @@ def link_model(
1137
1140
  ValueError: if name has invalid special characters
1138
1141
 
1139
1142
  Returns:
1140
- None
1143
+ The linked artifact if linking was successful, otherwise None.
1141
1144
  """
1142
1145
  ...
1143
1146
 
@@ -1,17 +1,19 @@
1
1
  """Internal utilities for working with pydantic."""
2
2
 
3
- from .base import Base, GQLBase, GQLId, SerializedToJson, Typename
4
- from .v1_compat import (
5
- IS_PYDANTIC_V2,
6
- AliasChoices,
7
- computed_field,
8
- field_validator,
9
- model_validator,
3
+ from .base import (
4
+ CompatBaseModel,
5
+ GQLBase,
6
+ GQLId,
7
+ SerializedToJson,
8
+ Typename,
9
+ ensure_json,
10
10
  )
11
+ from .utils import IS_PYDANTIC_V2, from_json, pydantic_isinstance, to_json
12
+ from .v1_compat import AliasChoices, computed_field, field_validator, model_validator
11
13
 
12
14
  __all__ = [
13
15
  "IS_PYDANTIC_V2",
14
- "Base",
16
+ "CompatBaseModel",
15
17
  "GQLBase",
16
18
  "Typename",
17
19
  "GQLId",
@@ -20,4 +22,8 @@ __all__ = [
20
22
  "computed_field",
21
23
  "field_validator",
22
24
  "model_validator",
25
+ "pydantic_isinstance",
26
+ "to_json",
27
+ "from_json",
28
+ "ensure_json",
23
29
  ]
wandb/_pydantic/base.py CHANGED
@@ -4,9 +4,10 @@ from __future__ import annotations
4
4
 
5
5
  from typing import TYPE_CHECKING, Any, Callable, Literal, TypeVar
6
6
 
7
- from pydantic import BaseModel, ConfigDict, Field, Json
7
+ from pydantic import BaseModel, ConfigDict, Field, Json, StrictStr
8
8
  from typing_extensions import Annotated, TypedDict, Unpack, override
9
9
 
10
+ from .utils import IS_PYDANTIC_V2, to_json
10
11
  from .v1_compat import PydanticCompatMixin
11
12
 
12
13
  if TYPE_CHECKING:
@@ -36,17 +37,25 @@ MODEL_DUMP_DEFAULTS = ModelDumpKwargs(
36
37
  )
37
38
 
38
39
 
39
- # Base class for all generated classes/types.
40
+ # v1-compatible base class for pydantic types.
41
+ class CompatBaseModel(PydanticCompatMixin, BaseModel):
42
+ __doc__ = None # Prevent subclasses from inheriting the BaseModel docstring
43
+
44
+
45
+ # Base class for all GraphQL-generated types.
40
46
  # Omitted from docstring to avoid inclusion in generated docs.
41
- class Base(PydanticCompatMixin, BaseModel):
47
+ class GQLBase(CompatBaseModel):
42
48
  model_config = ConfigDict(
43
- populate_by_name=True,
49
+ populate_by_name=True, # Discouraged in pydantic v2.11+, will be deprecated in v3
50
+ validate_by_name=True, # Introduced in pydantic v2.11
51
+ validate_by_alias=True, # Introduced in pydantic v2.11
52
+ serialize_by_alias=True, # Introduced in pydantic v2.11
44
53
  validate_assignment=True,
45
54
  validate_default=True,
46
- extra="forbid",
47
55
  use_attribute_docstrings=True,
48
56
  from_attributes=True,
49
57
  revalidate_instances="always",
58
+ protected_namespaces=(), # Some GraphQL fields may begin with "model_"
50
59
  )
51
60
 
52
61
  @override
@@ -70,44 +79,50 @@ class Base(PydanticCompatMixin, BaseModel):
70
79
  return super().model_dump_json(indent=indent, **kwargs)
71
80
 
72
81
 
73
- # Base class with extra customization for GQL generated types.
74
- # Omitted from docstring to avoid inclusion in generated docs.
75
- class GQLBase(Base):
76
- model_config = ConfigDict(
77
- extra="ignore",
78
- protected_namespaces=(),
79
- )
80
-
81
-
82
82
  # ------------------------------------------------------------------------------
83
83
  # Reusable annotations for field types
84
84
  T = TypeVar("T")
85
85
 
86
- GQLId = Annotated[
87
- str,
88
- Field(repr=False, strict=True, frozen=True),
89
- ]
86
+ if IS_PYDANTIC_V2 or TYPE_CHECKING:
87
+ GQLId = Annotated[
88
+ StrictStr,
89
+ Field(repr=False, frozen=True),
90
+ ]
91
+ else:
92
+ # FIXME: Find a way to fix this for pydantic v1, which doesn't like when
93
+ # `Field(...)` used in the field assignment AND `Annotated[...]`.
94
+ # This is a problem for codegen, which can currently outputs e.g.
95
+ #
96
+ # class MyModel(GQLBase):
97
+ # my_id: GQLId = Field(alias="myID")
98
+ #
99
+ GQLId = StrictStr # type: ignore[misc]
90
100
 
91
101
  Typename = Annotated[
92
102
  T,
93
- Field(repr=False, alias="__typename", frozen=True),
103
+ Field(repr=False, frozen=True, alias="__typename"),
94
104
  ]
95
105
 
96
106
 
97
- # FIXME: Restore, modify, or replace this later after ensuring pydantic v1 compatibility.
98
- # def validate_maybe_json(v: Any, handler: ValidatorFunctionWrapHandler) -> Any:
99
- # """Wraps default Json[...] field validator to allow instantiation with an already-decoded value."""
100
- # try:
101
- # return handler(v)
102
- # except ValidationError:
103
- # # Try revalidating after properly jsonifying the value
104
- # return handler(to_json(v, by_alias=True, round_trip=True))
105
- #
106
- #
107
- # SerializedToJson = Annotated[
108
- # Json[T],
109
- # # Allow lenient instantiation/validation: incoming data may already be deserialized.
110
- # WrapValidator(validate_maybe_json),
111
- # ]
112
-
113
- SerializedToJson = Json[T]
107
+ def ensure_json(v: Any) -> Any:
108
+ """In case the incoming value isn't serialized JSON, reserialize it.
109
+
110
+ This lets us use `Json[...]` fields with values that are already deserialized.
111
+ """
112
+ # NOTE: Assumes that the deserialized type is not itself a string.
113
+ # Revisit this if we need to support deserialized types that are str/bytes.
114
+ return v if isinstance(v, (str, bytes)) else to_json(v)
115
+
116
+
117
+ if IS_PYDANTIC_V2 or TYPE_CHECKING:
118
+ from pydantic import BeforeValidator, PlainSerializer
119
+
120
+ SerializedToJson = Annotated[
121
+ Json[T],
122
+ # Allow lenient instantiation/validation: incoming data may already be deserialized.
123
+ BeforeValidator(ensure_json),
124
+ PlainSerializer(to_json),
125
+ ]
126
+ else:
127
+ # FIXME: Restore, modify, or replace this later after ensuring pydantic v1 compatibility.
128
+ SerializedToJson = Json[T] # type: ignore[misc]
@@ -0,0 +1,73 @@
1
+ """Internal utilities for working with Pydantic types and data."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import sys
7
+ from contextlib import suppress
8
+ from typing import Any, Type
9
+
10
+ import pydantic
11
+ from pydantic import BaseModel, ValidationError
12
+ from typing_extensions import TypeAlias
13
+
14
+ PYTHON_VERSION = sys.version_info
15
+
16
+ pydantic_major, *_ = pydantic.VERSION.split(".")
17
+ IS_PYDANTIC_V2: bool = int(pydantic_major) >= 2
18
+
19
+
20
+ BaseModelType: TypeAlias = Type[BaseModel]
21
+
22
+
23
+ if IS_PYDANTIC_V2:
24
+ import pydantic_core # pydantic_core is only installed by pydantic v2
25
+
26
+ def from_json(s: str) -> Any:
27
+ """Quickly deserialize a JSON string to a Python object."""
28
+ return pydantic_core.from_json(s)
29
+
30
+ def to_json(v: Any) -> str:
31
+ """Quickly serialize a (possibly Pydantic) object to a JSON string."""
32
+ return pydantic_core.to_json(v, by_alias=True, round_trip=True).decode("utf-8")
33
+
34
+ def pydantic_isinstance(
35
+ v: Any, classinfo: BaseModelType | tuple[BaseModelType, ...]
36
+ ) -> bool:
37
+ """Return True if the object could be parsed into the given Pydantic type.
38
+
39
+ This is like a more lenient version of `isinstance()` for use with Pydantic.
40
+ In Pydantic v2, should be fast since the underlying implementation is in Rust,
41
+ and it may be preferable over `try:...except ValidationError:...`.
42
+
43
+ See: https://docs.pydantic.dev/latest/api/pydantic_core/#pydantic_core.SchemaValidator.isinstance_python
44
+ """
45
+ if isinstance(classinfo, tuple):
46
+ return any(
47
+ cls.__pydantic_validator__.isinstance_python(v) for cls in classinfo
48
+ )
49
+ cls = classinfo
50
+ return cls.__pydantic_validator__.isinstance_python(v)
51
+
52
+ else:
53
+ # Pydantic v1 fallback implementations.
54
+ # These may be noticeably slower, but their primary goal is to ensure
55
+ # compatibility with Pydantic v1 so long as we need to support it.
56
+
57
+ from pydantic.json import pydantic_encoder # Only valid in pydantic v1
58
+
59
+ def from_json(s: str) -> Any:
60
+ return json.loads(s)
61
+
62
+ def to_json(v: Any) -> str:
63
+ return json.dumps(v, default=pydantic_encoder)
64
+
65
+ def pydantic_isinstance(
66
+ v: Any, classinfo: BaseModelType | tuple[BaseModelType, ...]
67
+ ) -> bool:
68
+ classes = classinfo if isinstance(classinfo, tuple) else (classinfo,)
69
+ for cls in classes:
70
+ with suppress(ValidationError):
71
+ cls.model_validate(v)
72
+ return True
73
+ return False
@@ -2,26 +2,29 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- import sys
6
- from importlib.metadata import version
7
- from typing import (
8
- TYPE_CHECKING,
9
- Any,
10
- Callable,
11
- ClassVar,
12
- Literal,
13
- Mapping,
14
- TypeVar,
15
- overload,
16
- )
5
+ import json
6
+ from functools import lru_cache
7
+ from inspect import signature
8
+ from operator import attrgetter
9
+ from typing import TYPE_CHECKING, Any, Callable, ClassVar, Literal, overload
17
10
 
18
11
  import pydantic
19
- from typing_extensions import ParamSpec
12
+
13
+ from .utils import IS_PYDANTIC_V2, to_json
20
14
 
21
15
  if TYPE_CHECKING:
22
16
  from typing import Protocol
23
17
 
24
18
  class V1Model(Protocol):
19
+ # ------------------------------------------------------------------------------
20
+ # NOTE: These aren't part of the original v1 BaseModel spec, but were added as
21
+ # internal helpers and are (re-)declared here to satisfy mypy checks.
22
+ @classmethod
23
+ def _dump_json_vals(cls, values: dict, by_alias: bool) -> dict: ...
24
+
25
+ # ------------------------------------------------------------------------------
26
+ # These methods are part of the original v1 BaseModel spec.
27
+
25
28
  __config__: ClassVar[type]
26
29
  __fields__: ClassVar[dict[str, Any]]
27
30
  __fields_set__: set[str]
@@ -34,20 +37,10 @@ if TYPE_CHECKING:
34
37
  def parse_obj(cls, *args: Any, **kwargs: Any) -> V1Model: ...
35
38
  @classmethod
36
39
  def parse_raw(cls, *args: Any, **kwargs: Any) -> V1Model: ...
37
- def dict(self, *args: Any, **kwargs: Any) -> dict[str, Any]: ...
38
- def json(self, *args: Any, **kwargs: Any) -> str: ...
39
- def copy(self, *args: Any, **kwargs: Any) -> V1Model: ...
40
-
41
40
 
42
- PYTHON_VERSION = sys.version_info
43
-
44
- pydantic_major_version, *_ = version(pydantic.__name__).split(".")
45
- IS_PYDANTIC_V2: bool = int(pydantic_major_version) >= 2
46
-
47
-
48
- ModelT = TypeVar("ModelT")
49
- RT = TypeVar("RT")
50
- P = ParamSpec("P")
41
+ def dict(self, **kwargs: Any) -> dict[str, Any]: ...
42
+ def json(self, **kwargs: Any) -> str: ...
43
+ def copy(self, **kwargs: Any) -> V1Model: ...
51
44
 
52
45
 
53
46
  # Maps {v2 -> v1} model config keys that were renamed in v2.
@@ -66,11 +59,17 @@ _V1_CONFIG_KEYS = {
66
59
  }
67
60
 
68
61
 
69
- def _convert_v2_config(v2_config: dict[str, Any]) -> dict[str, Any]:
70
- """Return a copy of the v2 ConfigDict with renamed v1 keys."""
62
+ def convert_v2_config(v2_config: dict[str, Any]) -> dict[str, Any]:
63
+ """Internal helper: Return a copy of the v2 ConfigDict with renamed v1 keys."""
71
64
  return {_V1_CONFIG_KEYS.get(k, k): v for k, v in v2_config.items()}
72
65
 
73
66
 
67
+ @lru_cache(maxsize=None) # Reduce repeat introspection via `signature()`
68
+ def allowed_arg_names(func: Callable) -> set[str]:
69
+ """Internal helper: Return the names of args accepted by the given function."""
70
+ return set(signature(func).parameters)
71
+
72
+
74
73
  # Pydantic BaseModels are defined with a custom metaclass, but its namespace
75
74
  # has changed between pydantic versions.
76
75
  #
@@ -87,28 +86,22 @@ class V1MixinMetaclass(PydanticModelMetaclass):
87
86
  namespace: dict[str, Any],
88
87
  **kwargs: Any,
89
88
  ):
90
- # Converts a `model_config` dict in a V2 class definition, e.g.:
91
- #
92
- # class MyModel(BaseModel):
89
+ # In the class definition, convert the model config, if any:
90
+ # # BEFORE
91
+ # class MyModel(BaseModel): # v2 model with `ConfigDict`
93
92
  # model_config = ConfigDict(populate_by_name=True)
94
93
  #
95
- # ...to a `Config` class in a V1 class definition, e.g.:
96
- #
97
- # class MyModel(BaseModel):
94
+ # # AFTER
95
+ # class MyModel(BaseModel): # v1 model with inner `Config` class
98
96
  # class Config:
99
97
  # allow_population_by_field_name = True
100
- #
101
98
  if config_dict := namespace.pop("model_config", None):
102
- namespace["Config"] = type("Config", (), _convert_v2_config(config_dict))
99
+ namespace["Config"] = type("Config", (), convert_v2_config(config_dict))
103
100
  return super().__new__(cls, name, bases, namespace, **kwargs)
104
101
 
105
- # note: workarounds to patch "class properties" aren't consistent between python
106
- # versions, so this will have to do until changes are needed.
107
- if not ((3, 9) <= PYTHON_VERSION < (3, 13)):
108
-
109
- @property
110
- def model_fields(self) -> dict[str, Any]:
111
- return self.__fields__
102
+ @property
103
+ def model_fields(self) -> dict[str, Any]:
104
+ return self.__fields__ # type: ignore[deprecated]
112
105
 
113
106
 
114
107
  # Mixin to ensure compatibility of Pydantic models if Pydantic v1 is detected.
@@ -118,6 +111,23 @@ class V1MixinMetaclass(PydanticModelMetaclass):
118
111
  # Whenever possible, users should strongly prefer upgrading to Pydantic v2 to
119
112
  # ensure full compatibility.
120
113
  class V1Mixin(metaclass=V1MixinMetaclass):
114
+ # Internal compat helpers
115
+ @classmethod
116
+ def _dump_json_vals(cls, values: dict[str, Any], by_alias: bool) -> dict[str, Any]:
117
+ """Reserialize values from `Json`-typed fields after dumping the model to dict."""
118
+ # Get the expected keys (after `.model_dump()`) for `Json`-typed fields.
119
+ # Note: In v1, `Json` fields have `ModelField.parse_json == True`
120
+ json_fields = (f for f in cls.__fields__.values() if f.parse_json) # type: ignore[deprecated]
121
+ get_key = attrgetter("alias" if by_alias else "name")
122
+ json_field_keys = set(map(get_key, json_fields))
123
+
124
+ return {
125
+ # Only serialize `Json` fields with non-null values.
126
+ k: to_json(v) if ((v is not None) and (k in json_field_keys)) else v
127
+ for k, v in values.items()
128
+ }
129
+
130
+ # ------------------------------------------------------------------------------
121
131
  @classmethod
122
132
  def __try_update_forward_refs__(cls: type[V1Model], **localns: Any) -> None:
123
133
  if hasattr(sup := super(), "__try_update_forward_refs__"):
@@ -139,23 +149,35 @@ class V1Mixin(metaclass=V1MixinMetaclass):
139
149
  def model_validate_json(cls, *args: Any, **kwargs: Any) -> V1Model:
140
150
  return cls.parse_raw(*args, **kwargs)
141
151
 
142
- def model_dump(self: V1Model, *args: Any, **kwargs: Any) -> dict[str, Any]:
143
- return self.dict(*args, **kwargs)
152
+ def model_dump(self: V1Model, **kwargs: Any) -> dict[str, Any]:
153
+ # Pass only kwargs that are allowed in the V1 method.
154
+ allowed_keys = allowed_arg_names(self.dict) & kwargs.keys()
155
+ dict_ = self.dict(**{k: kwargs[k] for k in allowed_keys})
156
+
157
+ # Ugly hack: Try to serialize `Json` fields correctly when `round_trip=True` in pydantic v1
158
+ if kwargs.get("round_trip", False):
159
+ by_alias: bool = kwargs.get("by_alias", False)
160
+ return self._dump_json_vals(dict_, by_alias=by_alias)
161
+
162
+ return dict_
144
163
 
145
- def model_dump_json(self: V1Model, *args: Any, **kwargs: Any) -> str:
146
- return self.json(*args, **kwargs)
164
+ def model_dump_json(self: V1Model, **kwargs: Any) -> str:
165
+ # Pass only kwargs that are allowed in the V1 method.
166
+ allowed_keys = allowed_arg_names(self.json) & kwargs.keys()
167
+ json_ = self.json(**{k: kwargs[k] for k in allowed_keys})
147
168
 
148
- def model_copy(self: V1Model, *args: Any, **kwargs: Any) -> V1Model:
149
- return self.copy(*args, **kwargs)
169
+ # Ugly hack: Try to serialize `Json` fields correctly when `round_trip=True` in pydantic v1
170
+ if kwargs.get("round_trip", False):
171
+ by_alias: bool = kwargs.get("by_alias", False)
172
+ dict_ = json.loads(json_)
173
+ return json.dumps(self._dump_json_vals(dict_, by_alias=by_alias))
150
174
 
151
- # workarounds to patch "class properties" aren't consistent between python
152
- # versions, so this will have to do until changes are needed.
153
- if (3, 9) <= PYTHON_VERSION < (3, 13):
175
+ return json_
154
176
 
155
- @classmethod # type: ignore[misc]
156
- @property
157
- def model_fields(cls: type[V1Model]) -> Mapping[str, Any]:
158
- return cls.__fields__
177
+ def model_copy(self: V1Model, **kwargs: Any) -> V1Model:
178
+ # Pass only kwargs that are allowed in the V1 method.
179
+ allowed_keys = allowed_arg_names(self.copy) & kwargs.keys()
180
+ return self.copy(**{k: kwargs[k] for k in allowed_keys})
159
181
 
160
182
  @property
161
183
  def model_fields_set(self: V1Model) -> set[str]:
@@ -191,7 +213,7 @@ else:
191
213
  check_fields: bool | None = None,
192
214
  **_: Any,
193
215
  ) -> Callable:
194
- return pydantic.validator(
216
+ return pydantic.validator( # type: ignore[deprecated]
195
217
  field,
196
218
  *fields,
197
219
  pre=(mode == "before"),
@@ -1,7 +1,5 @@
1
1
  from wandb.apis.public.api import Api, RetryingClient, requests
2
2
  from wandb.apis.public.artifacts import (
3
- ARTIFACT_FILES_FRAGMENT,
4
- ARTIFACTS_TYPES_FRAGMENT,
5
3
  ArtifactCollection,
6
4
  ArtifactCollections,
7
5
  ArtifactFiles,
@@ -10,8 +8,10 @@ from wandb.apis.public.artifacts import (
10
8
  ArtifactTypes,
11
9
  RunArtifacts,
12
10
  )
11
+ from wandb.apis.public.automations import Automations
13
12
  from wandb.apis.public.files import FILE_FRAGMENT, File, Files
14
13
  from wandb.apis.public.history import HistoryScan, SampledHistoryScan
14
+ from wandb.apis.public.integrations import SlackIntegrations, WebhookIntegrations
15
15
  from wandb.apis.public.jobs import (
16
16
  Job,
17
17
  QueuedRun,