appsettings2 0.3.0__tar.gz → 0.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {appsettings2-0.3.0 → appsettings2-0.3.1}/PKG-INFO +67 -17
- {appsettings2-0.3.0 → appsettings2-0.3.1}/README.md +66 -16
- {appsettings2-0.3.0 → appsettings2-0.3.1}/pyproject.toml +1 -1
- {appsettings2-0.3.0 → appsettings2-0.3.1}/src/__init__.py +1 -1
- {appsettings2-0.3.0 → appsettings2-0.3.1}/LICENSE +0 -0
- {appsettings2-0.3.0 → appsettings2-0.3.1}/src/Configuration.py +0 -0
- {appsettings2-0.3.0 → appsettings2-0.3.1}/src/ConfigurationBuilder.py +0 -0
- {appsettings2-0.3.0 → appsettings2-0.3.1}/src/ConfigurationException.py +0 -0
- {appsettings2-0.3.0 → appsettings2-0.3.1}/src/providers/CommandLineConfigurationProvider.py +0 -0
- {appsettings2-0.3.0 → appsettings2-0.3.1}/src/providers/ConfigurationProvider.py +0 -0
- {appsettings2-0.3.0 → appsettings2-0.3.1}/src/providers/EnvironmentConfigurationProvider.py +0 -0
- {appsettings2-0.3.0 → appsettings2-0.3.1}/src/providers/JsonConfigurationProvider.py +0 -0
- {appsettings2-0.3.0 → appsettings2-0.3.1}/src/providers/TomlConfigurationProvider.py +0 -0
- {appsettings2-0.3.0 → appsettings2-0.3.1}/src/providers/YamlConfigurationProvider.py +0 -0
- {appsettings2-0.3.0 → appsettings2-0.3.1}/src/providers/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: appsettings2
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: A python library that unifies configuration sources into a Configuration object that can be bound to complex types, or accessed directly for configuration data.
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: Shaun Wilson
|
|
@@ -55,21 +55,39 @@ Given the example above, this means that configuration data provided via Environ
|
|
|
55
55
|
|
|
56
56
|
## Accessing Configuration Data
|
|
57
57
|
|
|
58
|
-
There are
|
|
58
|
+
There are multiple ways of accessing Configuration data:
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
* Attributes attached to the `Configuration` object which represent your configuration data.
|
|
61
|
+
* Calling `get(...)` and `set(...)` methods, passing in the key of the configuration data.
|
|
62
|
+
* A dictionary-like interface exposing configuration data from an indexer.
|
|
63
|
+
* Binding the configuration data to a class/object you define.
|
|
64
|
+
|
|
65
|
+
### Direct Access via `Configuration` object
|
|
66
|
+
|
|
67
|
+
Consider the following code which demonstrates the first three methods mentioned above. All three of these methods are equivalent and return the same underlying value.
|
|
61
68
|
|
|
62
69
|
```python
|
|
70
|
+
# access `Configuration` attributes
|
|
63
71
|
value = configuration.ConnectionStrings.SampleDb
|
|
64
|
-
|
|
72
|
+
# access using `get(...)`
|
|
65
73
|
value = configuration.get('ConnectionStrings__SampleDb')
|
|
74
|
+
value = configuration.get('ConnectionStrings:SampleDb')
|
|
75
|
+
value = configuration.get('ConnectionStrings').get('SampleDb')
|
|
76
|
+
# access using indexer
|
|
77
|
+
value = configuration['ConnectionStrings__SampleDb']
|
|
78
|
+
value = configuration['ConnectionStrings:SampleDb']
|
|
79
|
+
value = configuration['ConnectionStrings']['SampleDb']
|
|
66
80
|
```
|
|
67
81
|
|
|
68
|
-
When accessing hierarchical data
|
|
82
|
+
When accessing hierarchical data by "key" you can use a double-underscore delimiter or a colon delimiter, they are equivalent.
|
|
83
|
+
|
|
84
|
+
Devs and Ops from different walks will likely prefer one form over the other, so both are supported.
|
|
69
85
|
|
|
70
|
-
|
|
86
|
+
Additionally, keys are case-insensitive (attribute names are not.)
|
|
71
87
|
|
|
72
|
-
|
|
88
|
+
### Binding `Configuration` to Objects
|
|
89
|
+
|
|
90
|
+
It's possible to bind configuration data to complex types. While some of the implementation is currently naive, it should work well for the vast majority of use cases. If you find your particular case does not work well please reach out to me and I will work with you to implement a sensible solution. Consider the following Python code:
|
|
73
91
|
|
|
74
92
|
```python
|
|
75
93
|
json = """{
|
|
@@ -82,7 +100,7 @@ json = """{
|
|
|
82
100
|
|
|
83
101
|
class ConnStrs:
|
|
84
102
|
"""An ugly class name to demonstrate the class name does not matter."""
|
|
85
|
-
|
|
103
|
+
SampleDB:str = None
|
|
86
104
|
|
|
87
105
|
class AppSettings:
|
|
88
106
|
ConnectionStrings:ConnStrs = None
|
|
@@ -95,6 +113,8 @@ configuration = ConfigurationBuilder()\
|
|
|
95
113
|
|
|
96
114
|
settings = AppSettings()
|
|
97
115
|
configuration.bind(settings)
|
|
116
|
+
|
|
117
|
+
print(settings.ConnectionStrings.SampleDB) # prints "my_cxn_string"
|
|
98
118
|
```
|
|
99
119
|
|
|
100
120
|
The resulting `settings` object will contain all of the configuration data properly typed according to type hints.
|
|
@@ -103,19 +123,28 @@ It is also possible to bind to a subset of a configuration, building upon the ab
|
|
|
103
123
|
|
|
104
124
|
```python
|
|
105
125
|
connectionStrings = configuration.bind(ConnStrs(), 'ConnectionStrings')
|
|
106
|
-
print(connectionStrings.
|
|
126
|
+
print(connectionStrings.SampleDB)
|
|
107
127
|
```
|
|
108
128
|
|
|
109
|
-
|
|
129
|
+
Lastly, a cautious eye may have noticed that the input configuration and class definition have a casing difference. `SampleDb` vs `SampleDB` -- by design binding is case-insensitive. This ensures that automation systems which can only communicate in upper-case can still support developers that prefer to have readable code ... without burdening the developers with extra work.
|
|
130
|
+
|
|
131
|
+
### Accessing `Configuration` Dictionary-like
|
|
110
132
|
|
|
111
|
-
`Configuration` is dict-like
|
|
133
|
+
`Configuration` is dict-like and in most cases can be used as if it were a dict where strict type checks would not otherwise prevent it.
|
|
112
134
|
|
|
113
|
-
|
|
135
|
+
### Transform `Configuration` to Dictionary
|
|
136
|
+
|
|
137
|
+
You can transform a `Configuration` instance into a dictionary (as a copy.) If you have some chunk of code that can consume a dictionary but can't consume a Python object (it happens) then you can get at a proper dictionary instance as follows:
|
|
114
138
|
|
|
115
139
|
```python
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
140
|
+
configuration = builder.build()
|
|
141
|
+
print(
|
|
142
|
+
json.dumps(
|
|
143
|
+
configuration.toDictionary()
|
|
144
|
+
)
|
|
145
|
+
)
|
|
146
|
+
# or, this short-hand for the same
|
|
147
|
+
print(configuration)
|
|
119
148
|
```
|
|
120
149
|
|
|
121
150
|
## Providers
|
|
@@ -156,7 +185,7 @@ It's also possible to use a colon in lieu of a double_underscore, but this may h
|
|
|
156
185
|
'ConnectionStrings:SampleDb=my_connection_string'
|
|
157
186
|
```
|
|
158
187
|
|
|
159
|
-
This provider was implemented in a way that it does not interfere with libraries such as `argparse`, and should work as expected with a well-formed command-line interface.
|
|
188
|
+
This provider was implemented in a way that it does not interfere with libraries such as `argparse`, and should work as expected with a well-formed command-line interface. An explicit goal of this provider was to not depend on a CLI library at all.
|
|
160
189
|
|
|
161
190
|
### Environment Variables
|
|
162
191
|
|
|
@@ -215,6 +244,23 @@ The `TomlConfigurationProvider` should be functionally identical to the JSON pro
|
|
|
215
244
|
|
|
216
245
|
The `YamlConfigurationProvider` should be functionally identical to the JSON and TOML providers, with the obvious difference that it loads Configuration data from a yaml file.
|
|
217
246
|
|
|
247
|
+
### Custom Provider Development
|
|
248
|
+
|
|
249
|
+
You can implement custom Configuration Providers by subclassing `ConfigurationProvider` and implementing `populateConfiguration(...)`.
|
|
250
|
+
|
|
251
|
+
For a peek at the simplicity of provider implementation, this is `ConfigurationProvider`:
|
|
252
|
+
|
|
253
|
+
```python
|
|
254
|
+
class ConfigurationProvider(abstract):
|
|
255
|
+
|
|
256
|
+
@abstractmethod
|
|
257
|
+
def populateConfiguration(self, configuration:Configuration) -> None:
|
|
258
|
+
"""The ConfigurationProvider will populate the provided Configuration instance."""
|
|
259
|
+
pass
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Essentially, you read your configuration source and write the configuration data into the specified `Configuration` object. Much of the complexity in dealing with hierarchy and allocation is encapsulated within the impl of `Configuration`. As a result, most providers are less than 20 lines of functional code.
|
|
263
|
+
|
|
218
264
|
## Special Attribute Name Handling
|
|
219
265
|
|
|
220
266
|
### Upper-Case Attribute Names
|
|
@@ -280,7 +326,7 @@ Although attribute names are transformed, keys are not. Therefore, given the abo
|
|
|
280
326
|
|
|
281
327
|
```python
|
|
282
328
|
# access via object attributes
|
|
283
|
-
configuration.query_tab.
|
|
329
|
+
configuration.query_tab.fragment_left.input
|
|
284
330
|
# access using indexers
|
|
285
331
|
configuration['query-tab']['fragment#left']
|
|
286
332
|
# access using other 'key' methods
|
|
@@ -302,3 +348,7 @@ Attempting to access these values will result in a value of `False` being return
|
|
|
302
348
|
|
|
303
349
|
If you made it this far, this library is probably what you were looking for. I'm certain there are other libraries which accomplish much of what I am attempting to accomplish here, but I wanted something I can directly support over the coming years. That, and the nearest equivalent to what I wanted fell out of active development nearly a decade ago and I just didn't feel comfortable adopting it for new work.
|
|
304
350
|
|
|
351
|
+
## Contact
|
|
352
|
+
|
|
353
|
+
You can reach me on [Discord](https://discordapp.com/users/307684202080501761) or [open an Issue on Github](https://github.com/wilson0x4d/appsettings2/issues/new/choose).
|
|
354
|
+
|
|
@@ -34,21 +34,39 @@ Given the example above, this means that configuration data provided via Environ
|
|
|
34
34
|
|
|
35
35
|
## Accessing Configuration Data
|
|
36
36
|
|
|
37
|
-
There are
|
|
37
|
+
There are multiple ways of accessing Configuration data:
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
* Attributes attached to the `Configuration` object which represent your configuration data.
|
|
40
|
+
* Calling `get(...)` and `set(...)` methods, passing in the key of the configuration data.
|
|
41
|
+
* A dictionary-like interface exposing configuration data from an indexer.
|
|
42
|
+
* Binding the configuration data to a class/object you define.
|
|
43
|
+
|
|
44
|
+
### Direct Access via `Configuration` object
|
|
45
|
+
|
|
46
|
+
Consider the following code which demonstrates the first three methods mentioned above. All three of these methods are equivalent and return the same underlying value.
|
|
40
47
|
|
|
41
48
|
```python
|
|
49
|
+
# access `Configuration` attributes
|
|
42
50
|
value = configuration.ConnectionStrings.SampleDb
|
|
43
|
-
|
|
51
|
+
# access using `get(...)`
|
|
44
52
|
value = configuration.get('ConnectionStrings__SampleDb')
|
|
53
|
+
value = configuration.get('ConnectionStrings:SampleDb')
|
|
54
|
+
value = configuration.get('ConnectionStrings').get('SampleDb')
|
|
55
|
+
# access using indexer
|
|
56
|
+
value = configuration['ConnectionStrings__SampleDb']
|
|
57
|
+
value = configuration['ConnectionStrings:SampleDb']
|
|
58
|
+
value = configuration['ConnectionStrings']['SampleDb']
|
|
45
59
|
```
|
|
46
60
|
|
|
47
|
-
When accessing hierarchical data
|
|
61
|
+
When accessing hierarchical data by "key" you can use a double-underscore delimiter or a colon delimiter, they are equivalent.
|
|
62
|
+
|
|
63
|
+
Devs and Ops from different walks will likely prefer one form over the other, so both are supported.
|
|
48
64
|
|
|
49
|
-
|
|
65
|
+
Additionally, keys are case-insensitive (attribute names are not.)
|
|
50
66
|
|
|
51
|
-
|
|
67
|
+
### Binding `Configuration` to Objects
|
|
68
|
+
|
|
69
|
+
It's possible to bind configuration data to complex types. While some of the implementation is currently naive, it should work well for the vast majority of use cases. If you find your particular case does not work well please reach out to me and I will work with you to implement a sensible solution. Consider the following Python code:
|
|
52
70
|
|
|
53
71
|
```python
|
|
54
72
|
json = """{
|
|
@@ -61,7 +79,7 @@ json = """{
|
|
|
61
79
|
|
|
62
80
|
class ConnStrs:
|
|
63
81
|
"""An ugly class name to demonstrate the class name does not matter."""
|
|
64
|
-
|
|
82
|
+
SampleDB:str = None
|
|
65
83
|
|
|
66
84
|
class AppSettings:
|
|
67
85
|
ConnectionStrings:ConnStrs = None
|
|
@@ -74,6 +92,8 @@ configuration = ConfigurationBuilder()\
|
|
|
74
92
|
|
|
75
93
|
settings = AppSettings()
|
|
76
94
|
configuration.bind(settings)
|
|
95
|
+
|
|
96
|
+
print(settings.ConnectionStrings.SampleDB) # prints "my_cxn_string"
|
|
77
97
|
```
|
|
78
98
|
|
|
79
99
|
The resulting `settings` object will contain all of the configuration data properly typed according to type hints.
|
|
@@ -82,19 +102,28 @@ It is also possible to bind to a subset of a configuration, building upon the ab
|
|
|
82
102
|
|
|
83
103
|
```python
|
|
84
104
|
connectionStrings = configuration.bind(ConnStrs(), 'ConnectionStrings')
|
|
85
|
-
print(connectionStrings.
|
|
105
|
+
print(connectionStrings.SampleDB)
|
|
86
106
|
```
|
|
87
107
|
|
|
88
|
-
|
|
108
|
+
Lastly, a cautious eye may have noticed that the input configuration and class definition have a casing difference. `SampleDb` vs `SampleDB` -- by design binding is case-insensitive. This ensures that automation systems which can only communicate in upper-case can still support developers that prefer to have readable code ... without burdening the developers with extra work.
|
|
109
|
+
|
|
110
|
+
### Accessing `Configuration` Dictionary-like
|
|
89
111
|
|
|
90
|
-
`Configuration` is dict-like
|
|
112
|
+
`Configuration` is dict-like and in most cases can be used as if it were a dict where strict type checks would not otherwise prevent it.
|
|
91
113
|
|
|
92
|
-
|
|
114
|
+
### Transform `Configuration` to Dictionary
|
|
115
|
+
|
|
116
|
+
You can transform a `Configuration` instance into a dictionary (as a copy.) If you have some chunk of code that can consume a dictionary but can't consume a Python object (it happens) then you can get at a proper dictionary instance as follows:
|
|
93
117
|
|
|
94
118
|
```python
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
119
|
+
configuration = builder.build()
|
|
120
|
+
print(
|
|
121
|
+
json.dumps(
|
|
122
|
+
configuration.toDictionary()
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
# or, this short-hand for the same
|
|
126
|
+
print(configuration)
|
|
98
127
|
```
|
|
99
128
|
|
|
100
129
|
## Providers
|
|
@@ -135,7 +164,7 @@ It's also possible to use a colon in lieu of a double_underscore, but this may h
|
|
|
135
164
|
'ConnectionStrings:SampleDb=my_connection_string'
|
|
136
165
|
```
|
|
137
166
|
|
|
138
|
-
This provider was implemented in a way that it does not interfere with libraries such as `argparse`, and should work as expected with a well-formed command-line interface.
|
|
167
|
+
This provider was implemented in a way that it does not interfere with libraries such as `argparse`, and should work as expected with a well-formed command-line interface. An explicit goal of this provider was to not depend on a CLI library at all.
|
|
139
168
|
|
|
140
169
|
### Environment Variables
|
|
141
170
|
|
|
@@ -194,6 +223,23 @@ The `TomlConfigurationProvider` should be functionally identical to the JSON pro
|
|
|
194
223
|
|
|
195
224
|
The `YamlConfigurationProvider` should be functionally identical to the JSON and TOML providers, with the obvious difference that it loads Configuration data from a yaml file.
|
|
196
225
|
|
|
226
|
+
### Custom Provider Development
|
|
227
|
+
|
|
228
|
+
You can implement custom Configuration Providers by subclassing `ConfigurationProvider` and implementing `populateConfiguration(...)`.
|
|
229
|
+
|
|
230
|
+
For a peek at the simplicity of provider implementation, this is `ConfigurationProvider`:
|
|
231
|
+
|
|
232
|
+
```python
|
|
233
|
+
class ConfigurationProvider(abstract):
|
|
234
|
+
|
|
235
|
+
@abstractmethod
|
|
236
|
+
def populateConfiguration(self, configuration:Configuration) -> None:
|
|
237
|
+
"""The ConfigurationProvider will populate the provided Configuration instance."""
|
|
238
|
+
pass
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Essentially, you read your configuration source and write the configuration data into the specified `Configuration` object. Much of the complexity in dealing with hierarchy and allocation is encapsulated within the impl of `Configuration`. As a result, most providers are less than 20 lines of functional code.
|
|
242
|
+
|
|
197
243
|
## Special Attribute Name Handling
|
|
198
244
|
|
|
199
245
|
### Upper-Case Attribute Names
|
|
@@ -259,7 +305,7 @@ Although attribute names are transformed, keys are not. Therefore, given the abo
|
|
|
259
305
|
|
|
260
306
|
```python
|
|
261
307
|
# access via object attributes
|
|
262
|
-
configuration.query_tab.
|
|
308
|
+
configuration.query_tab.fragment_left.input
|
|
263
309
|
# access using indexers
|
|
264
310
|
configuration['query-tab']['fragment#left']
|
|
265
311
|
# access using other 'key' methods
|
|
@@ -280,3 +326,7 @@ Attempting to access these values will result in a value of `False` being return
|
|
|
280
326
|
## Conclusion
|
|
281
327
|
|
|
282
328
|
If you made it this far, this library is probably what you were looking for. I'm certain there are other libraries which accomplish much of what I am attempting to accomplish here, but I wanted something I can directly support over the coming years. That, and the nearest equivalent to what I wanted fell out of active development nearly a decade ago and I just didn't feel comfortable adopting it for new work.
|
|
329
|
+
|
|
330
|
+
## Contact
|
|
331
|
+
|
|
332
|
+
You can reach me on [Discord](https://discordapp.com/users/307684202080501761) or [open an Issue on Github](https://github.com/wilson0x4d/appsettings2/issues/new/choose).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "appsettings2"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.1"
|
|
4
4
|
description = "A python library that unifies configuration sources into a Configuration object that can be bound to complex types, or accessed directly for configuration data."
|
|
5
5
|
authors = ["Shaun Wilson <mrshaunwilson@msn.com>"]
|
|
6
6
|
license = "MIT"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|