Skip to content

Commit 4fb523f

Browse files
committed
add fortran module
1 parent 1fd7ad0 commit 4fb523f

3 files changed

Lines changed: 190 additions & 0 deletions

File tree

src/somesy/fortran/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"""Fortran module."""
2+
from .writer import Fortran
3+
4+
__all__ = ["Fortran"]

src/somesy/fortran/models.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
"""Pyproject models."""
2+
from typing import Optional, Set
3+
4+
from packaging.version import parse as parse_version
5+
from pydantic import (
6+
BaseModel,
7+
Field,
8+
field_validator,
9+
)
10+
from typing_extensions import Annotated
11+
12+
from somesy.core.types import HttpUrlStr
13+
14+
15+
class FortranConfig(BaseModel):
16+
"""Fortran configuration model."""
17+
18+
model_config = dict(use_enum_values=True)
19+
20+
name: Annotated[
21+
str,
22+
Field(pattern=r"^[A-Za-z0-9]+([_-][A-Za-z0-9]+)*$", description="Package name"),
23+
]
24+
version: Annotated[
25+
Optional[str],
26+
Field(
27+
pattern=r"^\d+(\.\d+)*((a|b|rc)\d+)?(post\d+)?(dev\d+)?$",
28+
description="Package version",
29+
),
30+
] = None
31+
description: Annotated[
32+
Optional[str], Field(description="Package description")
33+
] = None
34+
license: Annotated[
35+
Optional[str],
36+
Field(description="SPDX license identifier(s)."),
37+
] = None
38+
author: Annotated[
39+
Optional[str], Field(description="Package author information")
40+
] = None
41+
maintainer: Annotated[
42+
Optional[str], Field(description="Package maintainer information")
43+
] = None
44+
copyright: Annotated[
45+
Optional[str], Field(description="Package copyright text")
46+
] = None
47+
homepage: Annotated[
48+
Optional[HttpUrlStr], Field(description="Package homepage")
49+
] = None
50+
keywords: Annotated[
51+
Optional[Set[str]], Field(description="Keywords that describe the package")
52+
] = None
53+
categories: Annotated[
54+
Optional[Set[str]], Field(description="Categories that package falls into")
55+
] = None
56+
57+
@field_validator("version")
58+
@classmethod
59+
def validate_version(cls, v):
60+
"""Validate version using PEP 440."""
61+
try:
62+
_ = parse_version(v)
63+
except ValueError as err:
64+
raise ValueError("Invalid version") from err
65+
return v

src/somesy/fortran/writer.py

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
"""Fortran writer."""
2+
import logging
3+
from pathlib import Path
4+
from typing import Any, List, Optional
5+
6+
import tomlkit
7+
from rich.pretty import pretty_repr
8+
9+
from somesy.core.models import Person, ProjectMetadata
10+
from somesy.core.writer import FieldKeyMapping, IgnoreKey, ProjectMetadataWriter
11+
12+
from .models import FortranConfig
13+
14+
logger = logging.getLogger("somesy")
15+
16+
17+
class Fortran(ProjectMetadataWriter):
18+
"""Fortran config file handler parsed from fpm.toml."""
19+
20+
def __init__(self, path: Path):
21+
"""Fortran config file handler parsed from fpm.toml.
22+
23+
See [somesy.core.writer.ProjectMetadataWriter.__init__][].
24+
"""
25+
mappings: FieldKeyMapping = {
26+
"authors": ["author"],
27+
"maintainers": ["maintainer"],
28+
"documentation": IgnoreKey(),
29+
}
30+
super().__init__(path, create_if_not_exists=False, direct_mappings=mappings)
31+
32+
@property
33+
def authors(self):
34+
"""Return the only author of the fpm.toml file as list."""
35+
authors = []
36+
try:
37+
self._to_person(self._get_property(self._get_key("authors")))
38+
authors = [self._get_property(self._get_key("authors"))]
39+
except ValueError:
40+
logger.warning("Cannot convert authors to Person object.")
41+
return authors
42+
43+
@authors.setter
44+
def authors(self, authors: List[Person]) -> None:
45+
"""Set the authors of the project."""
46+
authors = self._from_person(authors[0])
47+
self._set_property(self._get_key("authors"), authors)
48+
49+
@property
50+
def maintainers(self):
51+
"""Return the only author of the fpm.toml file as list."""
52+
maintainers = self._get_property(self._get_key("maintainers"))
53+
if maintainers:
54+
return [self._get_property(self._get_key("maintainers"))]
55+
return []
56+
57+
@maintainers.setter
58+
def maintainers(self, maintainers: List[Person]) -> None:
59+
"""Set the maintainers of the project."""
60+
maintainers = self._from_person(maintainers[0])
61+
self._set_property(self._get_key("maintainers"), maintainers)
62+
63+
def _load(self) -> None:
64+
"""Load fpm.toml file."""
65+
with open(self.path) as f:
66+
self._data = tomlkit.load(f)
67+
68+
def _validate(self) -> None:
69+
"""Validate poetry config using pydantic class.
70+
71+
In order to preserve toml comments and structure, tomlkit library is used.
72+
Pydantic class only used for validation.
73+
"""
74+
config = dict(self._get_property([]))
75+
logger.debug(
76+
f"Validating config using {FortranConfig.__name__}: {pretty_repr(config)}"
77+
)
78+
FortranConfig(**config)
79+
80+
def save(self, path: Optional[Path] = None) -> None:
81+
"""Save the fpm file."""
82+
path = path or self.path
83+
with open(path, "w") as f:
84+
tomlkit.dump(self._data, f)
85+
86+
@staticmethod
87+
def _from_person(person: Person):
88+
"""Convert project metadata person object to poetry string for person format "full name <email>."""
89+
return person.to_name_email_string()
90+
91+
@staticmethod
92+
def _to_person(person_obj: Any) -> Person:
93+
"""Cannot convert from free string to person object."""
94+
try:
95+
return Person.from_name_email_string(person_obj)
96+
except ValueError:
97+
logger.warning(f"Cannot convert {person_obj} to Person object.")
98+
return None
99+
100+
def sync(self, metadata: ProjectMetadata) -> None:
101+
"""Sync output file with other metadata files."""
102+
self.name = metadata.name
103+
self.description = metadata.description
104+
105+
if metadata.version:
106+
self.version = metadata.version
107+
108+
if metadata.keywords:
109+
self.keywords = metadata.keywords
110+
111+
self.authors = metadata.authors()
112+
maintainers = metadata.maintainers()
113+
114+
# set if not empty
115+
if maintainers:
116+
# only one maintainer is allowed
117+
self.maintainers = maintainers
118+
119+
self.license = metadata.license.value
120+
121+
self.homepage = str(metadata.homepage) if metadata.homepage else None

0 commit comments

Comments
 (0)