2 # -*- coding: utf-8 -*-
7 Configuration defaults, read/write, and template file creation for Hooke.
12 import ConfigParser as configparser
16 from .compat.odict import odict as OrderedDict
19 '/usr/share/hooke/hooke.cfg',
20 '/etc/hooke/hooke.cfg',
23 """We start with the system files, and work our way to the more
24 specific user files, so the user can override the sysadmin who
25 in turn overrides the developer defaults.
28 class Setting (object):
29 """An entry (section or option) in HookeConfigParser.
31 def __init__(self, section, option=None, value=None, help=None, wrap=True):
32 self.section = section
39 return self.option == None
42 return not self.is_section()
44 def write(self, fp, value=None, comments=True, wrapper=None):
45 if comments == True and self.help != None:
49 wrapper = textwrap.TextWrapper(
51 subsequent_indent="# ")
52 text = wrapper.fill(text)
53 fp.write(text.rstrip()+'\n')
55 fp.write("[%s]\n" % self.section)
57 fp.write("%s = %s\n" % (self.option,
58 str(value).replace('\n', '\n\t')))
61 Setting('display', help='Control display appearance: colour, ???, etc.'),
62 Setting('display', 'colour_ext', 'None', help=None),
63 Setting('display', 'colour_ret', 'None', help=None),
64 Setting('display', 'ext', '1', help=None),
65 Setting('display', 'ret', '1', help=None),
67 Setting('display', 'correct', '1', help=None),
68 Setting('display', 'colout_correct', 'None', help=None),
69 Setting('display', 'contact_point', '0', help=None),
70 Setting('display', 'medfilt', '0', help=None),
72 Setting('display', 'xaxes', '0', help=None),
73 Setting('display', 'yaxes', '0', help=None),
74 Setting('display', 'flatten', '1', help=None),
76 Setting('conditions', 'temperature', '301', help=None),
78 Setting('fitting', 'auto_fit_points', '50', help=None),
79 Setting('fitting', 'auto_slope_span', '20', help=None),
80 Setting('fitting', 'auto_delta_force', '1-', help=None),
81 Setting('fitting', 'auto_fit_nm', '5', help=None),
82 Setting('fitting', 'auto_min_p', '0.005', help=None),
83 Setting('fitting', 'auto_max_p', '10', help=None),
85 Setting('?', 'baseline_clicks', '0', help=None),
86 Setting('fitting', 'auto_left_baseline', '20', help=None),
87 Setting('fitting', 'auto_right_baseline', '20', help=None),
88 Setting('fitting', 'force_multiplier', '1', help=None),
90 Setting('display', 'fc_showphase', '0', help=None),
91 Setting('display', 'fc_showimposed', '0', help=None),
92 Setting('display', 'fc_interesting', '0', help=None),
93 Setting('?', 'tccd_threshold', '0', help=None),
94 Setting('?', 'tccd_coincident', '0', help=None),
95 Setting('display', '', '', help=None),
96 Setting('display', '', '', help=None),
98 Setting('filesystem', 'filterindex', '0', help=None),
99 Setting('filesystem', 'filters',
100 "Playlist files (*.hkp)|*.hkp|Text files (*.txt)|*.txt|All files (*.*)|*.*')",
102 Setting('filesystem', 'workdir', 'test',
103 help='\n'.join(['# Substitute your work directory',
104 '#workdir = D:\hooke']),
106 Setting('filesystem', 'playlist', 'test.hkp', help=None),
109 def get_setting(settings, match):
110 """Return the first Setting object matching both match.section and
114 if s.section == match.section and s.option == match.option:
118 class HookeConfigParser (configparser.SafeConfigParser):
119 """A wrapper around configparser.SafeConfigParser.
121 You will probably only need .read and .write.
127 >>> c = HookeConfigParser(paths=DEFAULT_PATHS,
128 ... default_settings=DEFAULT_SETTINGS)
129 >>> c.write(sys.stdout) # doctest: +ELLIPSIS
130 # Control display appearance: colour, ???, etc.
136 def __init__(self, paths=None, default_settings=None, defaults=None,
137 dict_type=OrderedDict, indent='# ', **kwargs):
138 # Can't use super() because SafeConfigParser is a classic class
139 #super(HookeConfigParser, self).__init__(defaults, dict_type)
140 configparser.SafeConfigParser.__init__(self, defaults, dict_type)
143 self._config_paths = paths
144 if default_settings == None:
145 default_settings = []
146 self._default_settings = default_settings
147 for key in ['initial_indent', 'subsequent_indent']:
148 if key not in kwargs:
150 self._comment_textwrap = textwrap.TextWrapper(**kwargs)
151 self._setup_default_settings()
153 def _setup_default_settings(self):
154 for setting in self._default_settings: #reversed(self._default_settings):
155 # reversed cause: http://docs.python.org/library/configparser.html
156 # "When adding sections or items, add them in the reverse order of
157 # how you want them to be displayed in the actual file."
158 if setting.section not in self.sections():
159 self.add_section(setting.section)
160 if setting.option != None:
161 self.set(setting.section, setting.option, setting.value)
163 def read(self, filenames=None):
164 """Read and parse a filename or a list of filenames.
166 If filenames is None, it defaults to ._config_paths. If a
167 filename is not in ._config_paths, it gets appended to the
168 list. We also run os.path.expanduser() on the input filenames
169 internally so you don't have to worry about it.
171 Files that cannot be opened are silently ignored; this is
172 designed so that you can specify a list of potential
173 configuration file locations (e.g. current directory, user's
174 home directory, systemwide directory), and all existing
175 configuration files in the list will be read. A single
176 filename may also be given.
178 Return list of successfully read files.
180 if filenames == None:
181 filenames = [os.path.expanduser(p) for p in self._config_paths]
183 if isinstance(filenames, basestring):
184 filenames = [filenames]
185 paths = [os.path.abspath(os.path.expanduser(p))
186 for p in self._config_paths]
187 for filename in filenames:
188 if os.path.abspath(os.path.expanduser(filename)) not in paths:
189 self._config_paths.append(filename)
190 # Can't use super() because SafeConfigParser is a classic class
191 #return super(HookeConfigParser, self).read(filenames)
192 return configparser.SafeConfigParser.read(self, filenames)
194 def _write_setting(self, fp, section=None, option=None, value=None,
196 """Print, if known, a nicely wrapped comment form of a
199 match = get_setting(self._default_settings, Setting(section, option))
201 match = Setting(section, option, value, **kwargs)
202 match.write(fp, value=value, wrapper=self._comment_textwrap)
204 def write(self, fp=None, comments=True):
205 """Write an .ini-format representation of the configuration state.
207 This expands on RawConfigParser.write() by optionally adding
208 comments when they are known (i.e. for ._default_settings).
209 However, comments are not read in during a read, so .read(x)
210 .write(x) may `not preserve comments`_.
212 .. _not preserve comments: http://bugs.python.org/issue1410680
217 >>> import sys, StringIO
218 >>> c = HookeConfigParser()
225 >>> c._read(StringIO.StringIO(instring), 'example.cfg')
226 >>> c.write(sys.stdout)
231 local_fp = fp == None
233 fp = open(self._config_paths[-1], 'w')
236 self._write_setting(fp, configparser.DEFAULTSECT,
237 help="Miscellaneous options")
238 for (key, value) in self._defaults.items():
239 self._write_setting(fp, configparser.DEFAULTSECT, key, value)
241 for section in self._sections:
242 self._write_setting(fp, section)
243 for (key, value) in self._sections[section].items():
244 if key != "__name__":
245 self._write_setting(fp, section, key, value)