Coverage for src/somesy/fortran/writer.py: 91%

67 statements  

« prev     ^ index     » next       coverage.py v7.16.0, created at 2026-11-03 13:13 +0000

1"""Fortran writer.""" 

2 

3import logging 

4from pathlib import Path 

5 

6import tomlkit 

7from rich.pretty import pretty_repr 

8 

9from somesy.core.models import Entity, Person, ProjectMetadata, parse_name_email 

10from somesy.core.writer import ( 

11 FieldKeyMapping, 

12 IgnoreKey, 

13 ProjectMetadataWriter, 

14 format_toml_data, 

15 license_expression, 

16) 

17 

18from .models import FortranConfig 

19 

20logger = logging.getLogger("somesy") 

21 

22 

23class Fortran(ProjectMetadataWriter): 

24 """Fortran config file handler parsed from fpm.toml.""" 

25 

26 def __init__( 

27 self, 

28 path: Path, 

29 pass_validation: bool | None = False, 

30 ): 

31 """Fortran config file handler parsed from fpm.toml. 

32 

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

34 """ 

35 mappings: FieldKeyMapping = { 

36 "authors": ["author"], 

37 "maintainers": ["maintainer"], 

38 "documentation": IgnoreKey(), 

39 } 

40 super().__init__( 

41 path, 

42 create_if_not_exists=False, 

43 direct_mappings=mappings, 

44 pass_validation=pass_validation, 

45 ) 

46 

47 @property 

48 def authors(self): 

49 """Return the only author of the fpm.toml file as list.""" 

50 author = self._get_property(self._get_key("authors")) 

51 return [author] if author else [] 

52 

53 @authors.setter 

54 def authors(self, authors: list[Person | Entity]) -> None: 

55 """Set the authors of the project.""" 

56 self._set_property(self._get_key("authors"), self._from_person(authors[0])) 

57 

58 @property 

59 def maintainers(self): 

60 """Return the only author of the fpm.toml file as list.""" 

61 maintainers = self._get_property(self._get_key("maintainers")) 

62 if maintainers: 

63 return [self._get_property(self._get_key("maintainers"))] 

64 return [] 

65 

66 @maintainers.setter 

67 def maintainers(self, maintainers: list[Person | Entity]) -> None: 

68 """Set the maintainers of the project.""" 

69 maintainer = self._from_person(maintainers[0]) 

70 self._set_property(self._get_key("maintainers"), maintainer) 

71 

72 def _load(self) -> None: 

73 """Load fpm.toml file.""" 

74 with open(self.path, encoding="utf-8") as f: 

75 self._data = tomlkit.load(f) 

76 

77 def _validate(self) -> None: 

78 """Validate poetry config using pydantic class. 

79 

80 In order to preserve toml comments and structure, tomlkit library is used. 

81 Pydantic class only used for validation. 

82 """ 

83 if self.pass_validation: 

84 return 

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

86 logger.debug( 

87 f"Validating config using {FortranConfig.__name__}: {pretty_repr(config)}" 

88 ) 

89 FortranConfig(**config) 

90 

91 def save(self, path: Path | None = None) -> None: 

92 """Save the fpm file.""" 

93 path = path or self.path 

94 format_toml_data(self._data) 

95 

96 with open(path, "w", encoding="utf-8") as f: 

97 tomlkit.dump(self._data, f) 

98 

99 @staticmethod 

100 def _from_person(person: Person | Entity): 

101 """Convert project metadata person/entity object to poetry string for person format "full name <email>.""" 

102 return person.to_name_email_string() 

103 

104 @staticmethod 

105 def _to_person(person_obj: str) -> Person | Entity | None: 

106 """Convert from free string to person or entity object.""" 

107 return parse_name_email(person_obj) 

108 

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

110 """Sync output file with other metadata files.""" 

111 if metadata.name is not None: 

112 self.name = metadata.name 

113 if metadata.description is not None: 

114 self.description = metadata.description 

115 

116 if metadata.version: 

117 self.version = metadata.version 

118 

119 if metadata.keywords: 

120 self.keywords = metadata.keywords 

121 

122 if metadata.authors(): 

123 self.authors = metadata.authors() 

124 maintainers = metadata.maintainers() 

125 

126 # set if not empty 

127 if maintainers: 

128 # only one maintainer is allowed 

129 self.maintainers = maintainers 

130 

131 if licenses := metadata.license: 

132 self.license = license_expression(licenses) 

133 

134 if metadata.homepage: 

135 self.homepage = str(metadata.homepage)