2323import os
2424import pathlib
2525import re
26+ from dataclasses import dataclass
2627from typing import IO , Any , Dict , List , Optional , Sequence , Tuple , Union
2728
2829import yaml
3839logger = get_logger (__name__ )
3940
4041
42+ @dataclass
43+ class ManifestEntryLocation :
44+ """Location of an entry in the manifest file."""
45+
46+ line_number : int
47+ start : int
48+ end : int
49+
50+
4151class RequestedProjectNotFoundError (RuntimeError ):
4252 """Exception if items are not found in list of possibilities."""
4353
@@ -111,8 +121,8 @@ def __init__(
111121 ) -> None :
112122 """Create the manifest."""
113123 self .__version : str = str (manifest .get ("version" , self .CURRENT_VERSION ))
114- self .__text : str = str ( text )
115- self .__path : str = str (path )
124+ self .__text : str = text if text else ""
125+ self .__path : str = str (path ) if path else ""
116126
117127 self ._remotes , default_remotes = self ._determine_remotes (
118128 manifest .get ("remotes" , [])
@@ -193,6 +203,9 @@ def from_yaml(
193203 path : Optional [Union [str , os .PathLike [str ]]] = None ,
194204 ) -> "Manifest" :
195205 """Create a manifest from a file like object."""
206+ if isinstance (text , (io .TextIOWrapper , IO )):
207+ text = text .read ()
208+
196209 loaded_yaml = Manifest ._load_yaml (text )
197210
198211 if not loaded_yaml :
@@ -203,10 +216,6 @@ def from_yaml(
203216 if not manifest :
204217 raise RuntimeError ("Missing manifest root element!" )
205218
206- if isinstance (text , (io .TextIOWrapper , IO )):
207- text .seek (0 )
208- text = text .read ()
209-
210219 return Manifest (manifest , text = text , path = path )
211220
212221 @staticmethod
@@ -313,24 +322,29 @@ def dump(self, path: str) -> None:
313322 self ._as_dict (), manifest_file , Dumper = ManifestDumper , sort_keys = False
314323 )
315324
316- def find_name_in_manifest (self , name : str ) -> Tuple [int , int , int ]:
317- """Find the location of a project name in the manifest."""
325+ def find_name_in_manifest (self , name : str ) -> ManifestEntryLocation :
326+ """Find the location of a project name in the manifest.
327+
328+ Returns:
329+ ManifestEntryLocation of the project name in the manifest.
330+
331+ Raises:
332+ FileNotFoundError: If manifest text is not available
333+ RuntimeError: If the project name is not found in the manifest
334+ """
318335 if not self .__text :
319336 raise FileNotFoundError ("No manifest text available" )
320337
321338 for line_nr , line in enumerate (self .__text .splitlines (), start = 1 ):
322- match = re .search (rf"^\s+-\s*name:\s*(?P<name>{ name } )\s*$" , line )
339+ match = re .search (rf"^\s+-\s*name:\s*(?P<name>{ re . escape ( name ) } )\s*$" , line )
323340
324341 if match :
325- return (
326- line_nr ,
327- int (match .start ("name" )) + 1 ,
328- int (match .end ("name" )),
342+ return ManifestEntryLocation (
343+ line_number = line_nr ,
344+ start = int (match .start ("name" )) + 1 ,
345+ end = int (match .end ("name" )),
329346 )
330- raise RuntimeError (
331- "An entry from the manifest was provided,"
332- " that doesn't exist in the manifest!"
333- )
347+ raise RuntimeError (f"{ name } was not found in the manifest!" )
334348
335349
336350def find_manifest () -> str :
0 commit comments