Coverage for navigaattori/bind.py: 100.00%

58 statements  

« prev     ^ index     » next       coverage.py v7.4.1, created at 2024-02-04 20:46:10 +00:00

1import copy 

2import pathlib 

3from typing import Union, no_type_check 

4 

5from navigaattori import ENCODING, log 

6 

7 

8@no_type_check 

9class Binder: 

10 """Represent a list of resources to be bound in sequence after resolving all parts.""" 

11 

12 def binder_has_content(self) -> None: 

13 """Ensure we received a folder to bootstrap.""" 

14 if not self.binder_path.is_file() or not self.binder_path.stat().st_size: 

15 self.state_code = 1 

16 self.state_message = f'binder ({self.binder_path}) is no file or empty' 

17 log.error(self.state_message) 

18 

19 def load_binder(self) -> None: 

20 """Load the sequence of resource paths.""" 

21 with open(self.binder_path, 'rt', encoding=ENCODING) as handle: 

22 self.resource_sequence = [line.strip() for line in handle.readlines() if line.strip()] 

23 self.resource_count = len(self.resource_sequence) 

24 res_sin_plu = 'resource' if self.resource_count == 1 else 'resources' 

25 if not self.resource_count: 

26 self.state_code = 1 

27 self.state_message = 'empty binder?' 

28 log.error(f'binder sequence failed to load any entry from ({self.binder_path})') 

29 else: 

30 log.info(f'binder sequence loaded {self.resource_count} {res_sin_plu} from ({self.binder_path}):') 

31 for resource in self.resource_sequence: 

32 log.info(f'- {resource}') 

33 log.info(f'binder sequence successfully loaded from ({self.binder_path}):') 

34 

35 def assess_resources(self) -> None: 

36 """Inspect the sequence of resource paths (empty targets are OK).""" 

37 for resource in self.resource_sequence: 

38 if not (self.binder_base / resource).is_file(): 

39 self.state_code = 1 

40 self.state_message = f'resource ({resource}) is no file (at {self.binder_base / resource})' 

41 log.error(self.state_message) 

42 continue 

43 log.info(f'- resource ({resource}) points to file (at {self.binder_base / resource})') 

44 

45 def __init__(self, binder_path: Union[str, pathlib.Path], options: dict[str, bool]): 

46 self._options = options 

47 self.quiet: bool = self._options.get('quiet', False) 

48 self.strict: bool = self._options.get('strict', False) 

49 self.verbose: bool = self._options.get('verbose', False) 

50 self.guess: bool = self._options.get('guess', False) 

51 self.binder_path: pathlib.Path = pathlib.Path(binder_path) 

52 self.binder_base = self.binder_path.parent 

53 self.resource_sequence = [] 

54 self.resource_count = len(self.resource_sequence) 

55 self.state_code = 0 

56 self.state_message = '' 

57 

58 self.binder_has_content() 

59 

60 if not self.state_code: 

61 self.load_binder() 

62 

63 if not self.state_code: 

64 self.assess_resources() 

65 

66 if not self.state_code: 

67 log.info(f'sequence of resources of ({self.binder_path}) is valid') 

68 

69 def is_valid(self) -> bool: 

70 """Is the model valid?""" 

71 return not self.state_code 

72 

73 def code_details(self) -> tuple[int, str]: 

74 """Return an ordered pair of state code and message""" 

75 return self.state_code, self.state_message 

76 

77 @no_type_check 

78 def container(self): 

79 """Return the resource sequence.""" 

80 return copy.deepcopy(self.resource_sequence)