wandb 0.22.2__py3-none-musllinux_1_2_aarch64.whl → 0.22.3__py3-none-musllinux_1_2_aarch64.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.
- wandb/__init__.py +1 -1
 - wandb/__init__.pyi +2 -2
 - wandb/_pydantic/__init__.py +8 -1
 - wandb/_pydantic/base.py +54 -18
 - wandb/_pydantic/field_types.py +8 -3
 - wandb/_pydantic/pagination.py +46 -0
 - wandb/_pydantic/utils.py +2 -2
 - wandb/apis/public/api.py +24 -19
 - wandb/apis/public/artifacts.py +259 -270
 - wandb/apis/public/registries/_utils.py +40 -54
 - wandb/apis/public/registries/registries_search.py +70 -85
 - wandb/apis/public/registries/registry.py +173 -156
 - wandb/apis/public/runs.py +27 -6
 - wandb/apis/public/utils.py +43 -20
 - wandb/automations/_generated/create_automation.py +2 -2
 - wandb/automations/_generated/create_generic_webhook_integration.py +4 -4
 - wandb/automations/_generated/delete_automation.py +2 -2
 - wandb/automations/_generated/fragments.py +31 -52
 - wandb/automations/_generated/generic_webhook_integrations_by_entity.py +3 -3
 - wandb/automations/_generated/get_automations.py +3 -3
 - wandb/automations/_generated/get_automations_by_entity.py +3 -3
 - wandb/automations/_generated/input_types.py +9 -9
 - wandb/automations/_generated/integrations_by_entity.py +3 -3
 - wandb/automations/_generated/operations.py +6 -6
 - wandb/automations/_generated/slack_integrations_by_entity.py +3 -3
 - wandb/automations/_generated/update_automation.py +2 -2
 - wandb/automations/_utils.py +3 -3
 - wandb/automations/actions.py +3 -3
 - wandb/automations/automations.py +6 -5
 - wandb/bin/gpu_stats +0 -0
 - wandb/bin/wandb-core +0 -0
 - wandb/cli/beta.py +8 -2
 - wandb/cli/beta_leet.py +2 -1
 - wandb/cli/beta_sync.py +1 -1
 - wandb/errors/term.py +8 -8
 - wandb/jupyter.py +0 -51
 - wandb/old/settings.py +6 -6
 - wandb/proto/v3/wandb_internal_pb2.py +351 -352
 - wandb/proto/v3/wandb_server_pb2.py +38 -37
 - wandb/proto/v3/wandb_settings_pb2.py +2 -2
 - wandb/proto/v3/wandb_sync_pb2.py +19 -6
 - wandb/proto/v4/wandb_internal_pb2.py +351 -352
 - wandb/proto/v4/wandb_server_pb2.py +38 -37
 - wandb/proto/v4/wandb_settings_pb2.py +2 -2
 - wandb/proto/v4/wandb_sync_pb2.py +10 -6
 - wandb/proto/v5/wandb_internal_pb2.py +351 -352
 - wandb/proto/v5/wandb_server_pb2.py +38 -37
 - wandb/proto/v5/wandb_settings_pb2.py +2 -2
 - wandb/proto/v5/wandb_sync_pb2.py +10 -6
 - wandb/proto/v6/wandb_internal_pb2.py +351 -352
 - wandb/proto/v6/wandb_server_pb2.py +38 -37
 - wandb/proto/v6/wandb_settings_pb2.py +2 -2
 - wandb/proto/v6/wandb_sync_pb2.py +10 -6
 - wandb/sdk/artifacts/_generated/__init__.py +96 -40
 - wandb/sdk/artifacts/_generated/add_aliases.py +3 -3
 - wandb/sdk/artifacts/_generated/add_artifact_collection_tags.py +26 -0
 - wandb/sdk/artifacts/_generated/artifact_by_id.py +2 -2
 - wandb/sdk/artifacts/_generated/artifact_by_name.py +3 -3
 - wandb/sdk/artifacts/_generated/artifact_collection_membership_file_urls.py +27 -8
 - wandb/sdk/artifacts/_generated/artifact_collection_membership_files.py +27 -8
 - wandb/sdk/artifacts/_generated/artifact_created_by.py +7 -20
 - wandb/sdk/artifacts/_generated/artifact_file_urls.py +19 -6
 - wandb/sdk/artifacts/_generated/artifact_membership_by_name.py +26 -0
 - wandb/sdk/artifacts/_generated/artifact_type.py +5 -5
 - wandb/sdk/artifacts/_generated/artifact_used_by.py +8 -17
 - wandb/sdk/artifacts/_generated/artifact_version_files.py +19 -8
 - wandb/sdk/artifacts/_generated/delete_aliases.py +3 -3
 - wandb/sdk/artifacts/_generated/delete_artifact.py +4 -4
 - wandb/sdk/artifacts/_generated/delete_artifact_collection_tags.py +23 -0
 - wandb/sdk/artifacts/_generated/delete_artifact_portfolio.py +4 -4
 - wandb/sdk/artifacts/_generated/delete_artifact_sequence.py +4 -4
 - wandb/sdk/artifacts/_generated/delete_registry.py +21 -0
 - wandb/sdk/artifacts/_generated/fetch_artifact_manifest.py +8 -20
 - wandb/sdk/artifacts/_generated/fetch_linked_artifacts.py +13 -35
 - wandb/sdk/artifacts/_generated/fetch_org_info_from_entity.py +28 -0
 - wandb/sdk/artifacts/_generated/fetch_registries.py +18 -8
 - wandb/sdk/{projects → artifacts}/_generated/fetch_registry.py +4 -4
 - wandb/sdk/artifacts/_generated/fragments.py +183 -333
 - wandb/sdk/artifacts/_generated/input_types.py +133 -7
 - wandb/sdk/artifacts/_generated/link_artifact.py +5 -5
 - wandb/sdk/artifacts/_generated/operations.py +1053 -548
 - wandb/sdk/artifacts/_generated/project_artifact_collection.py +9 -77
 - wandb/sdk/artifacts/_generated/project_artifact_collections.py +21 -9
 - wandb/sdk/artifacts/_generated/project_artifact_type.py +3 -3
 - wandb/sdk/artifacts/_generated/project_artifact_types.py +19 -6
 - wandb/sdk/artifacts/_generated/project_artifacts.py +7 -8
 - wandb/sdk/artifacts/_generated/registry_collections.py +21 -9
 - wandb/sdk/artifacts/_generated/registry_versions.py +20 -9
 - wandb/sdk/artifacts/_generated/rename_registry.py +25 -0
 - wandb/sdk/artifacts/_generated/run_input_artifacts.py +5 -9
 - wandb/sdk/artifacts/_generated/run_output_artifacts.py +5 -9
 - wandb/sdk/artifacts/_generated/type_info.py +2 -2
 - wandb/sdk/artifacts/_generated/unlink_artifact.py +3 -5
 - wandb/sdk/artifacts/_generated/update_artifact.py +3 -3
 - wandb/sdk/artifacts/_generated/update_artifact_collection_type.py +28 -0
 - wandb/sdk/artifacts/_generated/update_artifact_portfolio.py +7 -16
 - wandb/sdk/artifacts/_generated/update_artifact_sequence.py +7 -16
 - wandb/sdk/artifacts/_generated/upsert_registry.py +25 -0
 - wandb/sdk/artifacts/_gqlutils.py +170 -6
 - wandb/sdk/artifacts/_models/__init__.py +9 -0
 - wandb/sdk/artifacts/_models/artifact_collection.py +109 -0
 - wandb/sdk/artifacts/_models/manifest.py +26 -0
 - wandb/sdk/artifacts/_models/pagination.py +26 -0
 - wandb/sdk/artifacts/_models/registry.py +100 -0
 - wandb/sdk/artifacts/_validators.py +45 -27
 - wandb/sdk/artifacts/artifact.py +220 -215
 - wandb/sdk/artifacts/artifact_file_cache.py +1 -1
 - wandb/sdk/artifacts/artifact_manifest.py +37 -32
 - wandb/sdk/artifacts/artifact_manifest_entry.py +80 -125
 - wandb/sdk/artifacts/artifact_manifests/artifact_manifest_v1.py +43 -61
 - wandb/sdk/artifacts/storage_handlers/gcs_handler.py +8 -6
 - wandb/sdk/data_types/image.py +2 -2
 - wandb/sdk/interface/interface.py +72 -64
 - wandb/sdk/interface/interface_queue.py +27 -18
 - wandb/sdk/interface/interface_shared.py +61 -23
 - wandb/sdk/interface/interface_sock.py +9 -5
 - wandb/sdk/internal/_generated/server_features_query.py +4 -4
 - wandb/sdk/launch/inputs/schema.py +13 -10
 - wandb/sdk/lib/apikey.py +8 -12
 - wandb/sdk/lib/asyncio_compat.py +1 -1
 - wandb/sdk/lib/asyncio_manager.py +5 -5
 - wandb/sdk/lib/console_capture.py +38 -30
 - wandb/sdk/lib/progress.py +159 -64
 - wandb/sdk/lib/retry.py +3 -2
 - wandb/sdk/lib/service/service_connection.py +2 -2
 - wandb/sdk/lib/wb_logging.py +2 -1
 - wandb/sdk/mailbox/mailbox.py +1 -1
 - wandb/sdk/wandb_init.py +10 -13
 - wandb/sdk/wandb_run.py +9 -46
 - wandb/sdk/wandb_settings.py +102 -19
 - {wandb-0.22.2.dist-info → wandb-0.22.3.dist-info}/METADATA +2 -1
 - {wandb-0.22.2.dist-info → wandb-0.22.3.dist-info}/RECORD +135 -134
 - wandb/sdk/artifacts/_generated/artifact_via_membership_by_name.py +0 -26
 - wandb/sdk/artifacts/_generated/create_artifact_collection_tag_assignments.py +0 -36
 - wandb/sdk/artifacts/_generated/delete_artifact_collection_tag_assignments.py +0 -25
 - wandb/sdk/artifacts/_generated/move_artifact_collection.py +0 -35
 - wandb/sdk/projects/_generated/__init__.py +0 -26
 - wandb/sdk/projects/_generated/delete_project.py +0 -22
 - wandb/sdk/projects/_generated/enums.py +0 -4
 - wandb/sdk/projects/_generated/fragments.py +0 -41
 - wandb/sdk/projects/_generated/input_types.py +0 -13
 - wandb/sdk/projects/_generated/operations.py +0 -88
 - wandb/sdk/projects/_generated/rename_project.py +0 -27
 - wandb/sdk/projects/_generated/upsert_registry_project.py +0 -27
 - {wandb-0.22.2.dist-info → wandb-0.22.3.dist-info}/WHEEL +0 -0
 - {wandb-0.22.2.dist-info → wandb-0.22.3.dist-info}/entry_points.txt +0 -0
 - {wandb-0.22.2.dist-info → wandb-0.22.3.dist-info}/licenses/LICENSE +0 -0
 
