Coverage for src/somesy/julia/writer.py: 75%
60 statements
« prev ^ index » next coverage.py v7.6.0, created at 2025-03-14 13:02 +0000
« prev ^ index » next coverage.py v7.6.0, created at 2025-03-14 13:02 +0000
1"""Julia writer."""
3import logging
4from pathlib import Path
5from typing import Optional, Union
7import tomlkit
8from rich.pretty import pretty_repr
10from somesy.core.models import Entity, Person, ProjectMetadata
11from somesy.core.writer import ProjectMetadataWriter
13from .models import JuliaConfig
15logger = logging.getLogger("somesy")
18class Julia(ProjectMetadataWriter):
19 """Julia config file handler parsed from Project.toml."""
21 def __init__(
22 self,
23 path: Path,
24 pass_validation: Optional[bool] = False,
25 ):
26 """Julia config file handler parsed from Project.toml.
28 See [somesy.core.writer.ProjectMetadataWriter.__init__][].
29 """
30 super().__init__(
31 path,
32 create_if_not_exists=False,
33 pass_validation=pass_validation,
34 )
36 def _load(self) -> None:
37 """Load Project.toml file."""
38 with open(self.path) as f:
39 self._data = tomlkit.load(f)
41 def _validate(self) -> None:
42 """Validate poetry config using pydantic class.
44 In order to preserve toml comments and structure, tomlkit library is used.
45 Pydantic class only used for validation.
46 """
47 if self.pass_validation:
48 return
49 config = dict(self._get_property([]))
50 logger.debug(
51 f"Validating config using {JuliaConfig.__name__}: {pretty_repr(config)}"
52 )
53 JuliaConfig(**config)
55 def save(self, path: Optional[Path] = None) -> None:
56 """Save the julia file."""
57 path = path or self.path
58 if "description" in self._data:
59 if "\n" in self._data["description"]:
60 self._data["description"] = tomlkit.string(
61 self._data["description"], multiline=True
62 )
64 # Handle arrays with proper formatting
65 for key, value in self._data.items():
66 if isinstance(value, list):
67 array = tomlkit.array()
68 array.extend(value)
69 array.multiline(True)
70 # Ensure whitespace after commas in inline tables
71 for item in array:
72 if isinstance(item, tomlkit.items.InlineTable):
73 # Rebuild the inline table with desired formatting
74 formatted_item = tomlkit.inline_table()
75 for k, v in item.value.items():
76 formatted_item[k] = v
77 formatted_item.trivia.trail = " " # Add space after each comma
78 array[array.index(item)] = formatted_item
79 self._data[key] = array
80 else:
81 self._data[key] = value
83 with open(path, "w") as f:
84 tomlkit.dump(self._data, f)
86 @staticmethod
87 def _from_person(person: Union[Person, Entity]):
88 """Convert project metadata person object to a name+email string."""
89 return person.to_name_email_string()
91 @staticmethod
92 def _to_person(person: str) -> Optional[Person]:
93 """Convert from free string to person or entity object."""
94 try:
95 return Person.from_name_email_string(person)
96 except (ValueError, AttributeError):
97 logger.info(f"Cannot convert {person} to Person object, trying Entity.")
99 try:
100 return Entity.from_name_email_string(person)
101 except (ValueError, AttributeError):
102 logger.warning(f"Cannot convert {person} to Entity.")
103 return None
105 def sync(self, metadata: ProjectMetadata) -> None:
106 """Sync output file with other metadata files."""
107 # overridden to not sync fields that are not present in the Project.toml file
108 self.name = metadata.name
109 self.version = metadata.version
111 self._sync_authors(metadata)