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
« 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
5from navigaattori import ENCODING, log
8@no_type_check
9class Binder:
10 """Represent a list of resources to be bound in sequence after resolving all parts."""
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)
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}):')
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})')
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 = ''
58 self.binder_has_content()
60 if not self.state_code:
61 self.load_binder()
63 if not self.state_code:
64 self.assess_resources()
66 if not self.state_code:
67 log.info(f'sequence of resources of ({self.binder_path}) is valid')
69 def is_valid(self) -> bool:
70 """Is the model valid?"""
71 return not self.state_code
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
77 @no_type_check
78 def container(self):
79 """Return the resource sequence."""
80 return copy.deepcopy(self.resource_sequence)