Coverage for src/somesy/package_json/writer.py: 98%

64 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-08-10 14:33 +0000

1"""package.json parser and saver.""" 

2import json 

3import logging 

4from collections import OrderedDict 

5from pathlib import Path 

6from typing import List, Optional 

7 

8from rich.pretty import pretty_repr 

9 

10from somesy.core.models import Person, ProjectMetadata 

11from somesy.core.writer import ProjectMetadataWriter 

12from somesy.package_json.models import PackageJsonConfig 

13 

14logger = logging.getLogger("somesy") 

15 

16 

17class PackageJSON(ProjectMetadataWriter): 

18 """package.json parser and saver.""" 

19 

20 def __init__( 

21 self, 

22 path: Path, 

23 ): 

24 """package.json parser. 

25 

26 See [somesy.core.writer.ProjectMetadataWriter.__init__][]. 

27 """ 

28 mappings = { 

29 "authors": ["author"], 

30 } 

31 super().__init__(path, create_if_not_exists=False, direct_mappings=mappings) 

32 

33 @property 

34 def authors(self): 

35 """Return the only author of the package.json file as list.""" 

36 return [self._get_property(self._get_key("authors"))] 

37 

38 @authors.setter 

39 def authors(self, authors: List[Person]) -> None: 

40 """Set the authors of the project.""" 

41 authors = self._from_person(authors[0]) 

42 self._set_property(self._get_key("authors"), authors) 

43 

44 @property 

45 def contributors(self): 

46 """Return the contributors of the package.json file.""" 

47 return self._get_property(self._get_key("contributors")) 

48 

49 @contributors.setter 

50 def contributors(self, contributors: List[Person]) -> None: 

51 """Set the contributors of the project.""" 

52 contributors = [self._from_person(c) for c in contributors] 

53 self._set_property(self._get_key("contributors"), contributors) 

54 

55 def _load(self) -> None: 

56 """Load package.json file.""" 

57 with self.path.open() as f: 

58 self._data = json.load(f, object_pairs_hook=OrderedDict) 

59 

60 def _validate(self) -> None: 

61 """Validate package.json content using pydantic class.""" 

62 config = dict(self._get_property([])) 

63 logger.debug( 

64 f"Validating config using {PackageJsonConfig.__name__}: {pretty_repr(config)}" 

65 ) 

66 PackageJsonConfig(**config) 

67 

68 def save(self, path: Optional[Path] = None) -> None: 

69 """Save the package.json file.""" 

70 path = path or self.path 

71 logger.debug(f"Saving package.json to {path}") 

72 

73 with path.open("w") as f: 

74 # package.json indentation is 2 spaces 

75 json.dump(self._data, f, indent=2) 

76 

77 @staticmethod 

78 def _from_person(person: Person): 

79 """Convert project metadata person object to package.json dict for person format.""" 

80 person_dict = {"name": person.full_name} 

81 if person.email: 

82 person_dict["email"] = person.email 

83 if person.orcid: 

84 person_dict["url"] = person.orcid 

85 return person_dict 

86 

87 @staticmethod 

88 def _to_person(person) -> Person: 

89 """Convert package.json dict or str for person format to project metadata person object.""" 

90 if isinstance(person, str): 

91 # parse from package.json format 

92 person = PackageJsonConfig.convert_author(person).dict(exclude_none=True) 

93 

94 names = list(map(lambda s: s.strip(), person["name"].split())) 

95 person_obj = { 

96 "given-names": " ".join(names[:-1]), 

97 "family-names": names[-1], 

98 } 

99 if "email" in person: 

100 person_obj["email"] = person["email"].strip() 

101 if "url" in person: 

102 person_obj["orcid"] = person["url"].strip() 

103 return Person(**person_obj) 

104 

105 def sync(self, metadata: ProjectMetadata) -> None: 

106 """Sync package.json with project metadata. 

107 

108 Use existing sync function from ProjectMetadataWriter but update repository and contributors. 

109 """ 

110 super().sync(metadata) 

111 self.contributors = self._sync_person_list(self.contributors, metadata.people) 

112 if metadata.repository: 

113 self.repository = {"type": "git", "url": metadata.repository}