I have created a .ini-like file with all the values which I need later in my program, see below:
[debugging]
checkForAbort = 10
...
[official]
checkForAbort = 30
...
I would like to read all these items into a single class and make it accessible from other parts of my python project. What I have so far is the code below:
from ConfigParser import SafeConfigParser
import glob
class ConfigurationParameters
def __init__(self):
self.checkForAbortDuringIdleTime = None
parser = SafeConfigParser()
# making a list here in case we have multiple files in the future!
candidatesConfiguration = ['my.cfg']
foundCandidates = parser.read(candidatesConfiguration)
missingCandidates = set(candidatesConfiguration) - set(found)
if foundCandidates:
print 'Found config files:', sorted(found)
else
print 'Missing files :', sorted(missing)
print "aborting..."
# check for mandatory sections below
for candidateSections in ['official', 'debugging']:
if not parser.has_section(candidateSections)
print "the mandatory section", candidateSections " is missing"
print "aborting..."
for sectionName in ['official', 'debugging']:
for name, value in parser.items(section_name):
self.name = value
I am new to python but I can still see lots of problem with my code:
- I am forced to add a attribute for each item in my class file. and keep the configuration file and my class in sync all the time.
- This class is not a singleton, therefore the reading/parsing will be done from wherever it is imported!
- If a value is added to the config-file with is not defined in my class, it will probably crash!
How should I solve this problem instead? Can class attributes be created dynamically?
My class only need to read from the values, so no writing to the configuration file is needed!
Also, you could do it like Alex Martelli does in his Bunch class:
file MyConfig.py
:
from ConfigParser import SafeConfigParser
section_names = 'official', 'debugging'
class MyConfiguration(object):
def __init__(self, *file_names):
parser = SafeConfigParser()
parser.optionxform = str # make option names case sensitive
found = parser.read(file_names)
if not found:
raise ValueError('No config file found!')
for name in section_names:
self.__dict__.update(parser.items(name)) # <-- here the magic happens
config = MyConfiguration('my.cfg', 'other.cfg')
file foo.py
:
from MyConfig import config
# ...
file MyProgram.py
:
from MyConfig import config
print config.checkForAbort
import foo
assert config is foo.config
The Python Language Reference states that "Import statements are executed in two steps: (1) find a module, and initialize it if necessary; (2) define a name or names in the local namespace (of the scope where the import statement occurs)."
What that means is that, when a module gets imported, one or more local names are bound to a module object, and only the first time it is imported during the runtime of a Python program it gets initialized (i.e. read from file and run).
In the code above the name config
is just a local name that refers to an attribute of a module object. The module object has been initialized by the Python interpreter when it was referenced (via from MyConfig import config
) in MyProgram
. When MyProgram
imports foo
it is already initialized and gets bound to a local name in module foo
and in MyProgram
we can refer to it as foo.config
. But both names refer to the very same object.
self.name = value
doesn't work as you expect. You might mean setattr(self, name, value)
It creates instance attributes dynamicallly.
To make it a singleton you could make the instance to be a global variable in config
/settings
module. Initialize it once on a program startup e.g., like the logging
module does it for the root logger: logging.config.fileConfig('logging.conf')
.
It is a common desire to use an attribute access for what is essentially a dict e.g., argparse.Namespace
that carries command-line options.
You could access the config later by importing it: from mypackage.config import config
, e.g., mypackage/config.py
:
class Config(object):
...
config = Config() # empty or default config without any configuration
In mypackage/__main__.py
:
from .config import config
...
# initialization code
config.update_from_file('mypackage.conf') # it might call `setattr()` inside
Note: setattr()
works correctly even you set properties. __dict__.update()
breaks in this case:
class XValidatorMixin(object):
@property
def x(self):
return self._x
@x.setter
def x(self, value):
if value < 0:
raise ValueError
self._x = value
class CUpdate(XValidatorMixin):
def __init__(self, **options):
self.__dict__.update(options)
class CSetAttr(XValidatorMixin):
def __init__(self, **options):
for name, value in options.items():
setattr(self, name, value)
for C in [CUpdate, CSetAttr]:
o = C(a=1, b=2) # non-property attributes work as expected
assert o.a == 1 and o.b == 2
o = CSetAttr(x=1)
assert o.x == 1 # setattr also sets property
o = CUpdate(x=1)
try:
o.x # .update() doesn't set property
except AttributeError:
pass
else:
assert 0
try:
o = CSetAttr(x=-1) # invokes property setter
except ValueError: # that correctly reject negative values
pass
else:
assert 0
The Answer by pillmuncher is very helpful and can be easily adapted to use within an existing class. Furthermore, it is also possible to automatically convert the data types using the localconfig module (link). To get these additional functionalities you can use something like:
from localconfig import LocalConfig
configfile = 'config.ini'
config = LocalConfig(configfile)
sections = list(config)
for section in sections:
items = list(config.items(section))
CLASS.__dict__.update(items)
来源:https://stackoverflow.com/questions/12620602/creating-a-class-with-all-the-elements-specified-in-a-file-using-configparser