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',
24 """We start with the system files, and work our way to the more
25 specific user files, so the user can override the sysadmin who
26 in turn overrides the developer defaults.
29 class Setting (object):
30 """An entry (section or option) in HookeConfigParser.
32 def __init__(self, section, option=None, value=None, help=None, wrap=True):
33 self.section = section
40 return self.option == None
43 return not self.is_section()
45 def write(self, fp, value=None, comments=True, wrapper=None):
46 if comments == True and self.help != None:
50 wrapper = textwrap.TextWrapper(
52 subsequent_indent="# ")
53 text = wrapper.fill(text)
54 fp.write(text.rstrip()+'\n')
56 fp.write("[%s]\n" % self.section)
58 fp.write("%s = %s\n" % (self.option,
59 str(value).replace('\n', '\n\t')))
62 Setting('display', help='Control display appearance: colour, ???, etc.'),
63 Setting('display', 'colour_ext', 'None', help=None),
64 Setting('display', 'colour_ret', 'None', help=None),
65 Setting('display', 'ext', '1', help=None),
66 Setting('display', 'ret', '1', help=None),
68 Setting('display', 'correct', '1', help=None),
69 Setting('display', 'colout_correct', 'None', help=None),
70 Setting('display', 'contact_point', '0', help=None),
71 Setting('display', 'medfilt', '0', help=None),
73 Setting('display', 'xaxes', '0', help=None),
74 Setting('display', 'yaxes', '0', help=None),
75 Setting('display', 'flatten', '1', help=None),
77 Setting('conditions', 'temperature', '301', help=None),
79 Setting('fitting', 'auto_fit_points', '50', help=None),
80 Setting('fitting', 'auto_slope_span', '20', help=None),
81 Setting('fitting', 'auto_delta_force', '1-', help=None),
82 Setting('fitting', 'auto_fit_nm', '5', help=None),
83 Setting('fitting', 'auto_min_p', '0.005', help=None),
84 Setting('fitting', 'auto_max_p', '10', help=None),
86 Setting('?', 'baseline_clicks', '0', help=None),
87 Setting('fitting', 'auto_left_baseline', '20', help=None),
88 Setting('fitting', 'auto_right_baseline', '20', help=None),
89 Setting('fitting', 'force_multiplier', '1', help=None),
91 Setting('display', 'fc_showphase', '0', help=None),
92 Setting('display', 'fc_showimposed', '0', help=None),
93 Setting('display', 'fc_interesting', '0', help=None),
94 Setting('?', 'tccd_threshold', '0', help=None),
95 Setting('?', 'tccd_coincident', '0', help=None),
96 Setting('display', '', '', help=None),
97 Setting('display', '', '', help=None),
99 Setting('filesystem', 'filterindex', '0', help=None),
100 Setting('filesystem', 'filters',
101 "Playlist files (*.hkp)|*.hkp|Text files (*.txt)|*.txt|All files (*.*)|*.*')",
103 Setting('filesystem', 'workdir', 'test',
104 help='\n'.join(['# Substitute your work directory',
105 '#workdir = D:\hooke']),
107 Setting('filesystem', 'playlist', 'test.hkp', help=None),
110 def get_setting(settings, match):
111 """Return the first Setting object matching both match.section and
115 if s.section == match.section and s.option == match.option:
119 class HookeConfigParser (configparser.SafeConfigParser):
120 """A wrapper around configparser.SafeConfigParser.
122 You will probably only need .read and .write.
128 >>> c = HookeConfigParser(paths=DEFAULT_PATHS,
129 ... default_settings=DEFAULT_SETTINGS)
130 >>> c.write(sys.stdout) # doctest: +ELLIPSIS
131 # Control display appearance: colour, ???, etc.
137 def __init__(self, paths=None, default_settings=None, defaults=None,
138 dict_type=OrderedDict, indent='# ', **kwargs):
139 # Can't use super() because SafeConfigParser is a classic class
140 #super(HookeConfigParser, self).__init__(defaults, dict_type)
141 configparser.SafeConfigParser.__init__(self, defaults, dict_type)
144 self._config_paths = paths
145 if default_settings == None:
146 default_settings = []
147 self._default_settings = default_settings
148 for key in ['initial_indent', 'subsequent_indent']:
149 if key not in kwargs:
151 self._comment_textwrap = textwrap.TextWrapper(**kwargs)
152 self._setup_default_settings()
154 def _setup_default_settings(self):
155 for setting in self._default_settings: #reversed(self._default_settings):
156 # reversed cause: http://docs.python.org/library/configparser.html
157 # "When adding sections or items, add them in the reverse order of
158 # how you want them to be displayed in the actual file."
159 if setting.section not in self.sections():
160 self.add_section(setting.section)
161 if setting.option != None:
162 self.set(setting.section, setting.option, setting.value)
164 def read(self, filenames=None):
165 """Read and parse a filename or a list of filenames.
167 If filenames is None, it defaults to ._config_paths. If a
168 filename is not in ._config_paths, it gets appended to the
169 list. We also run os.path.expanduser() on the input filenames
170 internally so you don't have to worry about it.
172 Files that cannot be opened are silently ignored; this is
173 designed so that you can specify a list of potential
174 configuration file locations (e.g. current directory, user's
175 home directory, systemwide directory), and all existing
176 configuration files in the list will be read. A single
177 filename may also be given.
179 Return list of successfully read files.
181 if filenames == None:
182 filenames = [os.path.expanduser(p) for p in self._config_paths]
184 if isinstance(filenames, basestring):
185 filenames = [filenames]
186 paths = [os.path.abspath(os.path.expanduser(p))
187 for p in self._config_paths]
188 for filename in filenames:
189 if os.path.abspath(os.path.expanduser(filename)) not in paths:
190 self._config_paths.append(filename)
191 # Can't use super() because SafeConfigParser is a classic class
192 #return super(HookeConfigParser, self).read(filenames)
193 return configparser.SafeConfigParser.read(self, filenames)
195 def _write_setting(self, fp, section=None, option=None, value=None,
197 """Print, if known, a nicely wrapped comment form of a
200 match = get_setting(self._default_settings, Setting(section, option))
202 match = Setting(section, option, value, **kwargs)
203 match.write(fp, value=value, wrapper=self._comment_textwrap)
205 def write(self, fp=None, comments=True):
206 """Write an .ini-format representation of the configuration state.
208 This expands on RawConfigParser.write() by optionally adding
209 comments when they are known (i.e. for ._default_settings).
210 However, comments are not read in during a read, so .read(x)
211 .write(x) may `not preserve comments`_.
213 .. _not preserve comments: http://bugs.python.org/issue1410680
218 >>> import sys, StringIO
219 >>> c = HookeConfigParser()
226 >>> c._read(StringIO.StringIO(instring), 'example.cfg')
227 >>> c.write(sys.stdout)
232 local_fp = fp == None
234 fp = open(os.path.expanduser(self._config_paths[-1]), 'w')
237 self._write_setting(fp, configparser.DEFAULTSECT,
238 help="Miscellaneous options")
239 for (key, value) in self._defaults.items():
240 self._write_setting(fp, configparser.DEFAULTSECT, key, value)
242 for section in self._sections:
243 self._write_setting(fp, section)
244 for (key, value) in self._sections[section].items():
245 if key != "__name__":
246 self._write_setting(fp, section, key, value)