Coverage for src/somesy/julia/writer.py: 76%
59 statements
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-04 11:35 +0000
« prev ^ index » next coverage.py v7.16.0, created at 2026-09-04 11:35 +0000
1"""Julia writer."""
3import logging
4from pathlib import Path
6import tomlkit
7from rich.pretty import pretty_repr
8from tomlkit.items import InlineTable
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: bool | None = 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: Path | None = None) -> None:
56 """Save the julia file."""
57 path = path or self.path
58 if "description" in self._data and "\n" in self._data["description"]:
59 self._data["description"] = tomlkit.string(
60 self._data["description"], multiline=True
61 )
63 # Handle arrays with proper formatting
64 for key, value in self._data.items():
65 if isinstance(value, list):
66 array = tomlkit.array()
67 array.extend(value)
68 array.multiline(True)
69 # Ensure whitespace after commas in inline tables
70 for item in array:
71 if isinstance(item, InlineTable):
72 # Rebuild the inline table with desired formatting
73 formatted_item = tomlkit.inline_table()
74 for k, v in item.value.items():
75 formatted_item[k] = v
76 formatted_item.trivia.trail = " " # Add space after each comma
77 array[array.index(item)] = formatted_item
78 self._data[key] = array
79 else:
80 self._data[key] = value
82 with open(path, "w") as f:
83 tomlkit.dump(self._data, f)
85 @staticmethod
86 def _from_person(person: Person | Entity):
87 """Convert project metadata person object to a name+email string."""
88 return person.to_name_email_string()
90 @staticmethod
91 def _to_person(person_obj: str) -> Person | Entity | None:
92 """Convert from free string to person or entity object."""
93 try:
94 return Person.from_name_email_string(person_obj)
95 except (ValueError, AttributeError):
96 logger.info(f"Cannot convert {person_obj} to Person object, trying Entity.")
98 try:
99 return Entity.from_name_email_string(person_obj)
100 except (ValueError, AttributeError):
101 logger.warning(f"Cannot convert {person_obj} to Entity.")
102 return None
104 def sync(self, metadata: ProjectMetadata) -> None:
105 """Sync output file with other metadata files."""
106 # overridden to not sync fields that are not present in the Project.toml file
107 self.name = metadata.name
108 self.version = metadata.version
110 self._sync_authors(metadata)