1919
2020"""Package class."""
2121
22+ from dataclasses import dataclass
2223from pathlib import Path
24+ from typing import TYPE_CHECKING
2325
24- from rubisco .kernel .project_config import (
26+ from rubisco .shared .api .exception import (
27+ RUNotRubiscoProjectError ,
28+ RUTypeError ,
29+ RUValueError ,
30+ )
31+ from rubisco .shared .api .git import git_clone
32+ from rubisco .shared .api .kernel import (
2533 ProjectConfigration ,
2634 load_project_config ,
2735)
28- from rubisco .lib .exceptions import RUNotRubiscoProjectError
29- from rubisco .lib .variable .fast_format_str import fast_format_str
30- from rubisco .lib .variable .utils import make_pretty
36+ from rubisco .shared .api .l10n import _
37+ from rubisco .shared .api .variable import fast_format_str
38+
39+ if TYPE_CHECKING :
40+ from rubisco .lib .variable .autoformatdict import AutoFormatDict
41+
42+
43+ @dataclass
44+ class SubpackageReference :
45+ """Subpackage reference."""
46+
47+ name : str
48+ branch : str
49+ url : str
50+ paths : list [Path ]
51+ # We support multiple paths. If one of its path exists, we will use it.
52+
53+ def exists (self ) -> bool :
54+ """Check if the subpackage exists.
55+
56+ Returns:
57+ bool: True if the subpackage exists.
58+
59+ """
60+ return any (path .exists () for path in self .paths )
61+
62+ def get_path (self ) -> Path :
63+ """Get the path of the subpackage.
64+
65+ Returns:
66+ Path: The path of the subpackage.
67+
68+ """
69+ for path in self .paths :
70+ if path .exists ():
71+ return path
72+ return self .paths [0 ]
73+
74+ def fetch (
75+ self ,
76+ protocol : str ,
77+ * ,
78+ shallow : bool = True ,
79+ use_direct : bool = False ,
80+ ) -> "Package | None" :
81+ """Fetch the subpackage.
82+
83+ Args:
84+ protocol (str): The protocol to use.
85+ shallow (bool, optional): Whether to use shallow clone. Defaults to
86+ True.
87+ use_direct (bool, optional): Whether to use direct url without
88+ mirror speed test. Defaults to False.
89+
90+ Returns:
91+ Package | None: The fetched package.
92+
93+ """
94+ path = self .get_path ()
95+ git_clone (
96+ self .url ,
97+ path ,
98+ branch = self .branch ,
99+ protocol = protocol ,
100+ shallow = shallow ,
101+ use_fastest = not use_direct ,
102+ )
103+ try :
104+ pkg = Package (path )
105+ pkg .fetch (
106+ protocol = protocol ,
107+ shallow = shallow ,
108+ use_direct = use_direct ,
109+ )
110+ except RUNotRubiscoProjectError :
111+ return None
112+ return pkg
31113
32114
33115class Package :
@@ -36,7 +118,8 @@ class Package:
36118 name : str
37119 path : Path
38120 config : ProjectConfigration
39- subpackages : list ["Package" ]
121+ subpackage_refs : list [SubpackageReference ]
122+ subpackages : list ["Package | None" ]
40123
41124 def __init__ (self , path : Path ) -> None :
42125 """Parse a rubisco package.
@@ -45,19 +128,73 @@ def __init__(self, path: Path) -> None:
45128 path (Path): The path of the package.
46129
47130 """
48- try :
49- self .config = load_project_config (path )
50- except RUNotRubiscoProjectError as exc :
51- raise RUNotRubiscoProjectError (
52- fast_format_str (
53- _ (
54- "Working directory '[underline]${{path}}[/underline]'"
55- " not a rubisco project." ,
131+ config = load_project_config (path )
132+ self .name = config .name
133+ self .path = path
134+ self .config = config
135+ self ._load_subpkgs ()
136+
137+ def _load_subpkgs (self ) -> None :
138+ subpkgs : list [SubpackageReference ] = []
139+ subpkg_dict = self .config .config .get ("subpackages" , [], valtype = dict )
140+ subpkg_dict : AutoFormatDict
141+ for name , subpkg in subpkg_dict .items ():
142+ if not isinstance (subpkg , dict ): # type: ignore[union-attr]
143+ msg = fast_format_str (
144+ _ ("Subpackage reference ${{name}} is not a dict." ),
145+ fmt = {"name" : name },
146+ )
147+ raise RUTypeError (msg )
148+ subpkg : AutoFormatDict
149+ branch = subpkg .get ("branch" , valtype = str )
150+ url = subpkg .get ("url" , valtype = str )
151+ paths = subpkg .get ("path" , valtype = list [str ] | str )
152+ if isinstance (paths , str ):
153+ paths = [paths ]
154+ if not paths :
155+ raise RUValueError (
156+ fast_format_str (
157+ _ ("Subpackage reference ${{name}} has no path." ),
158+ fmt = {"name" : name },
56159 ),
57- fmt = {"path" : make_pretty (Path .cwd ().absolute ())},
58- ),
59- hint = fast_format_str (
60- _ ("'[underline]${{path}}[/underline]' is not found." ),
61- fmt = {"path" : make_pretty (USER_REPO_CONFIG .absolute ())},
62- ),
63- ) from exc
160+ )
161+ spr = SubpackageReference (
162+ name = name ,
163+ branch = branch ,
164+ url = url ,
165+ paths = [self .path / Path (p ) for p in paths ],
166+ )
167+ subpkgs .append (spr )
168+
169+ self .subpackage_refs = subpkgs
170+
171+ def fetch (
172+ self ,
173+ protocol : str ,
174+ * ,
175+ shallow : bool = True ,
176+ use_direct : bool = False ,
177+ ) -> list ["Package | None" ]:
178+ """Fetch the package.
179+
180+ Args:
181+ protocol (str): The protocol to use.
182+ shallow (bool, optional): Whether to use shallow clone. Defaults to
183+ True.
184+ use_direct (bool, optional): Whether to use direct url without
185+ mirror speed test. Defaults to False.
186+
187+ Returns:
188+ list[Package | None]: The fetched packages.
189+
190+ """
191+ subpkgs = [
192+ subpkg .fetch (
193+ protocol ,
194+ shallow = shallow ,
195+ use_direct = use_direct ,
196+ )
197+ for subpkg in self .subpackage_refs
198+ ]
199+ self .subpackages = subpkgs
200+ return subpkgs
0 commit comments