Coverage for .nox/test-3-9/lib/python3.9/site-packages/nskit/mixer/components/folder.py: 96%

77 statements  

« prev     ^ index     » next       coverage.py v7.4.2, created at 2024-02-25 17:38 +0000

1"""Folder component.""" 

2from pathlib import Path 

3from typing import Any, Dict, List, Optional, Union 

4 

5from pydantic import Field, field_validator 

6 

7from .file import File 

8from .filesystem_object import FileSystemObject 

9 

10 

11class Folder(FileSystemObject): 

12 """Folder component.""" 

13 

14 contents: List[Union[File, 'Folder']] = Field(default_factory=list, description='The folder contents') 

15 

16 def write(self, base_path: Path, context: Dict[str, Any], override_path: Optional[Path] = None): 

17 """Write the rendered content to the appropriate path within the ``base_path``.""" 

18 folder_path = self.get_path(base_path, context, override_path) 

19 folder_path.mkdir(exist_ok=True, parents=True) 

20 contents_dict = {} 

21 for obj in self.contents: 

22 contents_dict.update(obj.write(folder_path, context)) 

23 return {folder_path: contents_dict} 

24 

25 def dryrun(self, base_path: Path, context: Dict[str, Any], override_path: Optional[Path] = None): 

26 """Preview the file contents using the context.""" 

27 folder_path = self.get_path(base_path, context, override_path) 

28 contents_dict = {} 

29 for u in self.contents: 

30 contents_dict.update(u.dryrun(folder_path, context)) 

31 result = {folder_path: contents_dict} 

32 return result 

33 

34 def validate(self, base_path: Path, context: Dict[str, Any], override_path: Optional[Path] = None): 

35 """Validate the output against expected.""" 

36 missing = [] 

37 errors = [] 

38 ok = [] 

39 path = self.get_path(base_path, context, override_path) 

40 if not path.exists(): 

41 missing.append(path) 

42 for child in self.contents: 

43 child_missing, child_errors, child_ok = child.validate(path, context) 

44 missing += child_missing 

45 errors += child_errors 

46 ok += child_ok 

47 if not missing and not errors: 

48 ok.append(path) 

49 return missing, errors, ok 

50 

51 @field_validator('contents', mode='before') 

52 @classmethod 

53 def _validate_contents_ids_unique(cls, contents): 

54 if contents: 

55 ids_ = [] 

56 for item in contents: 

57 id_ = None 

58 if isinstance(item, FileSystemObject): 

59 id_ = item.id_ 

60 if isinstance(item, dict): 

61 id_ = item.get('id_', None) 

62 if id_ is None: 

63 # No id_ provided 

64 continue 

65 if id_ in ids_: 

66 raise ValueError(f'IDs for contents must be unique. The ID({id_}) already exists in the folder contents') 

67 ids_.append(id_) 

68 return contents 

69 

70 def index(self, name_or_id): 

71 """Get the index of a specific file or folder given the name (or ID).""" 

72 for i, item in enumerate(self.contents): 

73 if item.id_ == name_or_id or item.name == name_or_id: 

74 return i 

75 raise KeyError(f'Name or id_ {name_or_id} not found in contents') 

76 

77 def __getitem__(self, name_or_id): 

78 """Get the item by name or id.""" 

79 index = self.index(name_or_id) 

80 return self.contents[index] 

81 

82 def __setitem__(self, name_or_id, value): 

83 """Set an item by name or id.""" 

84 try: 

85 index = self.index(name_or_id) 

86 self.contents.pop(index) 

87 self.contents.insert(index, value) 

88 except KeyError: 

89 self.contents.append(value) 

90 

91 def _repr(self, context=None, indent=0, **kwargs): # noqa: U100 

92 """Represent the contents of the folder.""" 

93 indent_ = ' '*indent 

94 line_start = f'\n{indent_}|- ' 

95 contents_repr = '' 

96 if self.contents: 

97 contents = sorted(self.contents, key=lambda x: isinstance(x, Folder)) 

98 lines = [u._repr(context=context, indent=indent+2) for u in contents] 

99 contents_repr = ':'+line_start.join(['']+lines) 

100 return f'{super()._repr(context=context)}{contents_repr}'