0
# pyaml-env
1
2
A Python library that parses YAML configuration files and resolves environment variables, enabling secure configuration management by keeping sensitive data like passwords, API keys, and database credentials out of version control. The library supports custom YAML tags, default value handling, multiple environment variables per line, and provides a BaseConfig class for attribute-style configuration access.
3
4
## Package Information
5
6
- **Package Name**: pyaml-env
7
- **Package Type**: pypi
8
- **Language**: Python
9
- **Installation**: `pip install pyaml-env`
10
11
## Core Imports
12
13
```python
14
from pyaml_env import parse_config, BaseConfig
15
```
16
17
Individual imports:
18
19
```python
20
from pyaml_env import parse_config
21
from pyaml_env import BaseConfig
22
```
23
24
## Basic Usage
25
26
```python
27
from pyaml_env import parse_config, BaseConfig
28
29
# Parse YAML file with environment variable resolution
30
config = parse_config('path/to/config.yaml')
31
32
# Access as dictionary
33
username = config['database']['username']
34
35
# Or use BaseConfig for attribute-style access
36
config_obj = BaseConfig(config)
37
username = config_obj.database.username
38
```
39
40
Example YAML file with environment variables:
41
42
```yaml
43
database:
44
name: test_db
45
username: !ENV ${DB_USER}
46
password: !ENV ${DB_PASS}
47
url: !ENV 'http://${DB_BASE_URL}:${DB_PORT}'
48
```
49
50
With environment variables set:
51
52
```bash
53
export DB_USER=my_user
54
export DB_PASS=my_password
55
export DB_BASE_URL=localhost
56
export DB_PORT=5432
57
```
58
59
## Capabilities
60
61
### YAML Configuration Parsing
62
63
Parse YAML configuration files with automatic environment variable resolution using custom tags.
64
65
```python { .api }
66
def parse_config(
67
path=None,
68
data=None,
69
tag='!ENV',
70
default_sep=':',
71
default_value='N/A',
72
raise_if_na=False,
73
loader=yaml.SafeLoader,
74
encoding='utf-8'
75
):
76
"""
77
Load yaml configuration from path or from the contents of a file (data)
78
and resolve any environment variables. The environment variables
79
must have the tag e.g. !ENV *before* them and be in this format to be
80
parsed: ${VAR_NAME}
81
82
Args:
83
path (str, optional): Path to the yaml file
84
data (str, optional): YAML data itself as a stream
85
tag (str): Tag to look for environment variables (default: '!ENV'). If None, all env variables will be resolved
86
default_sep (str): Separator for default values (default: ':')
87
default_value (str): Default value when no environment variable or default is found (default: 'N/A')
88
raise_if_na (bool): Raise exception if there is no default value set for the env variable (default: False)
89
loader (Type[yaml.loader]): YAML loader to use (default: yaml.SafeLoader)
90
encoding (str): Encoding of the data if a path is specified (default: 'utf-8')
91
92
Returns:
93
dict: Parsed configuration dictionary with resolved environment variables
94
95
Raises:
96
ValueError: If neither path nor data is provided, or if raise_if_na=True and no default value is found for an environment variable
97
"""
98
```
99
100
**Usage Examples:**
101
102
File-based parsing:
103
```python
104
config = parse_config(path='config.yaml')
105
```
106
107
String-based parsing:
108
```python
109
yaml_data = '''
110
database:
111
host: !ENV ${DB_HOST:localhost}
112
port: !ENV ${DB_PORT:5432}
113
'''
114
config = parse_config(data=yaml_data)
115
```
116
117
Custom tag and defaults:
118
```python
119
config = parse_config(
120
path='config.yaml',
121
tag='!CUSTOM',
122
default_sep='|',
123
default_value='missing',
124
raise_if_na=True
125
)
126
```
127
128
**Environment Variable Syntax:**
129
130
Basic environment variable:
131
```yaml
132
username: !ENV ${DB_USER}
133
```
134
135
With default value:
136
```yaml
137
username: !ENV ${DB_USER:default_user}
138
port: !ENV ${DB_PORT:5432}
139
```
140
141
Multiple variables in one line:
142
```yaml
143
url: !ENV 'http://${HOST:localhost}:${PORT:8080}/api'
144
```
145
146
Type conversion with YAML tags:
147
```yaml
148
port: !ENV tag:yaml.org,2002:int ${DB_PORT:5432}
149
enabled: !ENV tag:yaml.org,2002:bool ${FEATURE_ENABLED:false}
150
ratio: !ENV tag:yaml.org,2002:float ${RATIO:1.5}
151
```
152
153
### Attribute-Style Configuration Access
154
155
Provides attribute-style access to configuration dictionaries, allowing dot notation for nested configuration access.
156
157
```python { .api }
158
class BaseConfig:
159
"""
160
A base Config class providing attribute-style access to configuration dictionaries.
161
Recursively converts nested dictionaries to BaseConfig objects.
162
"""
163
164
def __init__(self, config_dict):
165
"""
166
Initialize BaseConfig with a configuration dictionary.
167
Copies existing class attributes, updates with config_dict,
168
and recursively converts nested dictionaries to BaseConfig objects.
169
170
Args:
171
config_dict (dict): Configuration dictionary to wrap
172
"""
173
174
def __getattr__(self, field_name: str) -> Any:
175
"""
176
Provides attribute-style access to configuration values.
177
178
Args:
179
field_name (str): Name of the configuration field
180
181
Returns:
182
Any: The configuration value
183
"""
184
185
@property
186
def errors(self):
187
"""
188
Returns:
189
list: List of validation errors
190
"""
191
192
@property
193
def _is_validated(self):
194
"""
195
Returns:
196
bool: Whether validation has been performed
197
"""
198
199
@property
200
def _is_valid(self):
201
"""
202
Returns:
203
bool: Whether configuration passed validation
204
"""
205
206
@property
207
def _errors(self):
208
"""
209
Returns:
210
list: Internal list of validation errors
211
"""
212
213
def validate(self):
214
"""
215
Abstract method for configuration validation (must be implemented by subclasses).
216
217
Raises:
218
NotImplementedError: This method must be implemented in subclasses
219
"""
220
```
221
222
**Usage Examples:**
223
224
Basic attribute access:
225
```python
226
from pyaml_env import parse_config, BaseConfig
227
228
config = BaseConfig(parse_config('config.yaml'))
229
230
# Dot notation access instead of dictionary syntax
231
host = config.database.host
232
port = config.database.port
233
username = config.database.username
234
```
235
236
Nested configuration access:
237
```python
238
# YAML:
239
# app:
240
# database:
241
# primary:
242
# host: !ENV ${PRIMARY_DB_HOST}
243
# port: !ENV ${PRIMARY_DB_PORT:5432}
244
# cache:
245
# redis_url: !ENV ${REDIS_URL}
246
247
config = BaseConfig(parse_config('config.yaml'))
248
primary_host = config.app.database.primary.host
249
redis_url = config.app.database.cache.redis_url
250
```
251
252
Custom validation (subclass implementation):
253
```python
254
class DatabaseConfig(BaseConfig):
255
def validate(self):
256
if not hasattr(self, 'host'):
257
self._errors.append("Missing required field: host")
258
if hasattr(self, 'port') and not isinstance(self.port, int):
259
self._errors.append("Port must be an integer")
260
self._is_validated = True
261
self._is_valid = len(self._errors) == 0
262
263
db_config = DatabaseConfig(parse_config('db_config.yaml'))
264
db_config.validate()
265
if not db_config._is_valid:
266
print("Configuration errors:", db_config.errors)
267
```
268
269
## Advanced Features
270
271
### Custom YAML Loaders
272
273
Support for different YAML loaders including UnsafeLoader for serialized Python objects:
274
275
```python
276
import yaml
277
from pyaml_env import parse_config
278
279
# Using UnsafeLoader for Python objects
280
config = parse_config(path='config.yaml', loader=yaml.UnsafeLoader)
281
282
# Using FullLoader
283
config = parse_config(path='config.yaml', loader=yaml.FullLoader)
284
```
285
286
### Error Handling Modes
287
288
Control behavior when environment variables are missing:
289
290
```python
291
# Strict mode - raise exception if no default provided
292
try:
293
config = parse_config('config.yaml', raise_if_na=True)
294
except ValueError as e:
295
print(f"Missing environment variable: {e}")
296
297
# Permissive mode - use default_value for missing variables
298
config = parse_config('config.yaml', default_value='MISSING')
299
```
300
301
### Environment Variable Resolution Rules
302
303
1. **Tag Resolution**: Only values with the specified tag (default `!ENV`) are processed
304
2. **Variable Extraction**: Variables must use `${VARIABLE_NAME}` syntax
305
3. **Default Handling**: Use separator (default `:`) for defaults: `${VAR:default}`
306
4. **Fallback Chain**: Environment variable → default value → global default_value
307
5. **Type Conversion**: Support for YAML type tags like `tag:yaml.org,2002:int`
308
309
## Types
310
311
```python { .api }
312
from typing import Any, Type
313
import yaml
314
315
# Type aliases for function parameters
316
LoaderType = Type[yaml.loader]
317
ConfigDict = dict
318
```