| 
         @@ -147,7 +147,7 @@ class ArtifactFileCache: 
     | 
|
| 
       147 
147 
     | 
    
         
             
                    if temp_size:
         
     | 
| 
       148 
148 
     | 
    
         
             
                        wandb.termwarn(
         
     | 
| 
       149 
149 
     | 
    
         
             
                            f"Cache contains {util.to_human_size(temp_size)} of temporary files. "
         
     | 
| 
       150 
     | 
    
         
            -
                            "Run `wandb artifact cleanup --remove-temp` to remove them."
         
     | 
| 
      
 150 
     | 
    
         
            +
                            "Run `wandb artifact cache cleanup --remove-temp` to remove them."
         
     | 
| 
       151 
151 
     | 
    
         
             
                        )
         
     | 
| 
       152 
152 
     | 
    
         | 
| 
       153 
153 
     | 
    
         
             
                    entries = []
         
     | 
| 
         @@ -2,75 +2,80 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            from __future__ import annotations
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
     | 
    
         
            -
            from  
     | 
| 
      
 5 
     | 
    
         
            +
            from abc import ABC, abstractmethod
         
     | 
| 
      
 6 
     | 
    
         
            +
            from typing import TYPE_CHECKING, Any, Dict
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            from pydantic import Field
         
     | 
| 
      
 9 
     | 
    
         
            +
            from typing_extensions import Annotated
         
     | 
| 
       6 
10 
     | 
    
         | 
| 
       7 
11 
     | 
    
         
             
            from wandb.sdk.internal.internal_api import Api as InternalApi
         
     | 
| 
       8 
12 
     | 
    
         
             
            from wandb.sdk.lib.hashutil import HexMD5
         
     | 
| 
       9 
13 
     | 
    
         | 
| 
      
 14 
     | 
    
         
            +
            from ._models.base_model import ArtifactsBase
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
       10 
16 
     | 
    
         
             
            if TYPE_CHECKING:
         
     | 
| 
       11 
     | 
    
         
            -
                from  
     | 
| 
       12 
     | 
    
         
            -
                from  
     | 
| 
      
 17 
     | 
    
         
            +
                from .artifact_manifest_entry import ArtifactManifestEntry
         
     | 
| 
      
 18 
     | 
    
         
            +
                from .storage_policy import StoragePolicy
         
     | 
| 
       13 
19 
     | 
    
         | 
| 
       14 
20 
     | 
    
         | 
| 
       15 
     | 
    
         
            -
            class ArtifactManifest:
         
     | 
| 
       16 
     | 
    
         
            -
                 
     | 
| 
      
 21 
     | 
    
         
            +
            class ArtifactManifest(ArtifactsBase, ABC):
         
     | 
| 
      
 22 
     | 
    
         
            +
                # Note: this can't be named "version" since it conflicts with the prior `version()` classmethod.
         
     | 
| 
      
 23 
     | 
    
         
            +
                manifest_version: Annotated[Any, Field(repr=False)]
         
     | 
| 
      
 24 
     | 
    
         
            +
                entries: Dict[str, ArtifactManifestEntry] = Field(default_factory=dict)  # noqa: UP006
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                storage_policy: Annotated[StoragePolicy, Field(exclude=True, repr=False)]
         
     | 
| 
       17 
27 
     | 
    
         | 
| 
       18 
28 
     | 
    
         
             
                @classmethod
         
     | 
| 
      
 29 
     | 
    
         
            +
                def version(cls) -> int:
         
     | 
| 
      
 30 
     | 
    
         
            +
                    return cls.model_fields["manifest_version"].default
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                @classmethod
         
     | 
| 
      
 33 
     | 
    
         
            +
                @abstractmethod
         
     | 
| 
       19 
34 
     | 
    
         
             
                def from_manifest_json(
         
     | 
| 
       20 
     | 
    
         
            -
                    cls, manifest_json: dict, api: InternalApi | None = None
         
     | 
| 
      
 35 
     | 
    
         
            +
                    cls, manifest_json: dict[str, Any], api: InternalApi | None = None
         
     | 
| 
       21 
36 
     | 
    
         
             
                ) -> ArtifactManifest:
         
     | 
| 
       22 
     | 
    
         
            -
                    if "version"  
     | 
| 
      
 37 
     | 
    
         
            +
                    if (version := manifest_json.get("version")) is None:
         
     | 
| 
       23 
38 
     | 
    
         
             
                        raise ValueError("Invalid manifest format. Must contain version field.")
         
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
       25 
40 
     | 
    
         
             
                    for sub in cls.__subclasses__():
         
     | 
| 
       26 
41 
     | 
    
         
             
                        if sub.version() == version:
         
     | 
| 
       27 
42 
     | 
    
         
             
                            return sub.from_manifest_json(manifest_json, api=api)
         
     | 
| 
       28 
43 
     | 
    
         
             
                    raise ValueError("Invalid manifest version.")
         
     | 
