Skip to content

Instantly share code, notes, and snippets.

@peacock0803sz
Created August 9, 2023 15:46
Show Gist options
  • Save peacock0803sz/e3f1e7c0b611dacba81475099581ceea to your computer and use it in GitHub Desktop.
Save peacock0803sz/e3f1e7c0b611dacba81475099581ceea to your computer and use it in GitHub Desktop.
Expand Jinja2 Template with CSV/TSV/JSON
#!/usr/bin/env python3.11
import argparse
from collections.abc import Iterable
from dataclasses import asdict, dataclass
from enum import Enum
from pathlib import Path
from typing import Literal, NamedTuple
from jinja2 import Environment, FileSystemLoader
class SourceFileTypes(Enum):
CSV = "csv"
TSV = "tsv"
JSON = "json"
@classmethod
def to_list(cls):
return [s.value for s in cls]
class ScriptArgs(NamedTuple):
template: Path
source: Path
output: Path
source_type: SourceFileTypes
newlines: int
@dataclass
class ComputeInstance:
"""
Mapper and validation dataclass from dict
"""
id_: str
name: str
machine_type: str
description: str
subnet: str
zone: str
nat_ip: str
tags: list[str]
boot_disk: str
os_type: Literal["linux", "windows"]
@classmethod
def from_dict(cls, values: dict[str, str]):
_d = {}
# バリデーションと変換処理
return cls(**_d)
def _csv(path: Path, delimiter: Literal[",", "\t"] = ","):
import csv
with open(path.resolve(), "r") as file:
for row in csv.DictReader(file, delimiter=delimiter):
yield ComputeInstance.from_dict(row)
def _json(path: Path):
import json
with open(path.resolve(), "r") as file:
for row in file.readlines()[1:]:
yield ComputeInstance.from_dict(json.loads(row))
def load_values(
source_path: Path, source_type: SourceFileTypes
) -> Iterable[ComputeInstance]:
match SourceFileTypes(source_type):
case SourceFileTypes.CSV.value:
return _csv(source_path)
case SourceFileTypes.TSV.value:
return _csv(source_path, delimiter="\t")
case SourceFileTypes.JSON.value:
return _json(source_path)
case _:
raise ValueError("Unknown source type: %s" % source_type)
def render(template_name: Path, values: dict):
env = Environment(loader=FileSystemLoader(searchpath="./"))
path = template_name.relative_to(Path(__file__).parent.resolve())
return env.get_template(str(path)).render(**values)
def parse_args() -> ScriptArgs:
parser = argparse.ArgumentParser()
parser.add_argument("template", type=Path, help="Path to the Jinja template file")
parser.add_argument("source", type=Path, help="Path to the source file")
parser.add_argument("output", type=Path, help="Path to the output file")
parser.add_argument(
"--source-type",
"-t",
choices=SourceFileTypes.to_list(),
default="csv",
help="Source file type",
)
parser.add_argument(
"--newlines",
"-n",
type=int,
default=2,
help="Number of newlines at each end of result strings",
)
return parser.parse_args() # type: ignore
def main():
args = parse_args()
newlines = "\n" * args.newlines
with open(args.output.resolve(), "w") as f:
for value in load_values(args.source, args.source_type):
print("Loaded: %s" % value)
result = render(args.template.resolve(), asdict(value))
f.write(result + newlines)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment