The idea for this comes from ORMs like SqlAlchemy and how Spring Boot uses @ConfigurationProperties
Library Version | Python |
---|---|
1.x.x | 2.7, 3.5+ |
0.x (Beta) | 2.7, 3.5+ |
Zeno maps your configs to objects for you.
Spring:
Data:
MongoDb:
database: TESTDB
encryption: true
encryptionKey: "FakePassWord!"
password: "!54353Ffesf34"
replicaSet: FAKE-DB-531
second: 1
myList:
- first
- second
- third
MyServer:
host: my.server.com
port: 8080
Looks like
class Spring(Configuration): #Inheriting from Configuration lets the parser know which class to modify
"""
loads in from data.yml. accessing nested sections can be done via nested classes
"""
class Data:
class MongoDb:
database = String()
encryption = Boolean() # conversion automatically happens when specifying the type
encryptionKey = String()
password = String()
replicaSet = String()
second = Integer()
myList = List()
And can now be accessed like:
spring = Spring()
spring.Data.myList # ['first', 'second', 'third']
spring.Data.MongoDb.encryption is True # True
Classes give you autocompletion in your favorite IDE for all your config files
class MyServer(Configuration):
host = String()
port = Integer()
Classes are a powerful way for autocompletion, type hints and auto conversion to those types. However, if it isn't for you, calling Zeno directly can be done. The parameter to the constructor is the path within the dictionary. If no parameter is set, it will map the whole dictionary. More examples can be found here
from ZenoMapper.zeno import Zeno
zeno = Zeno('Spring.Data.MongoDb')
zeno.database # TESTDB
pip install ZenoMapper
from ZenoMapper.Configuration import ConfigParser, Configuration
from ZenoMapper.Types import String, Boolean, Integer, List
class MyConfig(Configuration):
__section__ = 'Spring.Data.Mongodb'
database = String()
encryption = Boolean()
encryptionKey = String()
password = String()
replicaSet = String()
Using your own configs is easy with Zeno, simply inherit from ConfigParser and instantiate the get_config()
function. Load in your file and Zeno will do the rest.
class MyConfig(ConfigParser):
"""
loading your own config is done via subclassing the ConfigParser class and implementing the
get_config function.
each time an object is instantiated, this is called, so cache the results to increase performance with the @lru_cache decorator
"""
@staticmethod
@lru_cache(maxsize=None) # @cache if you're on python3.9 or later
def get_config():
with open("data.yml", 'r') as stream:
return yaml.safe_load(stream)
Zeno currently has 4 types, where auto conversion will happen based on the specified type. It also brings a little static typing to Python. The plan is to add more, but currently Zeno supports: Integer
, String
, Boolean
, List
. Supported types can be found here
If you have another type you'd like but it isn't supported, etc. None
can be used to tell Zeno to not handle conversion
Users can specify their own types by inheriting from ConfigTypes
class AppConf(ConfigTypes):
"""
AppConf class to be used with Configuration class
"""
def __init__(self, name, to_type):
self.name = name
self.convert_to_type = to_type
def convert(self, obj):
"""Method called to convert obj to its specified convert type
NOTE: if the environment is production, it will first get it from appconf
and override the one in the config file
Required Args:
obj (obj) - The object to be converted
returned:
obj - The object as the specified type
"""
if os.environ.get('ENVIRONMENT') == 'production':
# we are in production and using appconf to get our values
obj = parse_appconf()[self.name]
# now convert it to the specified to_type
return self.convert_to_type().convert(obj)
and then can be called like the other types
class Database(Configuration):
"""section for database"""
__project__ = 'my_project'
dbname = String()
# you can even send in another type to do a later conversion within your new type
host = AppConf('host', String)
port = AppConf('port', Integer)
Zeno is powerful. Only specify what you'd like to map and Zeno ignores the rest
class Spring(Configuration):
class Data:
class MongoDb:
database = String()
Well then person reading this, Zeno is for you. All Classes are dictionary class hybrids, so you can access them like plain old dictionaries when necessary.
spring.Data.myList # ['first', 'second', 'third']
spring['Data']['myList'] # ['first', 'second', 'third']
spring['Data'].myList # ['first', 'second', 'third']
spring.Data['myList'] # ['first', 'second', 'third']
spring # {'Data': {'MongoDb': {'database': 'TESTDB', 'encryption': True, 'encryptionKey': 'FakePassWord!', 'password': '!54353Ffesf34', 'replicaSet': 'FAKE-DB-531'}, 'second': 1, 'myList': ['first', 'second', 'third']}}
Classes can fuzzy match while not breaking Python's class naming conventions
lower:
case_section: true
class Lower(Configuration):
CaseSection = Boolean()
- Any parser
- YAML
- INI
- etc, as long as it parses into a dictionary
- Automatic type conversion
- Custom conversion classes
- All Python 3 versions and 2.7