| 
       29 
44 
     | 
    
         | 
| 
       30 
     | 
    
         
            -
                @classmethod
         
     | 
| 
       31 
     | 
    
         
            -
                def version(cls) -> int:
         
     | 
| 
       32 
     | 
    
         
            -
                    raise NotImplementedError
         
     | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
       34 
     | 
    
         
            -
                def __init__(
         
     | 
| 
       35 
     | 
    
         
            -
                    self,
         
     | 
| 
       36 
     | 
    
         
            -
                    storage_policy: StoragePolicy,
         
     | 
| 
       37 
     | 
    
         
            -
                    entries: Mapping[str, ArtifactManifestEntry] | None = None,
         
     | 
| 
       38 
     | 
    
         
            -
                ) -> None:
         
     | 
| 
       39 
     | 
    
         
            -
                    self.storage_policy = storage_policy
         
     | 
| 
       40 
     | 
    
         
            -
                    self.entries = dict(entries) if entries else {}
         
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
45 
     | 
    
         
             
                def __len__(self) -> int:
         
     | 
| 
       43 
46 
     | 
    
         
             
                    return len(self.entries)
         
     | 
| 
       44 
47 
     | 
    
         | 
| 
       45 
     | 
    
         
            -
                 
     | 
| 
      
 48 
     | 
    
         
            +
                @abstractmethod
         
     | 
| 
      
 49 
     | 
    
         
            +
                def to_manifest_json(self) -> dict[str, Any]:
         
     | 
| 
       46 
50 
     | 
    
         
             
                    raise NotImplementedError
         
     | 
| 
       47 
51 
     | 
    
         | 
| 
      
 52 
     | 
    
         
            +
                @abstractmethod
         
     | 
| 
       48 
53 
     | 
    
         
             
                def digest(self) -> HexMD5:
         
     | 
| 
       49 
54 
     | 
    
         
             
                    raise NotImplementedError
         
     | 
| 
       50 
55 
     | 
    
         | 
| 
      
 56 
     | 
    
         
            +
                @abstractmethod
         
     | 
| 
      
 57 
     | 
    
         
            +
                def size(self) -> int:
         
     | 
| 
      
 58 
     | 
    
         
            +
                    raise NotImplementedError
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
       51 
60 
     | 
    
         
             
                def add_entry(self, entry: ArtifactManifestEntry, overwrite: bool = False) -> None:
         
     | 
| 
       52 
     | 
    
         
            -
                    path = entry.path
         
     | 
| 
       53 
61 
     | 
    
         
             
                    if (
         
     | 
| 
       54 
62 
     | 
    
         
             
                        (not overwrite)
         
     | 
| 
       55 
     | 
    
         
            -
                        and (old_entry := self.entries.get(path))
         
     | 
| 
      
 63 
     | 
    
         
            +
                        and (old_entry := self.entries.get(entry.path))
         
     | 
| 
       56 
64 
     | 
    
         
             
                        and (entry.digest != old_entry.digest)
         
     | 
| 
       57 
65 
     | 
    
         
             
                    ):
         
     | 
| 
       58 
     | 
    
         
            -
                        raise ValueError(f"Cannot add the same path twice: {path!r}")
         
     | 
| 
       59 
     | 
    
         
            -
                    self.entries[path] = entry
         
     | 
| 
      
 66 
     | 
    
         
            +
                        raise ValueError(f"Cannot add the same path twice: {entry.path!r}")
         
     | 
| 
      
 67 
     | 
    
         
            +
                    self.entries[entry.path] = entry
         
     | 
| 
       60 
68 
     | 
    
         | 
| 
       61 
69 
     | 
    
         
             
                def remove_entry(self, entry: ArtifactManifestEntry) -> None:
         
     | 
| 
       62 
70 
     | 
    
         
             
                    try:
         
     | 
| 
       63 
71 
     | 
    
         
             
                        del self.entries[entry.path]
         
     | 
| 
       64 
72 
     | 
    
         
             
                    except LookupError:
         
     | 
| 
       65 
     | 
    
         
            -
                        raise FileNotFoundError(f"Cannot remove missing entry:  
     | 
| 
      
 73 
     | 
    
         
            +
                        raise FileNotFoundError(f"Cannot remove missing entry: {entry.path!r}")
         
     | 
| 
       66 
74 
     | 
    
         | 
| 
       67 
75 
     | 
    
         
             
                def get_entry_by_path(self, path: str) -> ArtifactManifestEntry | None:
         
     | 
| 
       68 
76 
     | 
    
         
             
                    return self.entries.get(path)
         
     | 
| 
       69 
77 
     | 
    
         | 
| 
       70 
78 
     | 
    
         
             
                def get_entries_in_directory(self, directory: str) -> list[ArtifactManifestEntry]:
         
     | 
| 
       71 
     | 
    
         
            -
                     
     | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
       73 
     | 
    
         
            -
             
     | 
| 
       74 
     | 
    
         
            -
                        # entry keys (paths) use forward slash even for windows
         
     | 
| 
       75 
     | 
    
         
            -
                        if key.startswith(f"{directory}/")
         
     | 
| 
       76 
     | 
    
         
            -
                    ]
         
     | 
| 
      
 79 
     | 
    
         
            +
                    # entry keys (paths) use forward slash even for windows
         
     | 
| 
      
 80 
     | 
    
         
            +
                    dir_prefix = f"{directory}/"
         
     | 
| 
      
 81 
     | 
    
         
            +
                    return [obj for key, obj in self.entries.items() if key.startswith(dir_prefix)]
         
     | 
| 
         @@ -1,17 +1,24 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            """Artifact manifest entry."""
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
      
 3 
     | 
    
         
            +
            # Older-style type annotations required for Pydantic v1 / python 3.8 compatibility.
         
     | 
| 
      
 4 
     | 
    
         
            +
            # ruff: noqa: UP006, UP007, UP045
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
       3 
6 
     | 
    
         
             
            from __future__ import annotations
         
     | 
| 
       4 
7 
     | 
    
         | 
| 
       5 
8 
     | 
    
         
             
            import concurrent.futures
         
     | 
| 
       6 
9 
     | 
    
         
             
            import hashlib
         
     | 
| 
       7 
     | 
    
         
            -
            import json
         
     | 
| 
       8 
10 
     | 
    
         
             
            import logging
         
     | 
| 
       9 
11 
     | 
    
         
             
            import os
         
     | 
| 
       10 
12 
     | 
    
         
             
            from contextlib import suppress
         
     | 
| 
       11 
     | 
    
         
            -
            from  
     | 
| 
       12 
     | 
    
         
            -
            from typing import TYPE_CHECKING
         
     | 
| 
      
 13 
     | 
    
         
            +
            from os.path import getsize
         
     | 
| 
      
 14 
     | 
    
         
            +
            from typing import TYPE_CHECKING, Any, Dict, Final, Optional, Union
         
     | 
| 
       13 
15 
     | 
    
         
             
            from urllib.parse import urlparse
         
     | 
| 
       14 
16 
     | 
    
         | 
| 
      
 17 
     | 
    
         
            +
            from pydantic import Field, NonNegativeInt
         
     | 
| 
      
 18 
     | 
    
         
            +
            from typing_extensions import Annotated, Self
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            from wandb._pydantic import field_validator, model_validator
         
     | 
| 
      
 21 
     | 
    
         
            +
            from wandb._strutils import nameof
         
     | 
| 
       15 
22 
     | 
    
         
             
            from wandb.proto.wandb_deprecated import Deprecated
         
     | 
| 
       16 
23 
     | 
    
         
             
            from wandb.sdk.lib.deprecate import deprecate
         
     | 
| 
       17 
24 
     | 
    
         
             
            from wandb.sdk.lib.filesystem import copy_or_overwrite_changed
         
     | 
| 
         @@ -22,27 +29,18 @@ from wandb.sdk.lib.hashutil import ( 
     | 
|
| 
       22 
29 
     | 
    
         
             
                hex_to_b64_id,
         
     | 
| 
       23 
30 
     | 
    
         
             
                md5_file_b64,
         
     | 
| 
       24 
31 
     | 
    
         
             
            )
         
     | 
