Coverage for src/somesy/package_json/writer.py: 93%
72 statements
« prev ^ index » next coverage.py v7.3.2, created at 2024-04-30 09:42 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2024-04-30 09:42 +0000
1"""package.json parser and saver."""
2import logging
3from collections import OrderedDict
4from pathlib import Path
5from typing import Dict, List, Optional, Union
7from rich.pretty import pretty_repr
9from somesy.core.models import Person, ProjectMetadata
10from somesy.core.writer import FieldKeyMapping, IgnoreKey, ProjectMetadataWriter
11from somesy.json_wrapper import json
12from somesy.package_json.models import PackageJsonConfig
14logger = logging.getLogger("somesy")
17class PackageJSON(ProjectMetadataWriter):
18 """package.json parser and saver."""
20 def __init__(
21 self,
22 path: Path,
23 ):
24 """package.json parser.
26 See [somesy.core.writer.ProjectMetadataWriter.__init__][].
27 """
28 mappings: FieldKeyMapping = {
29 "authors": ["author"],
30 "documentation": IgnoreKey(),
31 }
32 super().__init__(path, create_if_not_exists=False, direct_mappings=mappings)
34 @property
35 def authors(self):
36 """Return the only author of the package.json file as list."""
37 return [self._get_property(self._get_key("authors"))]
39 @authors.setter
40 def authors(self, authors: List[Person]) -> None:
41 """Set the authors of the project."""
42 authors = self._from_person(authors[0])
43 self._set_property(self._get_key("authors"), authors)
45 @property
46 def contributors(self):
47 """Return the contributors of the package.json file."""
48 return self._get_property(self._get_key("contributors"))
50 @contributors.setter
51 def contributors(self, contributors: List[Person]) -> None:
52 """Set the contributors of the project."""
53 contributors = [self._from_person(c) for c in contributors]
54 self._set_property(self._get_key("contributors"), contributors)
56 def _load(self) -> None:
57 """Load package.json file."""
58 with self.path.open() as f:
59 self._data = json.load(f, object_pairs_hook=OrderedDict)
61 def _validate(self) -> None:
62 """Validate package.json content using pydantic class."""
63 config = dict(self._get_property([]))
64 logger.debug(
65 f"Validating config using {PackageJsonConfig.__name__}: {pretty_repr(config)}"
66 )
67 PackageJsonConfig(**config)
69 def save(self, path: Optional[Path] = None) -> None:
70 """Save the package.json file."""
71 path = path or self.path
72 logger.debug(f"Saving package.json to {path}")
74 with path.open("w") as f:
75 # package.json indentation is 2 spaces
76 json.dump(self._data, f)
78 @staticmethod
79 def _from_person(person: Person):
80 """Convert project metadata person object to package.json dict for person format."""
81 person_dict = {"name": person.full_name}
82 if person.email:
83 person_dict["email"] = person.email
84 if person.orcid:
85 person_dict["url"] = str(person.orcid)
86 return person_dict
88 @staticmethod
89 def _to_person(person) -> Person:
90 """Convert package.json dict or str for person format to project metadata person object."""
91 if isinstance(person, str):
92 # parse from package.json format
93 person = PackageJsonConfig.convert_author(person).model_dump(
94 exclude_none=True
95 )
97 names = list(map(lambda s: s.strip(), person["name"].split()))
98 person_obj = {
99 "given-names": " ".join(names[:-1]),
100 "family-names": names[-1],
101 }
102 if "email" in person:
103 person_obj["email"] = person["email"].strip()
104 if "url" in person:
105 person_obj["orcid"] = person["url"].strip()
106 return Person(**person_obj)
108 def sync(self, metadata: ProjectMetadata) -> None:
109 """Sync package.json with project metadata.
111 Use existing sync function from ProjectMetadataWriter but update repository and contributors.
112 """
113 super().sync(metadata)
114 self.contributors = self._sync_person_list(self.contributors, metadata.people)
116 @property
117 def repository(self) -> Optional[Union[str, Dict]]:
118 """Return the repository url of the project."""
119 if repo := super().repository:
120 if isinstance(repo, str):
121 return repo
122 else:
123 return repo.get("url")
124 else:
125 return None
127 @repository.setter
128 def repository(self, value: Optional[Union[str, Dict]]) -> None:
129 """Set the repository url of the project."""
130 self._set_property(self._get_key("repository"), dict(type="git", url=value))