From ae93d779a01138c50fcfcda52920a96364296a40 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Thu, 28 Jul 2011 13:30:53 -0400 Subject: [PATCH] Convert to use new h5config package. --- README | 35 ++- pypiezo/__init__.py | 45 +-- pypiezo/afm.py | 8 +- pypiezo/base.py | 6 +- pypiezo/config.py | 696 +++----------------------------------------- pypiezo/surface.py | 1 - 6 files changed, 74 insertions(+), 717 deletions(-) diff --git a/README b/README index eedd8c5..acf6f19 100644 --- a/README +++ b/README @@ -33,17 +33,19 @@ Dependencies If you're installing by hand or packaging pypiezo for another distribution, you'll need the following dependencies: -=========== ================= ===================== -Package Debian_ Gentoo_ -=========== ================= ===================== -Numpy_ python-numpy dev-python/numpy -Scipy_ python-scipy sci-libs/scipy -H5Py_ python-h5py dev-python/h5py -Matplotlib_ python-matplotlib dev-python/matplotlib -Nose_ python-nose dev-python/nose -Pycomedi_ dev-python/pycomedi -PyYAML_ python-pyyaml dev-python/pyyaml -=========== ================= ===================== +========================== ================= ============================================= +Package Debian_ Gentoo_ +========================== ================= ============================================= +Numpy_ python-numpy dev-python/numpy +Scipy_ python-scipy sci-libs/scipy +Matplotlib_ python-matplotlib dev-python/matplotlib +H5config_ dev-python/h5config [#wtk]_ +Curses-check-for-keypress_ dev-python/curses-check-for-keypress [#wtk]_ +Nose_ python-nose dev-python/nose +Pycomedi_ dev-python/pycomedi [#wtk]_ +========================== ================= ============================================= + +.. [#wtk] In the `wtk overlay`_. Installing by hand @@ -95,12 +97,15 @@ Copyright 2007-2011 .. _Gentoo: http://www.gentoo.org/ .. _NumPy: http://numpy.scipy.org/ .. _SciPy: http://www.scipy.org/ -.. _H5Py: http://code.google.com/p/h5py/ .. _Matplotlib: http://matplotlib.sourceforge.net/ +.. _H5config: + http://physics.drexel.edu/~wking/unfolding-disasters/posts/h5config/ +.. _Curses-ckeck-for-keypress: + http://physics.drexel.edu/~wking/unfolding-disasters/posts/curses-check-for-keypress/ .. _Nose: http://somethingaboutorange.com/mrl/projects/nose/ -.. _Pycomedi: http://www.physics.drexel.edu/~wking/unfolding-disasters/posts/pycomedi/ -.. _PyYAML: http://pyyaml.org/wiki/PyYAML +.. _Pycomedi: + http://physics.drexel.edu/~wking/unfolding-disasters/posts/pycomedi/ .. _Git: http://git-scm.com/ .. _homepage: - http://www.physics.drexel.edu/~wking/unfolding-disasters/posts/pypiezo/ + http://www.physics.drexel.edu/~wking/unfolding-disasters/posts/pypiezo/ .. _GNU General Public License Version 3: http://www.gnu.org/licenses/gpl.txt diff --git a/pypiezo/__init__.py b/pypiezo/__init__.py index 30fe502..e1f8221 100644 --- a/pypiezo/__init__.py +++ b/pypiezo/__init__.py @@ -18,47 +18,10 @@ import logging as _logging import logging.handlers as _logging_handlers +from .config import PackageConfig as _PackageConfig -__version__ = '0.4' +__version__ = '0.5' -LOG = _logging.getLogger('pypiezo') -"Pypiezo logger" - -LOG.setLevel(_logging.WARN) -_formatter = _logging.Formatter( - '%(asctime)s - %(name)s - %(levelname)s - %(message)s') - -_stream_handler = _logging.StreamHandler() -_stream_handler.setLevel(_logging.DEBUG) -_stream_handler.setFormatter(_formatter) -LOG.addHandler(_stream_handler) - -_syslog_handler = None - - -from .config import _BaseConfig -from .config import find_base_config as _find_base_config - - -def setup_base_config(config): - global base_config, _syslog_handler - base_config = config - - LOG.setLevel(base_config['log-level']) - - if base_config['syslog']: - if not _syslog_handler: - _syslog_handler = _logging_handlers.SysLogHandler() - _syslog_handler.setLevel(_logging.DEBUG) - LOG.handlers = [_syslog_handler] - else: - LOG.handlers = [_stream_handler] - - LOG.info('setup base_config:\n%s' % base_config.dump()) - -def clear_base_config(): - setup_base_config(_BaseConfig()) - -base_config = _find_base_config() -setup_base_config(base_config) +package_config = _PackageConfig(package_name=__name__) +package_config.load_system() diff --git a/pypiezo/afm.py b/pypiezo/afm.py index 49adb25..5a9a6e6 100644 --- a/pypiezo/afm.py +++ b/pypiezo/afm.py @@ -30,8 +30,8 @@ except (ImportError, RuntimeError), e: from curses_check_for_keypress import CheckForKeypress as _CheckForKeypress from . import LOG as _LOG -from . import base_config as _base_config from . import base as _base +from . import package_config as _package_config from . import surface as _surface @@ -175,7 +175,7 @@ class AFMPiezo (_base.Piezo): number of 'null' steps to take before moving (confirming a stable input deflection). """ - if return_data or _base_config['matplotlib']: + if return_data or _package_config['matplotlib']: aquire_data = True else: aquire_data = False @@ -230,7 +230,7 @@ class AFMPiezo (_base.Piezo): log_string = 'current position %d and deflection %g' % ( self.last_output[axis_name], current_deflection) _LOG.debug(log_string) - if _base_config['matplotlib']: + if _package_config['matplotlib']: if not _matplotlib: raise _matplotlib_import_error figure = _matplotlib_pyplot.figure() @@ -270,7 +270,7 @@ class AFMPiezo (_base.Piezo): Modern commercial AFMs with computer-aligned lasers must do something like this automatically. """ - if _base_config['matplotlib']: + if _package_config['matplotlib']: plot = True if laser_wavelength and amplitude: log_string = \ diff --git a/pypiezo/base.py b/pypiezo/base.py index b80d727..b648a9d 100644 --- a/pypiezo/base.py +++ b/pypiezo/base.py @@ -35,8 +35,8 @@ from pycomedi.constant import TRIG_SRC, SDF from pycomedi.utility import inttrig_insn, Reader, Writer from . import LOG as _LOG -from . import base_config as _base_config from . import config as _config +from . import package_config as _package_config def convert_bits_to_volts(config, data): @@ -443,7 +443,7 @@ class Piezo (object): >>> d.close() """ - def __init__(self, axes, input_channels, base_config=None): + def __init__(self, axes, input_channels): self.axes = axes self.input_channels = input_channels self.last_output = {} @@ -635,7 +635,7 @@ class Piezo (object): for i,name in enumerate(output_names): self.last_output[name] = data[-1,i] - if _base_config['matplotlib']: + if _package_config['matplotlib']: if not _matplotlib: raise _matplotlib_import_error figure = _matplotlib_pyplot.figure() diff --git a/pypiezo/config.py b/pypiezo/config.py index 6df2460..791c2e8 100644 --- a/pypiezo/config.py +++ b/pypiezo/config.py @@ -1,717 +1,107 @@ -"""Piezo configuration +# Copyright (C) 2011 W. Trevor King +# +# This file is part of pypiezo. +# +# pypiezo is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# pypiezo is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pypiezo. If not, see . + +"Piezo configuration" -Broken out from the main modules to make it easy to override if you -wish to use a different configuration file format. - -The HDF5- and YAML-backed config file classes are created dynamically: - ->>> print '\\n'.join([obj for obj in sorted(locals().keys()) -... if obj.endswith('Config') -... and not obj.startswith('_')]) -HDF5_AxisConfig -HDF5_BaseConfig -HDF5_ChannelConfig -HDF5_InputChannelConfig -HDF5_OutputChannelConfig -YAML_AxisConfig -YAML_BaseConfig -YAML_ChannelConfig -YAML_InputChannelConfig -YAML_OutputChannelConfig - -The first time you use them, the file they create will probably be -empty or not exist. - ->>> import os ->>> import tempfile ->>> fd,filename = tempfile.mkstemp(suffix='.h5', prefix='pypiezo-') ->>> os.close(fd) - ->>> c = HDF5_BaseConfig(filename=filename, group='/base') ->>> c.load() - -Loading will create a stub group group if it hadn't existed before. - ->>> pprint_HDF5(filename) -/ - /base ->>> print c.dump(from_file=True) - - -Saving fills in all the config values. - ->>> c['syslog'] = True ->>> c.save() ->>> pprint_HDF5(filename) # doctest: +REPORT_UDIFF -/ - /base - - warn - - no - - yes ->>> print c.dump(from_file=True) -log-level: warn -matplotlib: no -syslog: yes - -If you want more details, you can dump with help strings. - ->>> print c.dump(help=True, from_file=True) # doctest: +NORMALIZE_WHITESPACE -log-level: warn\t(Module logging level. Default: warn. Choices: - \t critical, error, warn, info, debug) -matplotlib: no\t(Plot piezo motion using `matplotlib`. Default: no. - \t Choices: yes, no) -syslog: yes\t(Log to syslog (otherwise log to stderr). Default: no. - \t Choices: yes, no) - -Settings also support `None`, even if they have numeric types. - ->>> c = HDF5_AxisConfig(filename=filename, group='/z-axis') ->>> c.load() ->>> c.save() ->>> c.load() ->>> print c.dump(from_file=True) -gain: 1.0 -maximum: None -minimum: None -sensitivity: 1.0 ->>> print (c['minimum'] == None) -True - -Cleanup our temporary config file. - ->>> os.remove(filename) -""" - -import logging as _logging -import os.path as _os_path import sys as _sys -import h5py as _h5py -import yaml as _yaml - -from . import LOG as _LOG - - -class _Setting (object): - "A named setting with arbitrart text values." - def __init__(self, name, help='', default=None): - self.name = name - self._help = help - self.default = default - - def __str__(self): - return '<%s %s>' % (self.__class__.__name__, self.name) - - def __repr__(self): - return self.__str__() - - def help(self): - ret = '%s Default: %s.' % ( - self._help, self.convert_to_text(self.default)) - return ret.strip() - - def convert_from_text(self, value): - return value - - def convert_to_text(self, value): - return value - - -class _ChoiceSetting (_Setting): - """A named setting with a limited number of possible values. - - `choices` should be a list of `(config_file_value, Python value)` - pairs. For example - - >>> s = _ChoiceSetting(name='bool', - ... choices=[('yes', True), ('no', False)]) - >>> s.convert_from_text('yes') - True - >>> s.convert_to_text(True) - 'yes' - >>> s.convert_to_text('invalid') - Traceback (most recent call last): - ... - ValueError: invalid - >>> s.help() - 'Default: yes. Choices: yes, no' - """ - def __init__(self, choices=None, **kwargs): - if 'default' not in kwargs: - if None not in [keyval[1] for keyval in choices]: - kwargs['default'] = choices[0][1] - super(_ChoiceSetting, self).__init__(**kwargs) - if choices == None: - choices = [] - self.choices = choices - - def help(self): - ret = '%s Choices: %s' % ( - super(_ChoiceSetting, self).help(), - ', '.join([key for key,value in self.choices])) - return ret.strip() - - def convert_from_text(self, value): - return dict(self.choices)[value] - - def convert_to_text(self, value): - for keyval in self.choices: - key,val = keyval - if val == value: - return key - raise ValueError(value) - - -class _BooleanSetting (_ChoiceSetting): - """A named settubg that can be either true or false. - - >>> s = _BooleanSetting(name='bool') - - >>> s.convert_from_text('yes') - True - >>> s.convert_to_text(True) - 'yes' - >>> s.convert_to_text('invalid') - Traceback (most recent call last): - ... - ValueError: invalid - >>> s.help() - 'Default: no. Choices: yes, no' - """ - def __init__(self, **kwargs): - assert 'choices' not in kwargs - if 'default' not in kwargs: - kwargs['default'] = False - super(_BooleanSetting, self).__init__( - choices=[('yes', True), ('no', False)], **kwargs) - - -class _NumericSetting (_Setting): - """A named setting with numeric values. - - >>> s = _NumericSetting(name='float') - >>> s.default - 0 - >>> s.convert_to_text(13) - '13' - """ - _default_value = 0 - - def __init__(self, **kwargs): - if 'default' not in kwargs: - kwargs['default'] = self._default_value - super(_NumericSetting, self).__init__(**kwargs) - - def convert_to_text(self, value): - return str(value) - - def convert_from_text(self, value): - if value in [None, 'None']: - return None - return self._convert_from_text(value) - - def _convert_from_text(self, value): - raise NotImplementedError() - - -class _IntegerSetting (_NumericSetting): - """A named setting with integer values. - - >>> s = _IntegerSetting(name='int') - >>> s.default - 1 - >>> s.convert_from_text('8') - 8 - """ - _default_value = 1 - - def _convert_from_text(self, value): - return int(value) - - -class _FloatSetting (_NumericSetting): - """A named setting with floating point values. - - >>> s = _FloatSetting(name='float') - >>> s.default - 1.0 - >>> s.convert_from_text('8') - 8.0 - >>> s.convert_from_text('invalid') - Traceback (most recent call last): - ... - ValueError: invalid literal for float(): invalid - """ - _default_value = 1.0 - - def _convert_from_text(self, value): - return float(value) - - -class _FloatListSetting (_Setting): - """A named setting with a list of floating point values. +import h5config.config as _config +import h5config.tools as _h5config_tools +import h5config.util as _util - >>> s = _FloatListSetting(name='floatlist') - >>> s.default - [] - >>> s.convert_to_text([1, 2.3]) - '1, 2.3' - >>> s.convert_from_text('4.5, -6.7') # doctest: +ELLIPSIS - [4.5, -6.700...] - >>> s.convert_to_text([]) - '' - >>> s.convert_from_text('') - [] - """ - def __init__(self, **kwargs): - if 'default' not in kwargs: - kwargs['default'] = [] - super(_FloatListSetting, self).__init__(**kwargs) - def _convert_from_text(self, value): - if value is None: - return value - return float(value) - - def convert_from_text(self, value): - if value is None: - return None - elif value == '': - return [] - return [self._convert_from_text(x) for x in value.split(',')] - - def convert_to_text(self, value): - if value is None: - return None - return ', '.join([str(x) for x in value]) - - -class _Config (dict): - "A class with a list `._keys` of `_Setting`\s." - settings = [] - - def __init__(self): - for s in self.settings: - self[s.name] = s.default - - def dump(self, help=False): - """Return all settings and their values as a string - - >>> b = _BaseConfig() - >>> print b.dump() - syslog: no - matplotlib: no - log-level: warn - >>> print b.dump(help=True) # doctest: +NORMALIZE_WHITESPACE - syslog: no (Log to syslog (otherwise log to stderr). - Default: no. Choices: yes, no) - matplotlib: no (Plot piezo motion using `matplotlib`. - Default: no. Choices: yes, no) - log-level: warn (Module logging level. Default: warn. - Choices: critical, error, warn, info, debug) - """ - lines = [] - settings = dict([(s.name, s) for s in self.settings]) - for key,value in self.iteritems(): - if key in settings: - setting = settings[key] - value_string = setting.convert_to_text(value) - if help: - help_string = '\t(%s)' % setting.help() - else: - help_string = '' - lines.append('%s: %s%s' % (key, value_string, help_string)) - return '\n'.join(lines) - - -class _BackedConfig (_Config): - "A `_Config` instance with some kind of storage interface" - def load(self): - raise NotImplementedError() - - def save(self): - raise NotImplementedError() - - -class _BaseConfig (_Config): - """Configure `pypiezo` module operation - - >>> b = _BaseConfig() - >>> b.settings # doctest: +NORMALIZE_WHITESPACE - [<_ChoiceSetting log-level>, <_BooleanSetting syslog>, - <_BooleanSetting matplotlib>] - >>> print b['log-level'] == _logging.WARN - True - """ - settings = [ - _ChoiceSetting( - name='log-level', - help='Module logging level.', - default=_logging.WARN, - choices=[ - ('critical', _logging.CRITICAL), - ('error', _logging.ERROR), - ('warn', _logging.WARN), - ('info', _logging.INFO), - ('debug', _logging.DEBUG), - ]), - _BooleanSetting( - name='syslog', - help='Log to syslog (otherwise log to stderr).', - default=False), - _BooleanSetting( +class PackageConfig (_h5config_tools.PackageConfig): + "Configure `pypiezo` module operation" + settings = _h5config_tools.PackageConfig.settings + [ + _config.BooleanSetting( name='matplotlib', help='Plot piezo motion using `matplotlib`.', default=False), ] -class _AxisConfig (_Config): +class AxisConfig (_config.Config): "Configure a single piezo axis" settings = [ - _FloatSetting( + _config.FloatSetting( name='gain', help=( 'Volts applied at piezo per volt output from the DAQ card ' '(e.g. if your DAQ output is amplified before driving the ' 'piezo),')), - _FloatSetting( + _config.FloatSetting( name='sensitivity', help='Meters of piezo deflection per volt applied to the piezo.'), - _FloatSetting( + _config.FloatSetting( name='minimum', help='Set a lower limit on allowed output voltage', default=None), - _FloatSetting( + _config.FloatSetting( name='maximum', help='Set an upper limit on allowed output voltage', default=None), ] -class _ChannelConfig (_Config): +class ChannelConfig (_config.Config): settings = [ - _Setting( + _config.Setting( name='device', help='Comedi device.', default='/dev/comedi0'), - _IntegerSetting( + _config.IntegerSetting( name='subdevice', help='Comedi subdevice index. -1 for automatic detection.', default=-1), - _IntegerSetting( + _config.IntegerSetting( name='channel', help='Subdevice channel index.', default=0), - _IntegerSetting( + _config.IntegerSetting( name='maxdata', help="Channel's maximum bit value."), - _IntegerSetting( + _config.IntegerSetting( name='range', help="Channel's selected range index."), - _FloatListSetting( + _config.FloatListSetting( name='conversion-coefficients', help=('Bit to physical unit conversion coefficients starting with ' 'the constant coefficient.')), - _FloatSetting( + _config.FloatSetting( name='conversion-origin', help=('Origin (bit offset) of bit to physical polynomial ' 'expansion.')), - _FloatListSetting( + _config.FloatListSetting( name='inverse-conversion-coefficients', help=('Physical unit to bit conversion coefficients starting with ' 'the constant coefficient.')), - _FloatSetting( + _config.FloatSetting( name='inverse-conversion-origin', help=('Origin (physical unit offset) of physical to bit ' 'polynomial expansion.')), ] -class _OutputChannelConfig (_ChannelConfig): +class OutputChannelConfig (ChannelConfig): pass -class _InputChannelConfig (_ChannelConfig): +class InputChannelConfig (ChannelConfig): pass -def pprint_HDF5(*args, **kwargs): - print pformat_HDF5(*args, **kwargs) - -def pformat_HDF5(filename, group='/'): - f = _h5py.File(filename, 'r') - cwg = f[group] - return '\n'.join(_pformat_hdf5(cwg)) - -def _pformat_hdf5(cwg, depth=0): - lines = [] - lines.append(' '*depth + cwg.name) - depth += 1 - for key,value in cwg.iteritems(): - if isinstance(value, _h5py.Group): - lines.extend(_pformat_hdf5(value, depth)) - elif isinstance(value, _h5py.Dataset): - lines.append(' '*depth + str(value)) - lines.append(' '*(depth+1) + str(value[...])) - else: - lines.append(' '*depth + str(value)) - return lines - -def h5_create_group(cwg, path): - "Create the group where the settings are stored (if necessary)." - if path == '/': - return cwg - gpath = [''] - for group in path.strip('/').split('/'): - gpath.append(group) - if group not in cwg.keys(): - _LOG.debug('creating group %s in %s' - % ('/'.join(gpath), cwg.file)) - cwg.create_group(group) - cwg = cwg[group] - return cwg - -class _HDF5Config (_BackedConfig): - """Mixin to back a `_Config` class with an HDF5 file. - - TODO: Special handling for Choice (enums), FloatList (arrays), etc.? - - The `.save` and `.load` methods have an optional `group` argument - that allows you to save and load settings from an externally - opened HDF5 file. This can make it easier to stash several - related `_Config` classes in a single file. For example - - >>> import os - >>> import tempfile - >>> fd,filename = tempfile.mkstemp(suffix='.h5', prefix='pypiezo-') - >>> os.close(fd) - - >>> f = _h5py.File(filename, 'a') - >>> c = HDF5_BaseConfig(filename='untouched_file.h5', - ... group='/untouched/group') - >>> c['syslog'] = True - >>> group = f.create_group('base') - >>> c.save(group) - >>> pprint_HDF5(filename) - / - /base - - warn - - no - - yes - >>> d = HDF5_BaseConfig(filename='untouched_file.h5', - ... group='/untouched/group') - >>> d.load(group) - >>> d['syslog'] - True - - >>> f.close() - >>> os.remove(filename) - """ - def __init__(self, filename, group='/', **kwargs): - super(_HDF5Config, self).__init__(**kwargs) - self.filename = filename - assert group.startswith('/'), group - if not group.endswith('/'): - group += '/' - self.group = group - self._file_checked = False - - def _check_file(self): - if self._file_checked: - return - self._setup_file() - self._file_checked = True - - def _setup_file(self): - f = _h5py.File(self.filename, 'a') - cwg = f # current working group - h5_create_group(cwg, self.group) - f.close() - - def dump(self, help=False, from_file=False): - """Return the relevant group in `self.filename` as a string - - Extends the base :meth:`dump` by adding the `from_file` - option. If `from_file` is true, dump all entries that - currently exist in the relevant group, rather than listing all - settings defined in the instance dictionary. - """ - if from_file: - self._check_file() - f = _h5py.File(self.filename, 'r') - cwg = f[self.group] - lines = [] - settings = dict([(s.name, s) for s in self.settings]) - for key,value in cwg.iteritems(): - if help and key in settings: - help_string = '\t(%s)' % settings[key].help() - else: - help_string = '' - lines.append('%s: %s%s' % (key, value[...], help_string)) - return '\n'.join(lines) - return super(_HDF5Config, self).dump(help=help) - - def load(self, group=None): - if group is None: - self._check_file() - f = _h5py.File(self.filename, 'r') - group = f[self.group] - else: - f = None - for s in self.settings: - if s.name not in group.keys(): - continue - self[s.name] = s.convert_from_text(group[s.name][...]) - if f: - f.close() - - def save(self, group=None): - if group is None: - self._check_file() - f = _h5py.File(self.filename, 'a') - group = f[self.group] - else: - f = None - for s in self.settings: - value = s.convert_to_text(self[s.name]) - try: - del group[s.name] - except KeyError: - pass - group[s.name] = value - if f: - f.close() - - -class _YAMLDumper (_yaml.SafeDumper): - def represent_bool(self, data): - "Use yes/no instead of the default true/false" - if data: - value = u'yes' - else: - value = u'no' - return self.represent_scalar(u'tag:yaml.org,2002:bool', value) - - -_YAMLDumper.add_representer(bool, _YAMLDumper.represent_bool) - - -class _YAMLConfig (_BackedConfig): - """Mixin to back a `_Config` class with a YAML file. - - TODO: Special handling for Choice (enums), FloatList (arrays), etc.? - - >>> import os - >>> import os.path - >>> import tempfile - >>> fd,filename = tempfile.mkstemp(suffix='.yaml', prefix='pypiezo-') - >>> os.close(fd) - - >>> c = YAML_BaseConfig(filename=filename) - >>> c.load() - - Saving writes all the config values to disk. - - >>> c['syslog'] = True - >>> c.save() - >>> print open(c.filename, 'r').read() - log-level: warn - matplotlib: no - syslog: yes - - - Loading reads the config files from disk. - - >>> c = YAML_BaseConfig(filename=filename) - >>> c.load() - >>> print c.dump() - syslog: yes - matplotlib: no - log-level: warn - - Cleanup our temporary config file. - - >>> os.remove(filename) - """ - dumper = _YAMLDumper - - def __init__(self, filename, **kwargs): - super(_YAMLConfig, self).__init__(**kwargs) - self.filename = filename - - def load(self): - if not _os_path.exists(self.filename): - open(self.filename, 'a').close() - with open(self.filename, 'r') as f: - data = _yaml.safe_load(f) - if data == None: - return # empty file - settings = dict([(s.name, s) for s in self.settings]) - for key,value in data.iteritems(): - setting = settings[key] - if isinstance(setting, _BooleanSetting): - v = value - else: - v = setting.convert_from_text(value) - self[key] = v - - def save(self): - data = {} - settings = dict([(s.name, s) for s in self.settings]) - for key,value in self.iteritems(): - if key in settings: - setting = settings[key] - if isinstance(setting, _BooleanSetting): - v = value - else: - v = setting.convert_to_text(value) - data[key] = v - with open(self.filename, 'w') as f: - _yaml.dump(data, stream=f, Dumper=self.dumper, - default_flow_style=False) - - -# Define HDF5- and YAML-backed subclasses of the basic _Config types. -for name,obj in locals().items(): - if (obj != _Config and - type(obj) == type and - issubclass(obj, _Config) and - not issubclass(obj, _BackedConfig)): - for prefix,base in [('HDF5', _HDF5Config), ('YAML', _YAMLConfig)]: - _name = '%s%s' % (prefix, name) - _bases = (base, obj) - _dict = {} - _class = type(_name, _bases, _dict) - setattr(_sys.modules[__name__], _name, _class) - -del name, obj, prefix, base, _name, _bases, _dict, _class - - -def find_base_config(): - "Return the best `_BaseConfig` match after scanning the filesystem" - _LOG.info('looking for base_config file') - user_basepath = _os_path.join(_os_path.expanduser('~'), '.pypiezorc') - system_basepath = _os_path.join('/etc', 'pypiezo', 'config') - distributed_basepath = _os_path.join('/usr', 'share', 'pypiezo', 'config') - for basepath in [user_basepath, system_basepath, distributed_basepath]: - for (extension, config) in [('.h5', HDF5_BaseConfig), - ('.yaml', YAML_BaseConfig)]: - filename = basepath + extension - if _os_path.exists(filename): - _LOG.info('base_config file found at %s' % filename) - base_config = config(filename) - base_config.load() - return base_config - else: - _LOG.debug('no base_config file at %s' % filename) - _LOG.info('new base_config file at %s' % filename) - basepath = user_basepath - filename = basepath + extension - return config(filename) +_util.build_backend_classes(_sys.modules[__name__]) diff --git a/pypiezo/surface.py b/pypiezo/surface.py index ec7f4e8..0145b23 100644 --- a/pypiezo/surface.py +++ b/pypiezo/surface.py @@ -35,7 +35,6 @@ except (ImportError, RuntimeError), e: _matplotlib_import_error = e from . import LOG as _LOG -from . import base_config as _base_config from . import base as _base -- 2.26.2