| 
       25 
     | 
    
         
            -
            from wandb.sdk.lib.paths import FilePathStr, LogicalPath,  
     | 
| 
      
 32 
     | 
    
         
            +
            from wandb.sdk.lib.paths import FilePathStr, LogicalPath, URIStr
         
     | 
| 
       26 
33 
     | 
    
         | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
      
 34 
     | 
    
         
            +
            from ._models.base_model import ArtifactsBase
         
     | 
| 
       28 
35 
     | 
    
         | 
| 
       29 
36 
     | 
    
         
             
            if TYPE_CHECKING:
         
     | 
| 
       30 
     | 
    
         
            -
                from  
     | 
| 
      
 37 
     | 
    
         
            +
                from .artifact import Artifact
         
     | 
| 
       31 
38 
     | 
    
         | 
| 
       32 
     | 
    
         
            -
                from wandb.sdk.artifacts.artifact import Artifact
         
     | 
| 
       33 
39 
     | 
    
         | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
     | 
    
         
            -
                    path: str
         
     | 
| 
       36 
     | 
    
         
            -
                    digest: str
         
     | 
| 
       37 
     | 
    
         
            -
                    skip_cache: bool
         
     | 
| 
       38 
     | 
    
         
            -
                    ref: str
         
     | 
| 
       39 
     | 
    
         
            -
                    birthArtifactID: str
         
     | 
| 
       40 
     | 
    
         
            -
                    size: int
         
     | 
| 
       41 
     | 
    
         
            -
                    extra: dict
         
     | 
| 
       42 
     | 
    
         
            -
                    local_path: str
         
     | 
| 
      
 40 
     | 
    
         
            +
            logger = logging.getLogger(__name__)
         
     | 
| 
       43 
41 
     | 
    
         | 
| 
       44 
42 
     | 
    
         | 
| 
       45 
     | 
    
         
            -
            _WB_ARTIFACT_SCHEME = "wandb-artifact"
         
     | 
| 
      
 43 
     | 
    
         
            +
            _WB_ARTIFACT_SCHEME: Final[str] = "wandb-artifact"
         
     | 
| 
       46 
44 
     | 
    
         | 
| 
       47 
45 
     | 
    
         | 
| 
       48 
46 
     | 
    
         
             
            def _checksum_cache_path(file_path: str) -> str:
         
     | 
| 
         @@ -87,76 +85,54 @@ def _write_cached_checksum(file_path: str, checksum: str) -> None: 
     | 
|
| 
       87 
85 
     | 
    
         
             
                    logger.debug(f"Failed to write checksum cache for {file_path!r}")
         
     | 
| 
       88 
86 
     | 
    
         | 
| 
       89 
87 
     | 
    
         | 
| 
       90 
     | 
    
         
            -
            class ArtifactManifestEntry:
         
     | 
| 
       91 
     | 
    
         
            -
                """A single entry in an artifact manifest. 
     | 
| 
      
 88 
     | 
    
         
            +
            class ArtifactManifestEntry(ArtifactsBase):
         
     | 
| 
      
 89 
     | 
    
         
            +
                """A single entry in an artifact manifest.
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                External code should avoid instantiating this class directly.
         
     | 
| 
      
 92 
     | 
    
         
            +
                """
         
     | 
| 
       92 
93 
     | 
    
         | 
| 
       93 
94 
     | 
    
         
             
                path: LogicalPath
         
     | 
| 
       94 
     | 
    
         
            -
                digest: B64MD5 | URIStr | FilePathStr | ETag
         
     | 
| 
       95 
     | 
    
         
            -
                skip_cache: bool
         
     | 
| 
       96 
     | 
    
         
            -
                ref: FilePathStr | URIStr | None
         
     | 
| 
       97 
     | 
    
         
            -
                birth_artifact_id: str | None
         
     | 
| 
       98 
     | 
    
         
            -
                size: int | None
         
     | 
| 
       99 
     | 
    
         
            -
                extra: dict
         
     | 
| 
       100 
     | 
    
         
            -
                local_path: str | None
         
     | 
| 
       101 
     | 
    
         
            -
             
     | 
| 
       102 
     | 
    
         
            -
                _parent_artifact: Artifact | None = None
         
     | 
| 
       103 
     | 
    
         
            -
                _download_url: str | None = None
         
     | 
| 
       104 
     | 
    
         
            -
             
     | 
| 
       105 
     | 
    
         
            -
                def __init__(
         
     | 
| 
       106 
     | 
    
         
            -
                    self,
         
     | 
| 
       107 
     | 
    
         
            -
                    path: StrPath,
         
     | 
| 
       108 
     | 
    
         
            -
                    digest: B64MD5 | URIStr | FilePathStr | ETag,
         
     | 
| 
       109 
     | 
    
         
            -
                    skip_cache: bool | None = False,
         
     | 
| 
       110 
     | 
    
         
            -
                    ref: FilePathStr | URIStr | None = None,
         
     | 
| 
       111 
     | 
    
         
            -
                    birth_artifact_id: str | None = None,
         
     | 
| 
       112 
     | 
    
         
            -
                    size: int | None = None,
         
     | 
| 
       113 
     | 
    
         
            -
                    extra: dict | None = None,
         
     | 
| 
       114 
     | 
    
         
            -
                    local_path: StrPath | None = None,
         
     | 
| 
       115 
     | 
    
         
            -
                ) -> None:
         
     | 
| 
       116 
     | 
    
         
            -
                    self.path = LogicalPath(path)
         
     | 
| 
       117 
     | 
    
         
            -
                    self.digest = digest
         
     | 
| 
       118 
     | 
    
         
            -
                    self.ref = ref
         
     | 
| 
       119 
     | 
    
         
            -
                    self.birth_artifact_id = birth_artifact_id
         
     | 
| 
       120 
     | 
    
         
            -
                    self.size = size
         
     | 
| 
       121 
     | 
    
         
            -
                    self.extra = extra or {}
         
     | 
| 
       122 
     | 
    
         
            -
                    self.local_path = str(local_path) if local_path else None
         
     | 
| 
       123 
     | 
    
         
            -
                    if self.local_path and self.size is None:
         
     | 
| 
       124 
     | 
    
         
            -
                        self.size = Path(self.local_path).stat().st_size
         
     | 
| 
       125 
     | 
    
         
            -
                    self.skip_cache = skip_cache or False
         
     | 
| 
       126 
95 
     | 
    
         | 
| 
       127 
     | 
    
         
            -
                 
     | 
| 
       128 
     | 
    
         
            -
             
     | 
| 
       129 
     | 
    
         
            -
             
     | 
| 
       130 
     | 
    
         
            -
             
     | 
| 
       131 
     | 
    
         
            -
             
     | 
| 
       132 
     | 
    
         
            -
             
     | 
| 
       133 
     | 
    
         
            -
             
     | 
| 
       134 
     | 
    
         
            -
             
     | 
| 
       135 
     | 
    
         
            -
             
     | 
| 
       136 
     | 
    
         
            -
             
     | 
| 
       137 
     | 
    
         
            -
             
     | 
| 
       138 
     | 
    
         
            -
             
     | 
| 
       139 
     | 
    
         
            -
             
     | 
| 
       140 
     | 
    
         
            -
             
     | 
| 
       141 
     | 
    
         
            -
             
     | 
| 
       142 
     | 
    
         
            -
             
     | 
| 
       143 
     | 
    
         
            -
             
     | 
| 
       144 
     | 
    
         
            -
             
     | 
| 
       145 
     | 
    
         
            -
                     
     | 
| 
       146 
     | 
    
         
            -
                     
     | 
| 
      
 96 
     | 
    
         
            +
                digest: Union[B64MD5, ETag, URIStr, FilePathStr]
         
     | 
| 
      
 97 
     | 
    
         
            +
                ref: Union[URIStr, FilePathStr, None] = None
         
     | 
| 
      
 98 
     | 
    
         
            +
                birth_artifact_id: Annotated[Optional[str], Field(alias="birthArtifactID")] = None
         
     | 
| 
      
 99 
     | 
    
         
            +
                size: Optional[NonNegativeInt] = None
         
     | 
| 
      
 100 
     | 
    
         
            +
                extra: Dict[str, Any] = Field(default_factory=dict)
         
     | 
| 
      
 101 
     | 
    
         
            +
                local_path: Optional[str] = None
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                skip_cache: bool = False
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                # Note: Pydantic considers these private attributes, omitting them from validation and comparison logic.
         
     | 
| 
      
 106 
     | 
    
         
            +
                _parent_artifact: Optional[Artifact] = None
         
     | 
| 
      
 107 
     | 
    
         
            +
                _download_url: Optional[str] = None
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                @field_validator("path", mode="before")
         
     | 
| 
      
 110 
     | 
    
         
            +
                def _validate_path(cls, v: Any) -> LogicalPath:
         
     | 
| 
      
 111 
     | 
    
         
            +
                    """Coerce `path` to a LogicalPath.
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                    LogicalPath doesn't implement its own pydantic validator, and implementing one for
         
     | 
