@@ -35,18 +35,30 @@ async def install(
3535
3636@dataclass
3737class RequirementsContext :
38- """Track state while parsing requirements files."""
38+ """Track state while parsing requirements files.
3939
40- index_url : Optional [str ] = None
40+ This class maintains state about requirements and their associated index URLs.
41+ Multiple index URLs can be tracked to support searching in multiple indices
42+ in order of specification.
43+ """
44+
45+ index_urls : List [str ] = None
4146 requirements : List [str ] = None
4247
4348 def __post_init__ (self ):
4449 if self .requirements is None :
4550 self .requirements = []
51+ if self .index_urls is None :
52+ self .index_urls = []
53+
54+ def add_index_url (self , url : str ) -> None :
55+ """Add an index URL to the list of URLs to search from."""
56+ if url not in self .index_urls :
57+ self .index_urls .append (url )
4658
4759 def add_requirement (self , req : str ):
48- """Add a requirement with the currently active index URL ."""
49- self .requirements .append ((req , self .index_url ))
60+ """Add a requirement that will use the current index URLs ."""
61+ self .requirements .append ((req , self .index_urls [:] if self . index_urls else None ))
5062
5163
5264REQ_FILE_PREFIX = r"^(-r|--requirements)\s*=?\s*(.*)\s*"
@@ -141,49 +153,45 @@ async def get_action_kwargs(argv: list[str]) -> tuple[typing.Optional[str], dict
141153 action = args .action
142154
143155 if action == "install" :
156+ all_index_urls = []
157+ if args .index_url :
158+ all_index_urls .append (args .index_url )
159+
144160 all_requirements = []
145161
146162 if args .packages :
147- all_requirements .extend ((pkg , args . index_url ) for pkg in args .packages )
163+ all_requirements .extend ((pkg , all_index_urls [:] ) for pkg in args .packages )
148164
149165 # Process requirements files
150166 for req_file in args .requirements or []:
151- context = RequirementsContext ()
152-
153- if not Path (req_file ).exists ():
154- warn (f"piplite could not find requirements file { req_file } " )
155- continue
156-
157- # First let the file be processed normally to capture any index URL
158- for line_no , line in enumerate (
159- Path (req_file ).read_text (encoding = "utf-8" ).splitlines ()
160- ):
161- await _packages_from_requirements_line (
162- Path (req_file ), line_no + 1 , line , context
167+ try :
168+ requirements , file_index_urls = await _packages_from_requirements_file (
169+ Path (req_file )
163170 )
164171
165- # If CLI provided an index URL, it should override the file's index URL
166- # We update all requirements to use the CLI index URL instead. Or, we use
167- # whatever index URL was found in the file (if any).
168- if args .index_url :
169- all_requirements .extend (
170- (req , args .index_url ) for req , _ in context .requirements
171- )
172- else :
173- all_requirements .extend (context .requirements )
172+ # If CLI provided an index URL, it should override the file's index URL
173+ # We update all requirements to use the CLI index URL instead. Or, we use
174+ # whatever index URL was found in the file (if any).
175+ if args .index_url :
176+ all_requirements .extend (
177+ (req , all_index_urls ) for req , _ in requirements
178+ )
179+ else :
180+ for url in file_index_urls :
181+ if url not in all_index_urls :
182+ all_index_urls .append (url )
183+ all_requirements .extend (requirements )
184+ except Exception as e :
185+ warn (f"Error processing requirements file { req_file } : { e } " )
186+ continue
174187
175188 if all_requirements :
176189 kwargs ["requirements" ] = []
177- active_index_url = None
190+ kwargs [ "requirements" ]. extend ( req for req , _ in all_requirements )
178191
179- for req , idx in all_requirements :
180- if idx is not None :
181- active_index_url = idx
182- kwargs ["requirements" ].append (req )
183-
184- # Set the final index URL, if we found one
185- if active_index_url is not None :
186- kwargs ["index_urls" ] = active_index_url
192+ # Set the final index URLs, if we found any
193+ if all_index_urls :
194+ kwargs ["index_urls" ] = all_index_urls
187195
188196 if args .pre :
189197 kwargs ["pre" ] = True
@@ -199,22 +207,29 @@ async def get_action_kwargs(argv: list[str]) -> tuple[typing.Optional[str], dict
199207
200208async def _packages_from_requirements_file (
201209 req_path : Path ,
202- ) -> Tuple [List [str ], Optional [str ]]:
203- """Extract (potentially nested) package requirements from a requirements file.
210+ ) -> Tuple [List [Tuple [str , Optional [List [str ]]]], List [str ]]:
211+ """Extract package requirements and index URLs from a requirements file.
212+
213+ This function processes a requirements file to collect both package requirements
214+ and any index URLs specified in it (with support for nested requirements).
204215
205216 Returns:
206- Tuple of (list of package requirements, optional index URL)
217+ A tuple of:
218+ - List of (requirement, index_urls) pairs, where index_urls is a list of URLs
219+ that should be used for this requirement
220+ - List of index URLs found in the file
207221 """
222+
208223 if not req_path .exists ():
209224 warn (f"piplite could not find requirements file { req_path } " )
210- return []
225+ return [], []
211226
212227 context = RequirementsContext ()
213228
214229 for line_no , line in enumerate (req_path .read_text (encoding = "utf-8" ).splitlines ()):
215230 await _packages_from_requirements_line (req_path , line_no + 1 , line , context )
216231
217- return context .requirements , context .index_url
232+ return context .requirements , context .index_urls
218233
219234
220235async def _packages_from_requirements_line (
@@ -237,15 +252,20 @@ async def _packages_from_requirements_line(
237252 sub_req = Path (sub_path )
238253 else :
239254 sub_req = req_path .parent / sub_path
240- sub_reqs , _ = await _packages_from_requirements_file (sub_req )
241- # Use current context's index_url for nested requirements
242- context .requirements .extend (sub_reqs )
255+ # Create a new context for the nested file to maintain its own index URLs.
256+ nested_context = RequirementsContext ()
257+ nested_context .index_urls = context .index_urls [
258+ :
259+ ] # i nherit parent's index URLs
260+ await _packages_from_requirements_file (sub_req , nested_context )
261+ # Extend our requirements with the nested ones
262+ context .requirements .extend (nested_context .requirements )
243263 return
244264
245265 # Check for index URL specification
246266 index_match = re .match (INDEX_URL_PREFIX , req )
247267 if index_match :
248- context .index_url = index_match [2 ].strip ()
268+ context .add_index_url ( index_match [2 ].strip () )
249269 return
250270
251271 if req .startswith ("-" ):
0 commit comments