-
Notifications
You must be signed in to change notification settings - Fork 75
/
env.py
125 lines (94 loc) · 3.59 KB
/
env.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
"""
A module for exposing application-wide environment variables.
This module is responsible for loading, parsing, and validating the application's
environment configuration from the `env.yaml` file. It uses Pydantic to ensure that
the configuration adheres to expected formats and types.
Functions:
- create_default_config: Creates a default configuration file if it doesn't exist.
Classes:
- EnvConfig: Loads the configuration and exposes it as Python objects.
- RemoteNode: Represents a remote node configuration with validation.
- Config: Represents the overall configuration structure with validation.
Usage:
- Initialize an instance of EnvConfig to load the configuration.
- Access configuration values via properties (e.g., env.remote_nodes).
"""
from pathlib import Path
from typing import Dict, List
import yaml
from jinja2 import Environment, PackageLoader
from pydantic import BaseModel, HttpUrl, ValidationError
ENV_PATH = Path(__file__).resolve().parent.parent.parent / "env.yaml"
class RemoteNode(BaseModel):
"""
Represents a configuration for a remote node.
Attributes:
- name (str): The name of the remote node.
- node_url (HttpUrl): The URL for the remote node, validated as a proper URL.
- rpc_headers (Dict[str, str]): A dictionary of optional RPC headers, defaults to empty dict.
"""
name: str
node_url: HttpUrl
rpc_headers: Dict[str, str] = {}
class Config(BaseModel):
"""
Represents the overall environment configuration.
Attributes:
- remote_nodes (List[RemoteNode]): A list of remote node configurations.
"""
remote_nodes: List[RemoteNode]
class EnvConfig:
"""
Loads and validates environment configuration from `env.yaml`.
Attributes:
- config (Config): An instance of the Config class containing validated settings.
Raises:
- ValueError: If the configuration is invalid.
"""
def __init__(self):
if not ENV_PATH.exists():
raise FileNotFoundError(
f"The configuration file '{ENV_PATH}' does not exist. "
"Run `uv run env_int` to create it."
)
with ENV_PATH.open("r") as file:
config_data = yaml.safe_load(file)
try:
self.config = Config(**config_data) # Validate and parse with Pydantic
except ValidationError as e:
raise ValueError(f"Invalid configuration: {e}")
@property
def remote_nodes(self):
"""Returns the list of remote nodes from the configuration."""
return self.config.remote_nodes
# The default configuration represented as a Config model.
DEFAULT_CONFIG = Config(
remote_nodes=[
RemoteNode(
name="mainnet_archive",
node_url="http://example.com",
rpc_headers={"client-secret": "<secret>"},
)
]
)
def create_default_config():
"""
Creates a default configuration file `env.yaml` from the Jinja2 template.
Raises:
IOError: If there is an error writing to the `env.yaml` file.
"""
# Check if the config file already exists
if ENV_PATH.exists():
print(
f"🚧 The configuration file '{ENV_PATH}' already exists. "
"Please update it manually if needed."
)
exit(1)
template_environment = Environment(
loader=PackageLoader("config"), trim_blocks=True, lstrip_blocks=True
)
template = template_environment.get_template("env.yaml.j2")
env_yaml = template.render(config=DEFAULT_CONFIG)
with ENV_PATH.open("w") as file:
file.write(env_yaml)
print(f"Env file created: {ENV_PATH}")