| 
      
 114 
     | 
    
         
            +
                    both pydantic V1 _and_ V2 would add too much boilerplate. Until we drop V1 support,
         
     | 
| 
      
 115 
     | 
    
         
            +
                    just coerce to LogicalPath in the field validator here.
         
     | 
| 
       147 
116 
     | 
    
         
             
                    """
         
     | 
| 
       148 
     | 
    
         
            -
                     
     | 
| 
       149 
     | 
    
         
            -
             
     | 
| 
       150 
     | 
    
         
            -
             
     | 
| 
       151 
     | 
    
         
            -
             
     | 
| 
       152 
     | 
    
         
            -
             
     | 
| 
       153 
     | 
    
         
            -
             
     | 
| 
       154 
     | 
    
         
            -
             
     | 
| 
       155 
     | 
    
         
            -
             
     | 
| 
       156 
     | 
    
         
            -
             
     | 
| 
       157 
     | 
    
         
            -
             
     | 
| 
       158 
     | 
    
         
            -
             
     | 
| 
       159 
     | 
    
         
            -
             
     | 
| 
      
 117 
     | 
    
         
            +
                    return LogicalPath(v)
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
                @field_validator("local_path", mode="before")
         
     | 
| 
      
 120 
     | 
    
         
            +
                def _validate_local_path(cls, v: Any) -> str | None:
         
     | 
| 
      
 121 
     | 
    
         
            +
                    """Coerce `local_path` to a str. Necessary if the input is a `PosixPath`."""
         
     | 
| 
      
 122 
     | 
    
         
            +
                    return str(v) if v else None
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                @model_validator(mode="after")
         
     | 
| 
      
 125 
     | 
    
         
            +
                def _infer_size_from_local_path(self) -> Self:
         
     | 
| 
      
 126 
     | 
    
         
            +
                    """If `size` isn't set, try to infer it from `local_path`."""
         
     | 
| 
      
 127 
     | 
    
         
            +
                    if (self.size is None) and self.local_path:
         
     | 
| 
      
 128 
     | 
    
         
            +
                        self.size = getsize(self.local_path)
         
     | 
| 
      
 129 
     | 
    
         
            +
                    return self
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
                def __repr__(self) -> str:
         
     | 
| 
      
 132 
     | 
    
         
            +
                    # For compatibility with prior behavior, don't display `extra` if it's empty
         
     | 
| 
      
 133 
     | 
    
         
            +
                    exclude = None if self.extra else {"extra"}
         
     | 
| 
      
 134 
     | 
    
         
            +
                    repr_dict = self.model_dump(by_alias=False, exclude_none=True, exclude=exclude)
         
     | 
| 
      
 135 
     | 
    
         
            +
                    return f"{nameof(type(self))}({', '.join(f'{k}={v!r}' for k, v in repr_dict.items())})"
         
     | 
| 
       160 
136 
     | 
    
         | 
| 
       161 
137 
     | 
    
         
             
                @property
         
     | 
| 
       162 
138 
     | 
    
         
             
                def name(self) -> LogicalPath:
         
     | 
| 
         @@ -193,16 +169,8 @@ class ArtifactManifestEntry: 
     | 
|
| 
       193 
169 
     | 
    
         
             
                        (str): The path of the downloaded artifact entry.
         
     | 
| 
       194 
170 
     | 
    
         
             
                    """
         
     | 
| 
       195 
171 
     | 
    
         
             
                    artifact = self.parent_artifact()
         
     | 
| 
       196 
     | 
    
         
            -
             
     | 
| 
       197 
     | 
    
         
            -
                     
     | 
| 
       198 
     | 
    
         
            -
                    artifact._add_download_root(root)
         
     | 
| 
       199 
     | 
    
         
            -
                    path = str(Path(self.path))
         
     | 
| 
       200 
     | 
    
         
            -
                    dest_path = os.path.join(root, path)
         
     | 
| 
       201 
     | 
    
         
            -
             
     | 
| 
       202 
     | 
    
         
            -
                    if skip_cache:
         
     | 
| 
       203 
     | 
    
         
            -
                        override_cache_path = dest_path
         
     | 
| 
       204 
     | 
    
         
            -
                    else:
         
     | 
| 
       205 
     | 
    
         
            -
                        override_cache_path = None
         
     | 
| 
      
 172 
     | 
    
         
            +
                    rootdir = artifact._add_download_root(root)
         
     | 
| 
      
 173 
     | 
    
         
            +
                    dest_path = os.path.join(rootdir, self.path)
         
     | 
| 
       206 
174 
     | 
    
         | 
| 
       207 
175 
     | 
    
         
             
                    # Skip checking the cache (and possibly downloading) if the file already exists
         
     | 
| 
       208 
176 
     | 
    
         
             
                    # and has the digest we're expecting.
         
     | 
| 
         @@ -222,26 +190,28 @@ class ArtifactManifestEntry: 
     | 
|
| 
       222 
190 
     | 
    
         
             
                        if self.digest == md5_hash:
         
     | 
| 
       223 
191 
     | 
    
         
             
                            return FilePathStr(dest_path)
         
     | 
| 
       224 
192 
     | 
    
         | 
| 
      
 193 
     | 
    
         
            +
                    # Override the target cache path IF we're skipping the cache.
         
     | 
| 
      
 194 
     | 
    
         
            +
                    # Note that `override_cache_path is None` <=> `skip_cache is False`.
         
     | 
| 
      
 195 
     | 
    
         
            +
                    override_cache_path = FilePathStr(dest_path) if skip_cache else None
         
     | 
| 
      
 196 
     | 
    
         
            +
                    storage_policy = artifact.manifest.storage_policy
         
     | 
| 
       225 
197 
     | 
    
         
             
                    if self.ref is not None:
         
     | 
| 
       226 
     | 
    
         
            -
                        cache_path =  
     | 
| 
      
 198 
     | 
    
         
            +
                        cache_path = storage_policy.load_reference(
         
     | 
| 
       227 
199 
     | 
    
         
             
                            self, local=True, dest_path=override_cache_path
         
     | 
| 
       228 
200 
     | 
    
         
             
                        )
         
     | 
| 
       229 
201 
     | 
    
         
             
                    else:
         
     | 
| 
       230 
     | 
    
         
            -
                        cache_path =  
     | 
| 
      
 202 
     | 
    
         
            +
                        cache_path = storage_policy.load_file(
         
     | 
| 
       231 
203 
     | 
    
         
             
                            artifact, self, dest_path=override_cache_path, executor=executor
         
     | 
| 
       232 
204 
     | 
    
         
             
                        )
         
     | 
| 
       233 
205 
     | 
    
         | 
| 
       234 
206 
     | 
    
         
             
                    # Determine the final path
         
     | 
| 
       235 
     | 
    
         
            -
                    final_path = (
         
     | 
| 
       236 
     | 
    
         
            -
                        dest_path
         
     | 
| 
       237 
     | 
    
         
            -
                        if skip_cache
         
     | 
| 
       238 
     | 
    
         
            -
                        else copy_or_overwrite_changed(cache_path, dest_path)
         
     | 
| 
      
 207 
     | 
    
         
            +
                    final_path = FilePathStr(
         
     | 
| 
      
 208 
     | 
    
         
            +
                        override_cache_path or copy_or_overwrite_changed(cache_path, dest_path)
         
     | 
| 
       239 
209 
     | 
    
         
             
                    )
         
     | 
| 
       240 
210 
     | 
    
         | 
| 
       241 
211 
     | 
    
         
             
                    # Cache the checksum for future downloads
         
     | 
| 
       242 
     | 
    
         
            -
                    _write_cached_checksum( 
     | 
| 
      
 212 
     | 
    
         
            +
                    _write_cached_checksum(final_path, self.digest)
         
     | 
| 
       243 
213 
     | 
    
         | 
| 
       244 
     | 
    
         
            -
                    return  
     | 
| 
      
 214 
     | 
    
         
            +
                    return final_path
         
     | 
| 
       245 
215 
     | 
    
         | 
| 
       246 
216 
     | 
    
         
             
                def ref_target(self) -> FilePathStr | URIStr:
         
     | 
| 
       247 
217 
     | 
    
         
             
                    """Get the reference URL that is targeted by this artifact entry.
         
     | 
| 
         @@ -254,11 +224,9 @@ class ArtifactManifestEntry: 
     | 
|
| 
       254 
224 
     | 
    
         
             
                    """
         
     | 
| 
       255 
225 
     | 
    
         
             
                    if self.ref is None:
         
     | 
| 
       256 
226 
     | 
    
         
             
                        raise ValueError("Only reference entries support ref_target().")
         
     | 
| 
       257 
     | 
    
         
            -
                    if self._parent_artifact is None:
         
     | 
| 
      
 227 
     | 
    
         
            +
                    if (parent_artifact := self._parent_artifact) is None:
         
     | 
| 
       258 
228 
     | 
    
         
             
                        return self.ref
         
     | 
| 
       259 
     | 
    
         
            -
                    return  
     | 
| 
       260 
     | 
    
         
            -
                        self._parent_artifact.manifest.entries[self.path], local=False
         
     | 
| 
       261 
     | 
    
         
            -
                    )
         
     | 
| 
      
 229 
     | 
    
         
            +
                    return parent_artifact.manifest.storage_policy.load_reference(self, local=False)
         
     | 
| 
       262 
230 
     | 
    
         | 
| 
       263 
231 
     | 
    
         
             
                def ref_url(self) -> str:
         
     | 
| 
       264 
232 
     | 
    
         
             
                    """Get a URL to this artifact entry.
         
     | 
| 
         @@ -279,26 +247,13 @@ class ArtifactManifestEntry: 
     | 
|
| 
       279 
247 
     | 
    
         
             
                        raise ValueError("Parent artifact is not set")
         
     | 
| 
       280 
248 
     | 
    
         
             
                    elif (parent_id := parent_artifact.id) is None:
         
     | 
| 
       281 
249 
     | 
    
         
             
                        raise ValueError("Parent artifact ID is not set")
         
     | 
| 
       282 
     | 
    
         
            -
                    return f"{_WB_ARTIFACT_SCHEME}://{b64_to_hex_id( 
     | 
| 
       283 
     | 
    
         
            -
             
     | 
| 
       284 
     | 
    
         
            -
                def to_json(self) ->  
     | 
| 
       285 
     | 
    
         
            -
                     
     | 
| 
       286 
     | 
    
         
            -
             
     | 
| 
       287 
     | 
    
         
            -
             
     | 
| 
       288 
     | 
    
         
            -
                     
     | 
| 
       289 
     | 
    
         
            -
                    if self.size is not None:
         
     | 
| 
       290 
     | 
    
         
            -
                        contents["size"] = self.size
         
     | 
| 
       291 
     | 
    
         
            -
                    if self.ref:
         
     | 
| 
       292 
     | 
    
         
            -
                        contents["ref"] = self.ref
         
     | 
| 
       293 
     | 
    
         
            -
                    if self.birth_artifact_id:
         
     | 
| 
       294 
     | 
    
         
            -
                        contents["birthArtifactID"] = self.birth_artifact_id
         
     | 
| 
       295 
     | 
    
         
            -
                    if self.local_path:
         
     | 
| 
       296 
     | 
    
         
            -
                        contents["local_path"] = self.local_path
         
     | 
| 
       297 
     | 
    
         
            -
                    if self.skip_cache:
         
     | 
| 
       298 
     | 
    
         
            -
                        contents["skip_cache"] = self.skip_cache
         
     | 
| 
       299 
     | 
    
         
            -
                    if self.extra:
         
     | 
| 
       300 
     | 
    
         
            -
                        contents["extra"] = self.extra
         
     | 
| 
       301 
     | 
    
         
            -
                    return contents
         
     | 
| 
      
 250 
     | 
    
         
            +
                    return f"{_WB_ARTIFACT_SCHEME}://{b64_to_hex_id(parent_id)}/{self.path}"
         
     | 
| 
      
 251 
     | 
    
         
            +
             
     | 
| 
      
 252 
     | 
    
         
            +
                def to_json(self) -> dict[str, Any]:
         
     | 
| 
      
 253 
     | 
    
         
            +
                    # NOTE: The method name `to_json` is a bit misleading, as this returns a
         
     | 
| 
      
 254 
     | 
    
         
            +
                    # python dict, NOT a JSON string. The historical name is kept for continuity,
         
     | 
| 
      
 255 
     | 
    
         
            +
                    # but consider deprecating this in favor of `BaseModel.model_dump()`.
         
     | 
| 
      
 256 
     | 
    
         
            +
                    return self.model_dump(exclude_none=True)  # type: ignore[return-value]
         
     | 
| 
       302 
257 
     | 
    
         | 
| 
       303 
258 
     | 
    
         
             
                def _is_artifact_reference(self) -> bool:
         
     | 
| 
       304 
259 
     | 
    
         
             
                    return self.ref is not None and urlparse(self.ref).scheme == _WB_ARTIFACT_SCHEME
         
     | 
| 
         @@ -1,61 +1,48 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            """Artifact manifest v1."""
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
      
 3 
     | 
    
         
            +
            # Older-style type annotations required for Pydantic v1 / python 3.8 compatibility.
         
     | 
| 
      
 4 
     | 
    
         
            +
            # ruff: noqa: UP006
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
       3 
6 
     | 
    
         
             
            from __future__ import annotations
         
     | 
| 
       4 
7 
     | 
    
         | 
| 
       5 
8 
     | 
    
         
             
            from operator import itemgetter
         
     | 
| 
       6 
     | 
    
         
            -
            from typing import Any,  
     | 
| 
      
 9 
     | 
    
         
            +
            from typing import Any, ClassVar, Dict, Literal, final
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            from pydantic import Field
         
     | 
| 
      
 12 
     | 
    
         
            +
            from typing_extensions import Annotated
         
     | 
| 
       7 
13 
     | 
    
         | 
| 
       8 
     | 
    
         
            -
            from wandb.sdk.artifacts.artifact_manifest import ArtifactManifest
         
     | 
| 
       9 
     | 
    
         
            -
            from wandb.sdk.artifacts.artifact_manifest_entry import ArtifactManifestEntry
         
     | 
| 
       10 
     | 
    
         
            -
            from wandb.sdk.artifacts.storage_policy import StoragePolicy
         
     | 
| 
       11 
14 
     | 
    
         
             
            from wandb.sdk.internal.internal_api import Api as InternalApi
         
     | 
| 
       12 
15 
     | 
    
         
             
            from wandb.sdk.lib.hashutil import HexMD5, _md5
         
     | 
| 
       13 
16 
     | 
    
         | 
| 
      
 17 
     | 
    
         
            +
            from .._factories import make_storage_policy
         
     | 
| 
      
 18 
     | 
    
         
            +
            from .._models.manifest import ArtifactManifestV1Data
         
     | 
| 
      
 19 
     | 
    
         
            +
            from ..artifact_manifest import ArtifactManifest
         
     | 
| 
      
 20 
     | 
    
         
            +
            from ..artifact_manifest_entry import ArtifactManifestEntry
         
     | 
| 
      
 21 
     | 
    
         
            +
            from ..storage_policy import StoragePolicy
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
       14 
23 
     | 
    
         | 
| 
      
 24 
     | 
    
         
            +
            @final
         
     | 
| 
       15 
25 
     | 
    
         
             
            class ArtifactManifestV1(ArtifactManifest):
         
     | 
| 
       16 
     | 
    
         
            -
                 
     | 
| 
       17 
     | 
    
         
            -
                 
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
      
 26 
     | 
    
         
            +
                manifest_version: Annotated[Literal[1], Field(repr=False)] = 1
         
     | 
| 
      
 27 
     | 
    
         
            +
                entries: Dict[str, ArtifactManifestEntry] = Field(default_factory=dict)
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                storage_policy: StoragePolicy = Field(
         
     | 
| 
      
 30 
     | 
    
         
            +
                    default_factory=make_storage_policy, exclude=True, repr=False
         
     | 
| 
      
 31 
     | 
    
         
            +
                )
         
     | 
| 
       19 
32 
     | 
    
         | 
| 
       20 
33 
     | 
    
         
             
                @classmethod
         
     | 
| 
       21 
34 
     | 
    
         
             
                def from_manifest_json(
         
     | 
| 
       22 
     | 
    
         
            -
                    cls, manifest_json: dict, api: InternalApi | None = None
         
     | 
| 
      
 35 
     | 
    
         
            +
                    cls, manifest_json: dict[str, Any], api: InternalApi | None = None
         
     | 
| 
       23 
36 
     | 
    
         
             
                ) -> ArtifactManifestV1:
         
     | 
| 
       24 
     | 
    
         
            -
                     
     | 
| 
       25 
     | 
    
         
            -
                        raise ValueError(
         
     | 
| 
       26 
     | 
    
         
            -
                            "Expected manifest version 1, got {}".format(manifest_json["version"])
         
     | 
| 
       27 
     | 
    
         
            -
                        )
         
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
     | 
    
         
            -
                    storage_policy_name = manifest_json["storagePolicy"]
         
     | 
| 
       30 
     | 
    
         
            -
                    storage_policy_config = manifest_json.get("storagePolicyConfig", {})
         
     | 
| 
       31 
     | 
    
         
            -
                    storage_policy_cls = StoragePolicy.lookup_by_name(storage_policy_name)
         
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
       33 
     | 
    
         
            -
                    entries: Mapping[str, ArtifactManifestEntry]
         
     | 
| 
       34 
     | 
    
         
            -
                    entries = {
         
     | 
| 
       35 
     | 
    
         
            -
                        name: ArtifactManifestEntry(
         
     | 
| 
       36 
     | 
    
         
            -
                            path=name,
         
     | 
| 
       37 
     | 
    
         
            -
                            digest=val["digest"],
         
     | 
| 
       38 
     | 
    
         
            -
                            birth_artifact_id=val.get("birthArtifactID"),
         
     | 
| 
       39 
     | 
    
         
            -
                            ref=val.get("ref"),
         
     | 
| 
       40 
     | 
    
         
            -
                            size=val.get("size"),
         
     | 
| 
       41 
     | 
    
         
            -
                            extra=val.get("extra"),
         
     | 
| 
       42 
     | 
    
         
            -
                            local_path=val.get("local_path"),
         
     | 
| 
       43 
     | 
    
         
            -
                            skip_cache=val.get("skip_cache"),
         
     | 
| 
       44 
     | 
    
         
            -
                        )
         
     | 
| 
       45 
     | 
    
         
            -
                        for name, val in manifest_json["contents"].items()
         
     | 
| 
       46 
     | 
    
         
            -
                    }
         
     | 
| 
      
 37 
     | 
    
         
            +
                    data = ArtifactManifestV1Data(**manifest_json)
         
     | 
| 
       47 
38 
     | 
    
         | 
| 
      
 39 
     | 
    
         
            +
                    policy_name = data.storage_policy
         
     | 
| 
      
 40 
     | 
    
         
            +
                    policy_cfg = data.storage_policy_config
         
     | 
| 
      
 41 
     | 
    
         
            +
                    policy = StoragePolicy.lookup_by_name(policy_name).from_config(policy_cfg, api)
         
     | 
| 
       48 
42 
     | 
    
         
             
                    return cls(
         
     | 
| 
       49 
     | 
    
         
            -
                         
     | 
| 
      
 43 
     | 
    
         
            +
                        manifest_version=data.version, entries=data.contents, storage_policy=policy
         
     | 
| 
       50 
44 
     | 
    
         
             
                    )
         
     | 
| 
       51 
45 
     | 
    
         | 
| 
       52 
     | 
    
         
            -
                def __init__(
         
     | 
| 
       53 
     | 
    
         
            -
                    self,
         
     | 
| 
       54 
     | 
    
         
            -
                    storage_policy: StoragePolicy,
         
     | 
| 
       55 
     | 
    
         
            -
                    entries: Mapping[str, ArtifactManifestEntry] | None = None,
         
     | 
| 
       56 
     | 
    
         
            -
                ) -> None:
         
     | 
| 
       57 
     | 
    
         
            -
                    super().__init__(storage_policy, entries=entries)
         
     | 
| 
       58 
     | 
    
         
            -
             
     | 
| 
       59 
46 
     | 
    
         
             
                def to_manifest_json(self) -> dict:
         
     | 
| 
       60 
47 
     | 
    
         
             
                    """This is the JSON that's stored in wandb_manifest.json.
         
     | 
| 
       61 
48 
     | 
    
         | 
| 
         @@ -64,31 +51,26 @@ class ArtifactManifestV1(ArtifactManifest): 
     | 
|
| 
       64 
51 
     | 
    
         
             
                    system. We don't need to include the local paths in the artifact manifest
         
     | 
| 
       65 
52 
     | 
    
         
             
                    contents.
         
     | 
| 
       66 
53 
     | 
    
         
             
                    """
         
     | 
| 
       67 
     | 
    
         
            -
                     
     | 
| 
       68 
     | 
    
         
            -
                    for name, entry in sorted(self.entries.items(), key=itemgetter(0)):
         
     | 
| 
       69 
     | 
    
         
            -
                        json_entry: dict[str, Any] = {
         
     | 
| 
       70 
     | 
    
         
            -
                            "digest": entry.digest,
         
     | 
| 
       71 
     | 
    
         
            -
                        }
         
     | 
| 
       72 
     | 
    
         
            -
                        if entry.birth_artifact_id:
         
     | 
| 
       73 
     | 
    
         
            -
                            json_entry["birthArtifactID"] = entry.birth_artifact_id
         
     | 
| 
       74 
     | 
    
         
            -
                        if entry.ref:
         
     | 
| 
       75 
     | 
    
         
            -
                            json_entry["ref"] = entry.ref
         
     | 
| 
       76 
     | 
    
         
            -
                        if entry.extra:
         
     | 
| 
       77 
     | 
    
         
            -
                            json_entry["extra"] = entry.extra
         
     | 
| 
       78 
     | 
    
         
            -
                        if entry.size is not None:
         
     | 
| 
       79 
     | 
    
         
            -
                            json_entry["size"] = entry.size
         
     | 
| 
       80 
     | 
    
         
            -
                        contents[name] = json_entry
         
     | 
| 
      
 54 
     | 
    
         
            +
                    omit_entry_fields = {"path", "local_path", "skip_cache"}
         
     | 
