2025-11-01 08:40:20 +01:00
|
|
|
from dataclasses import dataclass, field
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def unquote(value: str) -> str:
|
2024-12-16 08:40:56 +00:00
|
|
|
if value.startswith('"') and value.endswith('"'):
|
|
|
|
|
return value[1:-1]
|
|
|
|
|
else:
|
|
|
|
|
return value
|
|
|
|
|
|
|
|
|
|
|
2025-11-01 08:40:20 +01:00
|
|
|
@dataclass
|
2023-10-27 13:42:33 +02:00
|
|
|
class FepFile:
|
2025-11-01 08:40:20 +01:00
|
|
|
fep: str
|
|
|
|
|
frontmatter: list[str] = field(default_factory=list)
|
|
|
|
|
content: list[str] = field(default_factory=list)
|
|
|
|
|
|
|
|
|
|
def __post_init__(self):
|
2023-10-27 13:42:33 +02:00
|
|
|
with open(self.filename) as f:
|
2025-11-01 08:40:20 +01:00
|
|
|
lines = f.readlines()
|
|
|
|
|
|
|
|
|
|
status = 0
|
|
|
|
|
|
|
|
|
|
for line in lines:
|
|
|
|
|
if line == "---\n" and status <= 2:
|
|
|
|
|
status += 1
|
|
|
|
|
elif status == 1:
|
|
|
|
|
self.frontmatter.append(line.removesuffix("\n"))
|
|
|
|
|
elif status >= 2:
|
|
|
|
|
self.content.append(line.removesuffix("\n"))
|
2023-10-27 13:42:33 +02:00
|
|
|
|
|
|
|
|
@property
|
2025-11-01 08:40:20 +01:00
|
|
|
def filename(self) -> str:
|
2023-10-27 13:42:33 +02:00
|
|
|
return f"fep/{self.fep}/fep-{self.fep}.md"
|
|
|
|
|
|
2025-11-01 08:40:20 +01:00
|
|
|
def find_section_by_name(self, name):
|
2023-10-27 13:42:33 +02:00
|
|
|
result = []
|
|
|
|
|
is_summary = False
|
|
|
|
|
for x in self.content:
|
|
|
|
|
if is_summary:
|
|
|
|
|
if x.startswith("##"):
|
|
|
|
|
return "\n".join(result)
|
|
|
|
|
result.append(x)
|
2025-11-01 08:40:20 +01:00
|
|
|
elif x == f"## {name}":
|
2023-10-27 13:42:33 +02:00
|
|
|
is_summary = True
|
|
|
|
|
|
2025-11-01 08:40:20 +01:00
|
|
|
@property
|
|
|
|
|
def summary(self):
|
|
|
|
|
return self.find_section_by_name("Summary")
|
|
|
|
|
|
2023-10-27 13:42:33 +02:00
|
|
|
def write(self):
|
|
|
|
|
with open(self.filename, "w") as f:
|
|
|
|
|
f.write("---\n")
|
|
|
|
|
for x in self.frontmatter:
|
|
|
|
|
f.write(x + "\n")
|
|
|
|
|
f.write("---\n")
|
|
|
|
|
for x in self.content:
|
|
|
|
|
f.write(x + "\n")
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def parsed_frontmatter(self):
|
|
|
|
|
split = [x.split(":", 1) for x in self.frontmatter]
|
2024-12-16 08:40:56 +00:00
|
|
|
return {a: unquote(b.strip()) for a, b in split}
|
2023-10-27 13:42:33 +02:00
|
|
|
|
|
|
|
|
@property
|
2025-11-01 08:40:20 +01:00
|
|
|
def status(self):
|
|
|
|
|
return self.parsed_frontmatter["status"]
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def title(self) -> str:
|
2023-10-27 13:42:33 +02:00
|
|
|
titles = [x for x in self.content if x.startswith("# ")]
|
|
|
|
|
assert len(titles) > 0
|
|
|
|
|
|
|
|
|
|
title = titles[0]
|
|
|
|
|
|
|
|
|
|
begin_title = f"# FEP-{self.fep}: "
|
|
|
|
|
|
|
|
|
|
assert title.startswith(begin_title)
|
|
|
|
|
true_title = title.removeprefix(begin_title)
|
|
|
|
|
|
|
|
|
|
return true_title
|
|
|
|
|
|
2025-09-21 20:18:39 +02:00
|
|
|
@property
|
2025-11-01 08:40:20 +01:00
|
|
|
def implementation_count(self):
|
2025-09-21 20:18:39 +02:00
|
|
|
if self.parsed_frontmatter.get("type") != "implementation":
|
|
|
|
|
return 0
|
|
|
|
|
implementations = []
|
|
|
|
|
in_section = False
|
|
|
|
|
for line in self.content:
|
2025-11-01 08:40:20 +01:00
|
|
|
if line.startswith("#") and "Implementations" in line:
|
2025-09-21 20:18:39 +02:00
|
|
|
in_section = True
|
2025-11-01 08:40:20 +01:00
|
|
|
elif in_section is True and line.startswith("#"):
|
2025-09-21 20:18:39 +02:00
|
|
|
in_section = False
|
2025-11-01 08:40:20 +01:00
|
|
|
elif in_section is True and (line.startswith("-") or line.startswith("*")):
|
2025-09-21 20:18:39 +02:00
|
|
|
implementations.append(line)
|
2025-11-01 08:40:20 +01:00
|
|
|
|
2025-09-21 20:18:39 +02:00
|
|
|
return len(implementations)
|
|
|
|
|
|
2023-10-27 13:42:33 +02:00
|
|
|
@staticmethod
|
2025-11-01 08:40:20 +01:00
|
|
|
def parsefile(f) -> tuple[list[str], list[str]]:
|
2023-10-27 13:42:33 +02:00
|
|
|
lines = f.readlines()
|
|
|
|
|
|
|
|
|
|
status = 0
|
|
|
|
|
frontmatter = []
|
|
|
|
|
content = []
|
|
|
|
|
|
|
|
|
|
for line in lines:
|
|
|
|
|
if line == "---\n" and status <= 2:
|
|
|
|
|
status += 1
|
|
|
|
|
elif status == 1:
|
|
|
|
|
frontmatter.append(line.removesuffix("\n"))
|
|
|
|
|
elif status >= 2:
|
|
|
|
|
content.append(line.removesuffix("\n"))
|
|
|
|
|
|
|
|
|
|
return frontmatter, content
|
2025-11-01 08:40:20 +01:00
|
|
|
|
|
|
|
|
def frontmatter_table(self) -> str:
|
|
|
|
|
keys = " | ".join(self.parsed_frontmatter.keys())
|
|
|
|
|
values = " | ".join(self.parsed_frontmatter.values())
|
|
|
|
|
divider = " | ".join(["---"] * len(self.parsed_frontmatter))
|
|
|
|
|
|
|
|
|
|
return f"""
|
|
|
|
|
| {keys} |
|
|
|
|
|
| {divider} |
|
|
|
|
|
| {values} |
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def content_and_title(self) -> tuple[str, str]:
|
|
|
|
|
for j, line in enumerate(self.content):
|
|
|
|
|
if line.startswith("# "):
|
|
|
|
|
return line, "\n".join(self.content[j + 1 :])
|
|
|
|
|
|
|
|
|
|
raise Exception("Could not determine title and content")
|