88from pydantic import BaseModel
99from rigging .filesystem import marin_prefix , open_url
1010
11+ from marin .execution .executor_step_status import STATUS_SUCCESS , get_status_path
1112from marin .execution .step_spec import StepSpec , _is_relative_path
1213
1314T = TypeVar ("T" )
@@ -22,29 +23,60 @@ def from_path(cls, base_path: str | StepSpec, artifact_type: type[T]) -> T: ...
2223
2324 @overload
2425 @classmethod
25- def from_path (cls , base_path : str | StepSpec ) -> dict [str , Any ]: ...
26+ def from_path (cls , base_path : str | StepSpec ) -> "PathMetadata | dict[str, Any]" : ...
2627
2728 @classmethod
28- def from_path (cls , base_path : str | StepSpec , artifact_type : type [T ] | None = None ) -> T | dict [str , Any ]:
29+ def from_path (
30+ cls , base_path : str | StepSpec , artifact_type : type [T ] | None = None
31+ ) -> "T | PathMetadata | dict[str, Any]" :
2932 """Load an Artifact instance from the specified output base path.
3033
3134 If ``base_path`` is a relative path (no URL scheme, doesn't start with ``/``),
3235 it is resolved against ``marin_prefix()``.
36+
37+ If ``base_path`` has no ``.artifact`` file but its ``.executor_status`` file
38+ contains ``SUCCESS``, returns a :class:`PathMetadata` pointing at ``base_path``
39+ — provided the caller asked for no specific type or for ``PathMetadata``.
3340 """
3441
3542 if isinstance (base_path , StepSpec ):
3643 base_path = base_path .output_path
3744 elif _is_relative_path (base_path ):
3845 base_path = f"{ marin_prefix ()} /{ base_path } "
3946
40- with open_url (f"{ base_path } /{ cls .__artifact_file_name } " , "rb" ) as fd :
41- if artifact_type is None :
42- return json .load (fd )
43- if issubclass (artifact_type , BaseModel ):
44- return artifact_type .model_validate_json (fd .read ())
45- if is_dataclass (artifact_type ):
46- return artifact_type (** json .load (fd )) # type: ignore[not-callable]
47- raise ValueError (f"Unsupported artifact type: { artifact_type !r} " )
47+ try :
48+ with open_url (f"{ base_path } /{ cls .__artifact_file_name } " , "rb" ) as fd :
49+ if artifact_type is None :
50+ return json .load (fd )
51+ if issubclass (artifact_type , BaseModel ):
52+ return artifact_type .model_validate_json (fd .read ())
53+ if is_dataclass (artifact_type ):
54+ return artifact_type (** json .load (fd )) # type: ignore[not-callable]
55+ raise ValueError (f"Unsupported artifact type: { artifact_type !r} " )
56+ except FileNotFoundError :
57+ return cls ._from_executor_status (base_path , artifact_type )
58+
59+ @classmethod
60+ def _from_executor_status (cls , base_path : str , artifact_type : type [T ] | None ) -> "T | PathMetadata" :
61+ """Fallback when ``.artifact`` is absent: synthesize a :class:`PathMetadata`
62+ if the step published ``.executor_status = SUCCESS``.
63+
64+ Only valid when the caller wants no type or ``PathMetadata`` — other types
65+ cannot be reconstructed from a bare path.
66+ """
67+ if artifact_type is not None and artifact_type is not PathMetadata :
68+ raise FileNotFoundError (
69+ f"No { cls .__artifact_file_name } at { base_path } ; cannot synthesize "
70+ f"{ artifact_type !r} from { get_status_path (base_path )!r} "
71+ )
72+ with open_url (get_status_path (base_path ), "r" ) as fd :
73+ status = fd .read ().strip ()
74+ if status != STATUS_SUCCESS :
75+ raise FileNotFoundError (
76+ f"No { cls .__artifact_file_name } at { base_path } and "
77+ f"{ get_status_path (base_path )!r} is { status !r} (not { STATUS_SUCCESS !r} )"
78+ )
79+ return PathMetadata (path = base_path )
4880
4981 @classmethod
5082 def save (cls , artifact : T , base_path : str ) -> None :
@@ -61,9 +93,12 @@ def save(cls, artifact: T, base_path: str) -> None:
6193 fd .write (json .dumps (artifact ).encode ("utf-8" ))
6294
6395
64- @dataclass
65- class PathMetadata :
66- """Represents a single output path"""
96+ class PathMetadata (BaseModel ):
97+ """Represents a single output path.
98+
99+ Also used as the synthetic return type of :meth:`Artifact.from_path` when the
100+ step published a ``.executor_status = SUCCESS`` marker but no ``.artifact``.
101+ """
67102
68103 path : str
69104
0 commit comments