| 
       81 
55 
     | 
    
         
             
                    return {
         
     | 
| 
       82 
     | 
    
         
            -
                        "version": self. 
     | 
| 
      
 56 
     | 
    
         
            +
                        "version": self.manifest_version,
         
     | 
| 
       83 
57 
     | 
    
         
             
                        "storagePolicy": self.storage_policy.name(),
         
     | 
| 
       84 
     | 
    
         
            -
                        "storagePolicyConfig": self.storage_policy.config() 
     | 
| 
       85 
     | 
    
         
            -
                        "contents":  
     | 
| 
      
 58 
     | 
    
         
            +
                        "storagePolicyConfig": self.storage_policy.config(),
         
     | 
| 
      
 59 
     | 
    
         
            +
                        "contents": {
         
     | 
| 
      
 60 
     | 
    
         
            +
                            path: entry.model_dump(exclude=omit_entry_fields, exclude_defaults=True)
         
     | 
| 
      
 61 
     | 
    
         
            +
                            for path, entry in self.entries.items()
         
     | 
| 
      
 62 
     | 
    
         
            +
                        },
         
     | 
| 
       86 
63 
     | 
    
         
             
                    }
         
     | 
| 
       87 
64 
     | 
    
         | 
| 
      
 65 
     | 
    
         
            +
                _DIGEST_HEADER: ClassVar[bytes] = b"wandb-artifact-manifest-v1\n"
         
     | 
| 
      
 66 
     | 
    
         
            +
                """Encoded prefix/header for the ArtifactManifest digest."""
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
       88 
68 
     | 
    
         
             
                def digest(self) -> HexMD5:
         
     | 
| 
       89 
     | 
    
         
            -
                    hasher = _md5()
         
     | 
| 
       90 
     | 
    
         
            -
                    hasher.update(b"wandb-artifact-manifest-v1\n")
         
     | 
| 
      
 69 
     | 
    
         
            +
                    hasher = _md5(self._DIGEST_HEADER)
         
     | 
| 
       91 
70 
     | 
    
         
             
                    # sort by key (path)
         
     | 
| 
       92 
     | 
    
         
            -
                    for  
     | 
| 
       93 
     | 
    
         
            -
                        hasher.update(f"{ 
     | 
| 
       94 
     | 
    
         
            -
                    return  
     | 
| 
      
 71 
     | 
    
         
            +
                    for path, entry in sorted(self.entries.items(), key=itemgetter(0)):
         
     | 
| 
      
 72 
     | 
    
         
            +
                        hasher.update(f"{path}:{entry.digest}\n".encode())
         
     | 
| 
      
 73 
     | 
    
         
            +
                    return hasher.hexdigest()
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                def size(self) -> int:
         
     | 
| 
      
 76 
     | 
    
         
            +
                    return sum(entry.size for entry in self.entries.values() if entry.size)
         
     | 
| 
         @@ -79,6 +79,7 @@ class GCSHandler(StorageHandler): 
     | 
|
| 
       79 
79 
     | 
    
         
             
                    bucket, key, _ = self._parse_uri(manifest_entry.ref)
         
     | 
| 
       80 
80 
     | 
    
         
             
                    version = manifest_entry.extra.get("versionID")
         
     | 
| 
       81 
81 
     | 
    
         | 
| 
      
 82 
     | 
    
         
            +
                    # Skip downloading an entry that corresponds to a folder
         
     | 
| 
       82 
83 
     | 
    
         
             
                    if self._is_dir(manifest_entry):
         
     | 
| 
       83 
84 
     | 
    
         
             
                        raise _GCSIsADirectoryError(
         
     | 
| 
       84 
85 
     | 
    
         
             
                            f"Unable to download GCS folder {manifest_entry.ref!r}, skipping"
         
     | 
| 
         @@ -132,7 +133,8 @@ class GCSHandler(StorageHandler): 
     | 
|
| 
       132 
133 
     | 
    
         
             
                    obj = self._client.bucket(bucket).get_blob(key, generation=version)
         
     | 
| 
       133 
134 
     | 
    
         
             
                    if obj is None and version is not None:
         
     | 
| 
       134 
135 
     | 
    
         
             
                        raise ValueError(f"Object does not exist: {path}#{version}")
         
     | 
| 
       135 
     | 
    
         
            -
                     
     | 
| 
      
 136 
     | 
    
         
            +
                    # HNS buckets have blobs for directories, so we also check the blob name to see if its a directory
         
     | 
| 
      
 137 
     | 
    
         
            +
                    multi = obj is None or obj.name.endswith("/")
         
     | 
| 
       136 
138 
     | 
    
         
             
                    if multi:
         
     | 
| 
       137 
139 
     | 
    
         
             
                        start_time = time.monotonic()
         
     | 
| 
       138 
140 
     | 
    
         
             
                        termlog(
         
     | 
| 
         @@ -215,11 +217,11 @@ class GCSHandler(StorageHandler): 
     | 
|
| 
       215 
217 
     | 
    
         
             
                    assert manifest_entry.ref is not None
         
     | 
| 
       216 
218 
     | 
    
         
             
                    bucket, key, _ = self._parse_uri(manifest_entry.ref)
         
     | 
| 
       217 
219 
     | 
    
         
             
                    bucket_obj = self._client.bucket(bucket)
         
     | 
| 
       218 
     | 
    
         
            -
                    # A gcs  
     | 
| 
       219 
     | 
    
         
            -
                    # we  
     | 
| 
       220 
     | 
    
         
            -
                    #  
     | 
| 
       221 
     | 
    
         
            -
                    #  
     | 
| 
       222 
     | 
    
         
            -
                    # exists on gcloud
         
     | 
| 
      
 220 
     | 
    
         
            +
                    # A gcs folder key should end with a forward slash on gcloud, but
         
     | 
| 
      
 221 
     | 
    
         
            +
                    # we previously saved these refs without the forward slash in the manifest entry
         
     | 
| 
      
 222 
     | 
    
         
            +
                    # To check whether the entry corresponds to a folder, we check the size and extension,
         
     | 
| 
      
 223 
     | 
    
         
            +
                    # make sure there is no file with this reference, and that the ref with the slash
         
     | 
| 
      
 224 
     | 
    
         
            +
                    # exists on gcloud as a folder
         
     | 
| 
       223 
225 
     | 
    
         
             
                    return key.endswith("/") or (
         
     | 
| 
       224 
226 
     | 
    
         
             
                        not (manifest_entry.size or PurePosixPath(key).suffix)
         
     | 
| 
       225 
227 
     | 
    
         
             
                        and bucket_obj.get_blob(key) is None
         
     | 
    
        wandb/sdk/data_types/image.py
    CHANGED
    
    | 
         @@ -182,8 +182,8 @@ class Image(BatchableMedia): 
     | 
|
| 
       182 
182 
     | 
    
         
             
                            unless `normalize` is set to `False`.
         
     | 
| 
       183 
183 
     | 
    
         
             
                        - pytorch tensor should be in the format (channel, height, width)
         
     | 
| 
       184 
184 
     | 
    
         
             
                        - NumPy array should be in the format (height, width, channel)
         
     | 
| 
       185 
     | 
    
         
            -
                        mode: The PIL mode for an image. Most common are "L", "RGB",
         
     | 
| 
       186 
     | 
    
         
            -
                             
     | 
| 
      
 185 
     | 
    
         
            +
                        mode: The PIL mode for an image. Most common are "L", "RGB", "RGBA".
         
     | 
| 
      
 186 
     | 
    
         
            +
                            Full Pillow docs for more information https://pillow.readthedocs.io/en/stable/handbook/concepts.html#modes
         
     | 
| 
       187 
187 
     | 
    
         
             
                        caption: Label for display of image.
         
     | 
| 
       188 
188 
     | 
    
         
             
                        grouping: The grouping number for the image.
         
     | 
| 
       189 
189 
     | 
    
         
             
                        classes: A list of class information for the image,
         
     |