Coverage for src/somesy/mkdocs/writer.py: 100%

54 statements  

« prev     ^ index     » next       coverage.py v7.6.0, created at 2024-07-29 07:50 +0000

1"""Project documentation with Markdown (MkDocs) parser and saver.""" 

2 

3import logging 

4from pathlib import Path 

5from typing import List, Optional 

6 

7from rich.pretty import pretty_repr 

8from ruamel.yaml import YAML 

9 

10from somesy.core.models import Person, ProjectMetadata 

11from somesy.core.writer import FieldKeyMapping, IgnoreKey, ProjectMetadataWriter 

12from somesy.mkdocs.models import MkDocsConfig 

13 

14logger = logging.getLogger("somesy") 

15 

16 

17class MkDocs(ProjectMetadataWriter): 

18 """Project documentation with Markdown (MkDocs) parser and saver.""" 

19 

20 def __init__(self, path: Path, create_if_not_exists: bool = False): 

21 """Project documentation with Markdown (MkDocs) parser. 

22 

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

24 """ 

25 self._yaml = YAML() 

26 self._yaml.preserve_quotes = True 

27 

28 mappings: FieldKeyMapping = { 

29 "name": ["site_name"], 

30 "description": ["site_description"], 

31 "homepage": ["site_url"], 

32 "repository": ["repo_url"], 

33 "authors": ["site_author"], 

34 "documentation": IgnoreKey(), 

35 "version": IgnoreKey(), 

36 "maintainers": IgnoreKey(), 

37 "license": IgnoreKey(), 

38 "keywords": IgnoreKey(), 

39 } 

40 super().__init__( 

41 path, create_if_not_exists=create_if_not_exists, direct_mappings=mappings 

42 ) 

43 

44 def _load(self): 

45 """Load the MkDocs file.""" 

46 with open(self.path) as f: 

47 self._data = self._yaml.load(f) 

48 

49 def _validate(self): 

50 """Validate the MkDocs file.""" 

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

52 logger.debug( 

53 f"Validating config using {MkDocsConfig.__name__}: {pretty_repr(config)}" 

54 ) 

55 MkDocsConfig(**config) 

56 

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

58 """Save the MkDocs object to a file.""" 

59 path = path or self.path 

60 self._yaml.dump(self._data, path) 

61 

62 @property 

63 def authors(self): 

64 """Return the only author from the source file as list.""" 

65 authors = self._get_property(self._get_key("authors")) 

66 if authors is None or self._to_person(authors) is None: 

67 return [] 

68 else: 

69 return [authors] 

70 

71 @authors.setter 

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

73 """Set the authors of the project.""" 

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

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

76 

77 @staticmethod 

78 def _from_person(person: Person): 

79 """MkDocs Person is a string with full name.""" 

80 return person.to_name_email_string() 

81 

82 @staticmethod 

83 def _to_person(person: str) -> Optional[Person]: 

84 """MkDocs Person is a string with full name.""" 

85 try: 

86 return Person.from_name_email_string(person) 

87 except (ValueError, AttributeError): 

88 logger.warning(f"Cannot convert {person} to Person object.") 

89 return None 

90 

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

92 """Sync the MkDocs object with the ProjectMetadata object.""" 

93 self.name = metadata.name 

94 self.description = metadata.description 

95 # no author merge since it is a free text field 

96 self.authors = metadata.authors() 

97 if metadata.homepage: 

98 self.homepage = str(metadata.homepage) 

99 if metadata.repository: 

100 self.repository = str(metadata.repository) 

101 self.repo_name = metadata.repository.path