-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconfig.txt
More file actions
374 lines (292 loc) · 9.4 KB
/
Copy pathconfig.txt
File metadata and controls
374 lines (292 loc) · 9.4 KB
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
.. module:: firebird.base.config
:synopsis: Configuration definitions
##################################
config - Configuration definitions
##################################
Overview
========
Complex applications (and some library modules like `logging`) could be often parametrized
via configuration. This module provides a framework for unified structured configuration
that supports:
* configuration options of various data type, including lists and other complex types
* validation
* direct manipulation of configuration values
* reading from (and writing into) configuration in `configparser` format
* exchanging configuration (for example between processes) using Google protobuf messages
Architecture
------------
The framework is based around two classes:
* `.Config` - Collection of configuration options and sub-collections. Particular
configuration is then realized as descendant from this class, that defines configuration
options in constructor, and customize the validation when required.
* `.Option` - Abstract base class for configuration options, where descendants implement
handling of particular data type. This module provides implementation for next data
types: `str`, `int`, `float`, `bool`, `~decimal.Decimal`, `~enum.Enum`, `~enum.Flag`,
`~uuid.UUID`, `.MIME`, `.ZMQAddress`, `list`, `~dataclasses.dataclass`, `.PyExpr`,
`.PyCode` and `.PyCallable`. It also provides special options `ConfigOption` and
`ConfigListOption`.
Additionally, the `.DirectoryScheme` abstract base class defines set of mostly
used application directories. The function `.get_directory_scheme()` could be then used
to obtain instance that implements platform-specific standards for file-system location
for these directories. Currently, only "Windows", "Linux" and "MacOS" directory schemes
are supported.
.. tip::
You may use `.get_directory_scheme()` function to get the scheme suitable for platform
where your application is running.
.. tip::
If your configurations contain secrets like passwords or access tokens, that would be
read from files via `configparser`, you should consider to use `.EnvExtendedInterpolation`
that has support for option values defined via environment variables.
Usage
-----
First, you need to define your own configuration.
.. code-block::
from enum import IntEnum
from firebird.base.config import Config, StrOption, IntOption, ListOption
class SampleEnum(IntEnum):
"Enum for testing"
UNKNOWN = 0
READY = 1
RUNNING = 2
WAITING = 3
SUSPENDED = 4
FINISHED = 5
ABORTED = 6
class DbConfig(Config):
"Simple database config"
def __init__(self, name: str):
super().__init__(name)
# options
self.database: StrOption = StrOption('database', 'Database connection string',
required=True)
self.user: StrOption = StrOption('user', 'User name', required=True,
default='SYSDBA')
self.password: StrOption = StrOption('password', 'User password')
class SampleConfig(Config):
"""Sample Config.
Has three options and two sub-configs.
"""
def __init__(self):
super().__init__('sample-config')
# options
self.opt_str: StrOption = StrOption('opt_str', "Sample string option")
self.opt_int: IntOption = StrOption('opt_int', "Sample int option")
self.enum_list: ListOption = ListOption('enum_list', "List of enum values",
item_type=SampleEnum)
# sub configs
self.master_db: DbConfig = DbConfig('master-db')
self.backup_db: DbConfig = DbConfig('backup-db')
.. important::
Option must be assigned to Config attributes with the same name as option name.
Typically you need only one instance of your configuration class in application.
.. code-block::
app_config: SampleConfig = SampleConfig()
Typically, your application is configured using file(s) in `configparser` format. You may
create initial one using `Config.get_config()` method.
.. note::
`Config.get_config()` works with current configuration values. When called on "empty"
instance it returns "default" configuration. Option values that match the default are
returned as commented out.
.. code-block::
>>> print(app_config.get_config())
[sample-config]
;
; Sample Config.
;
; Has three options and two sub-configs.
;
; opt_str
; -------
;
; data type: str
;
; [optional] Sample string option
;
;opt_str = <UNDEFINED>
; opt_int
; -------
;
; data type: str
;
; [optional] Sample int option
;
;opt_int = <UNDEFINED>
; enum_list
; ---------
;
; data type: list
;
; [optional] List of enum values
;
;enum_list = <UNDEFINED>
[master-db]
;
; Simple DB config
;
; database
; --------
;
; data type: str
;
; [REQUIRED] Database connection string
;
;database = <UNDEFINED>
; user
; ----
;
; data type: str
;
; [REQUIRED] User name
;
;user = SYSDBA
; password
; --------
;
; data type: str
;
; [optional] User password
;
;password = <UNDEFINED>
[backup-db]
;
; Simple DB config
;
; database
; --------
;
; data type: str
;
; [REQUIRED] Database connection string
;
;database = <UNDEFINED>
; user
; ----
;
; data type: str
;
; [REQUIRED] User name
;
;user = SYSDBA
; password
; --------
;
; data type: str
;
; [optional] User password
;
;password = <UNDEFINED>
To read the configuration from file, use the `~configparser.ConfigParser` and pass it
to `Config.load_config()` method.
Example configuration file::
; myapp.cfg
[DEFAULT]
password = masterkey
[sample-config]
opt_str = Lorem ipsum
enum_list = ready, finished, aborted
[master-db]
database = primary
user = tester
password = lockpick
[backup-db]
database = secondary
.. code-block::
from configparser import ConfigParser
cfg = ConfigParser()
cfg.read('myapp.cfg')
app_config.load_config(cfg)
Access to configuration values is through attributes on your `Config` instance, and
their `value` attribute.
.. code-block::
>>> app_config.opt_str.value
Lorem ipsum
>>> app_config.opt_int.value
>>> app_config.enum_list.value
[READY, FINISHED, ABORTED]
>>> app_config.master_db.database.value
primary
>>> app_config.master_db.user.value
tester
>>> app_config.master_db.password.value
lockpick
>>> app_config.backup_db.database.value
secondary
>>> app_config.backup_db.user.value
SYSDBA
>>> app_config.backup_db.password.value
masterkey
ConfigProto
===========
You can transfer configuration (state) between instances of your `Config` classes using
Google Protocol Buffer message `firebird.base.ConfigProto` and methods
`~Config.save_proto()` and `~Config.load_proto()`.
The protobuf message is defined in :file:`/proto/config.proto`.
.. literalinclude:: ../proto/config.proto
:language: proto
:lines: 30-
.. note::
You can use it directly or via `.protobuf` registry.
.. code-block::
# Direct use
from firebird.base.config import ConfigProto
cfg_msg = ConfigProto()
Because the proto file is NOT registered in `.protobuf` registry, you must register
it manually. The proto file is listed in `pyproject.toml` under *"firebird.base.protobuf"*
entrypoint, so use `load_registered('firebird.base.protobuf')` for its registration.
.. code-block::
from firebird.base.protobuf import load_registered, create_message
load_registered('firebird.base.protobuf')
cfg_msg = create_message('firebird.base.ConfigProto')
.. important::
Although `Option` also provides methods `~Option.save_proto()` and `~Option.load_proto()`
to transfer option value in/out ConfigProto message, you should always use methods
on `Config` instance because option's serialization may relly on `Config` instance that
owns them.
.. seealso:: `.ConfigOption`, `.ConfigListOption`
Constants
=========
.. data:: PROTO_CONFIG
:annotation: Fully qualified name for `ConfigProto`_ protobuf.
.. tip::
To address `ConfigProto`_ in functions like `~firebird.base.protobuf.create_message()`,
use `PROTO_CONFIG` constant.
Application Directory Scheme
============================
.. versionadded:: 1.1.0
.. versionchanged:: 1.2.0
.. autoclass:: DirectoryScheme
.. autoclass:: WindowsDirectoryScheme
.. autoclass:: LinuxDirectoryScheme
.. autoclass:: MacOSDirectoryScheme
.. autofunction:: get_directory_scheme
Configparser interpolation
==========================
.. autoclass:: EnvExtendedInterpolation
Config
======
.. autoclass:: Config
Options
=======
.. autoclass:: Option
.. autoclass:: StrOption
.. autoclass:: IntOption
.. autoclass:: FloatOption
.. autoclass:: DecimalOption
.. autoclass:: BoolOption
.. autoclass:: ZMQAddressOption
.. autoclass:: EnumOption
.. autoclass:: FlagOption
.. autoclass:: UUIDOption
.. autoclass:: MIMEOption
.. autoclass:: ListOption
.. autoclass:: DataclassOption
.. autoclass:: PathOption
.. autoclass:: PyExprOption
.. autoclass:: PyCodeOption
.. autoclass:: PyCallableOption
.. autoclass:: ConfigOption
.. autoclass:: ConfigListOption
Functions
=========
.. autofunction:: has_verticals
.. autofunction:: has_leading_spaces