def test_float(self): """Test float""" ep = ExtensionPoint(IOption) class TMP_float(Plugin): declare_option("o1", cls=FloatOption) obj = TMP_float() pt = ep.service("o1") pt.load("o1", [-1.5]) self.assertEqual(pt.get_value(), -1.5) pt.load("o1", ["-1.5"]) self.assertEqual(pt.get_value(), -1.5) pt.load("o1", [[]]) self.assertEqual(pt.get_value(), 0) try: pt.load("o1", [['a']]) self.fail("expected error") except OptionError: pass try: pt.load("o1", ['a']) self.fail("expected error") except OptionError: pass
def test_bool(self): """Test boolean""" ep = ExtensionPoint(IOption) class TMP_bool(Plugin): declare_option("o1", cls=BoolOption) obj = TMP_bool() pt = ep.service("o1") pt.load("o1", [True]) self.assertEqual(pt.get_value(), True) pt.load("o1", [False]) self.assertEqual(pt.get_value(), False) pt.load("o1", [1]) self.assertEqual(pt.get_value(), True) pt.load("o1", [0]) self.assertEqual(pt.get_value(), False) pt.load("o1", ['YES']) self.assertEqual(pt.get_value(), True) pt.load("o1", ['no']) self.assertEqual(pt.get_value(), False)
def test_load5(self): """Test load method""" PluginGlobals.add_env("testing.config_loading") class TMP2(Plugin): def __init__(self): declare_option("a") declare_option("b", cls=FileOption) declare_option("c") declare_option("xx", section_re='globals.*') config = Configuration() tmp2 = TMP2() config.load(currdir + "config4.ini") #config.pprint() if False and sys.platform == "win32": # # A hack, to ensure cross-platform portability of this test # e = ExtensionPoint(IFileOption) for ep in e.extensions(): ep.set_value("/dev/null", raw=True) #PluginGlobals.pprint() config.save(currdir + "config4.out") #print config self.assertFileEqualsBaseline( currdir + "config4.out", currdir + "config4.txt", filter=filter) pyutilib.misc.setup_redirect(currdir + "log2.out") config.pprint() pyutilib.misc.reset_redirect() self.assertFileEqualsBaseline( currdir + "log2.out", currdir + "log2.txt", filter=filter) PluginGlobals.remove_env( "testing.config_loading", cleanup=True, singleton=False)
def create_test_suites(filename=None, config=None, _globals=None, options=None): if options is None: #pragma:nocover options = Options() # # Add categories specified by the PYUTILIB_AUTOTEST_CATEGORIES # or PYUTILIB_UNITTEST_CATEGORIES environments # if options is None or options.categories is None or len( options.categories) == 0: options.categories = set() if 'PYUTILIB_AUTOTEST_CATEGORIES' in os.environ: for cat in re.split(',', os.environ['PYUTILIB_AUTOTEST_CATEGORIES']): if cat != '': options.categories.add(cat.strip()) elif 'PYUTILIB_UNITTEST_CATEGORIES' in os.environ: for cat in re.split(',', os.environ['PYUTILIB_UNITTEST_CATEGORIES']): if cat != '': options.categories.add(cat.strip()) # if not filename is None: if options.currdir is None: options.currdir = dirname(abspath(filename)) + os.sep # ep = ExtensionPoint(plugins.ITestParser) ftype = os.path.splitext(filename)[1] if not ftype == '': ftype = ftype[1:] service = ep.service(ftype) if service is None: raise IOError( "Unknown file type. Cannot load test configuration from file '%s'" % filename) config = service.load_test_config(filename) #service.print_test_config(config) validate_test_config(config) # # Evaluate Python expressions # for item in config.get('python', []): try: exec(item, _globals) except Exception: err = sys.exc_info()[1] print("ERROR executing '%s'" % item) print(" Exception: %s" % str(err)) # # Create test driver, which is put in the global namespace # driver = plugins.TestDriverFactory(config['driver']) if driver is None: raise IOError("Unexpected test driver '%s'" % config['driver']) _globals["test_driver"] = driver # # Generate suite # for suite in config.get('suites', {}): create_test_suite(suite, config, _globals, options)
def log_summary(): observers = ExtensionPoint(IPlugin) logging.info('Registered plugins:') for observer in observers: logging.info('\t %s' % observer) observers = ExtensionPoint(IWaveformGenerator) logging.info('Registered function generator plugins:') for observer in observers: logging.info('\t %s' % observer) observers = ExtensionPoint(ILoggingPlugin) logging.info('Registered logging plugins:') for observer in observers: logging.info('\t %s' % observer)
def test_OptionPlugin(self): """Test OptionPlugin""" ep = ExtensionPoint(IOption) class TMP_OptionPlugin(Plugin): declare_option("o1") obj = TMP_OptionPlugin() pt = ep.service("o1") try: pt.load("o1", []) self.fail("expected error") except OptionError: pass
def test_save1(self): """Test save method""" config = Configuration() config.load(currdir+"config1.ini") # # A hack, to ensure cross-platform portability of this test # if False and sys.platform == "win32": e = ExtensionPoint(IFileOption) for ep in e.extensions(): if ep.enabled(): ep.set_value("/dev/null", raw=True) config.save(currdir+"config1.out") #PluginGlobals.pprint() self.assertFileEqualsBaseline(currdir+"config1.out",currdir+"config1.txt", filter=filter)
def test_dict2(self): """Test DictOption""" ep = ExtensionPoint(IOption) class TMP_dict2(Plugin): declare_option("options", DictOption) declare_option("b", local_name="o1") declare_option("c", local_name="o2", default=3) obj = TMP_dict2() # # testing attribute set/get # obj.options.x = 3 self.assertEqual(obj.options.x, 3) try: obj.options.xx self.fail("expected error") except OptionError: pass # # Testing the DictOption set/get # obj.options = {'yy': 3, 'zz': 2} self.assertEqual(obj.options.yy, 3) self.assertEqual(obj.options.zz, 2)
def on_timer(event): reloaded = self._config.reload_if_modified() if reloaded: for participant in ExtensionPoint(ISystemParticipant): participant.config_changed() for loader in loaders: loader.update()
def __init__(self): self.enabledPluginList = None self.env = PluginEnvironment("OneServer") PluginGlobals.push_env(self.env) self.eggLoader = PluginFactory("EggLoader", namespace="project1", env='pca') # PluginGlobals.env().load_services(path='./plugin/p',auto_disable=False) #Needs to be changed after this works PluginGlobals.env().load_services( path=sys.path, auto_disable=False) #Needs to be changed after this works self.administratorPlugins = ExtensionPoint(IAdministrationPlugin) self.storagePlugins = ExtensionPoint(IStoragePlugin) self.utilityPlugins = ExtensionPoint(IUtilityPlugin)
def __init__(self, filename=None, parser="ConfigParser"): """Constructor. @param filename - The associated configuration file. @param parser - Specify the name of the parser used to read/write configuration files. """ self.parser_type = "Configuration_ConfigParser" self.filename = filename # # Define extension points # self.parsers = ExtensionPoint(IConfiguration) self.option_plugins = ExtensionPoint(IOption) self.option_data_plugin = ExtensionPoint(IOptionDataProvider) self.pathoption_plugins = ExtensionPoint(IFileOption) self.postconfig_actions = ExtensionPoint(IUpdatedOptionsAction) self.clear()
def test_save1(self): """Test save method""" config = Configuration() config.load(currdir + "config1.ini") # # A hack, to ensure cross-platform portability of this test # if False and sys.platform == "win32": e = ExtensionPoint(IFileOption) for ep in e.extensions(): if ep.enabled(): ep.set_value("/dev/null", raw=True) config.save(currdir + "config1.out") #PluginGlobals.pprint() self.assertFileEqualsBaseline(currdir + "config1.out", currdir + "config1.txt", filter=filter)
def __init__(self): """ Declare an extension point for a data provider, and construct one if one hasn't already been provided. """ self.data = ExtensionPoint(IOptionDataProvider) if PluginGlobals._default_OptionData is None: PluginGlobals._default_OptionData = OptionData() # # This is a hack. We shouldn't need to test if len(self.data) is zero. # Somewhere in our tests, the weakref to the OptionData object is being # corrupted. Perhaps this is caused by 'nose' or 'import' logic? # if True and len(self.data) == 0: PluginGlobals.interface_services[IOptionDataProvider].add( PluginGlobals._default_OptionData._id) PluginGlobals.plugin_instances[ PluginGlobals._default_OptionData._id] = weakref.ref( PluginGlobals._default_OptionData) # if len(self.data) == 0: #if False: #print "ZZZ", ep.Xextensions() #print "HERE", PluginGlobals._default_OptionData._id, PluginGlobals._default_OptionData.ctr #print "HERE", PluginGlobals._default_OptionData #print "HERE - id", id(PluginGlobals._default_OptionData) #print "HERE", getattr(PluginGlobals._default_OptionData, '_HERE_', None) #print "HERE", PluginGlobals._default_OptionData.__interfaces__ #print "" #print "HERE", PluginGlobals.interface_services #print "HERE", PluginGlobals.plugin_instances.keys() #for exe_ in PluginGlobals._executables: #print exe_._id, exe_ #print "LEN", len(PluginGlobals.env) #for name_ in PluginGlobals.env: #env_ = PluginGlobals.env[name_] #print env_.name #print env_.nonsingleton_plugins #print [env_.singleton_services[cls_] for cls_ in env_.singleton_services] raise PluginError( "Problem constructing a global OptionData object %s" % self.name)
def test_path(self): """Test path""" ep = ExtensionPoint(IOption) if sys.platform == "win32": o1_default = "C:/default" else: o1_default = "/dev//default" class TMP_path(Plugin): declare_option("o1", cls=FileOption, default=o1_default, directory="/dev/null") obj = TMP_path() pt = ep.service("o1") pt.load("o1", [None]) if sys.platform == "win32": self.assertEqual(pt.get_value(), "c:\\default") else: self.assertEqual(pt.get_value(), "/dev/default") if sys.platform == "win32": pt.load("o1", ["C:/load1"]) else: pt.load("o1", ["/dev/load1"]) if sys.platform == "win32": self.assertEqual(pt.get_value(), "c:\\load1") else: self.assertEqual(pt.get_value(), "/dev/load1") if sys.platform == "win32": pt.set_dir("D:/foo") else: pt.set_dir("/dev/foo") pt.load("o1", ["bar"]) if sys.platform == "win32": self.assertEqual(pt.get_value(), "d:\\foo\\bar") else: self.assertEqual(pt.get_value(), "/dev/foo/bar")
class CommandSetBase(Plugin): implements(ICommandSet) observers = ExtensionPoint(ICommandSetObserver) def after_load(self): for observer in self.observers: observer.loaded_command_set(self) def after_unload(self): self.deactivate() for observer in self.observers: observer.unloaded_command_set(self)
def test_dict1(self): """Test DictOption""" ep = ExtensionPoint(IOption) class TMP_dict1(Plugin): declare_option("options", DictOption) declare_option("b") declare_option("c", default=3) obj = TMP_dict1() self.assertEqual(obj.options.b, None) self.assertEqual(obj.options.c, 3)
def test_path(self): """Test path""" ep = ExtensionPoint(IOption) if sys.platform == "win32": o1_default = "C:/default" else: o1_default = "/dev//default" class TMP_path(Plugin): declare_option( "o1", cls=FileOption, default=o1_default, directory="/dev/null") obj = TMP_path() pt = ep.service("o1") pt.load("o1", [None]) if sys.platform == "win32": self.assertEqual(pt.get_value(), "c:\\default") else: self.assertEqual(pt.get_value(), "/dev/default") if sys.platform == "win32": pt.load("o1", ["C:/load1"]) else: pt.load("o1", ["/dev/load1"]) if sys.platform == "win32": self.assertEqual(pt.get_value(), "c:\\load1") else: self.assertEqual(pt.get_value(), "/dev/load1") if sys.platform == "win32": pt.set_dir("D:/foo") else: pt.set_dir("/dev/foo") pt.load("o1", ["bar"]) if sys.platform == "win32": self.assertEqual(pt.get_value(), "d:\\foo\\bar") else: self.assertEqual(pt.get_value(), "/dev/foo/bar")
def register_executable(name, validate=None): ep = ExtensionPoint(IExternalExecutable) if len(ep(name, all=True)) == 0: PluginGlobals.add_env("pca") PluginGlobals._executables.append( ExternalExecutable(name=name, validate=validate)) PluginGlobals.pop_env() else: # # If the executable is being 'registered', then we search for it # again, since the user environment may have changed. # list(ep(name, all=True))[0].find_executable()
def test_load5(self): """Test load method""" PluginGlobals.add_env("testing.config_loading") class TMP2(Plugin): def __init__(self): declare_option("a") declare_option("b", cls=FileOption) declare_option("c") declare_option("xx", section_re='globals.*') config = Configuration() tmp2 = TMP2() config.load(currdir + "config4.ini") #config.pprint() if False and sys.platform == "win32": # # A hack, to ensure cross-platform portability of this test # e = ExtensionPoint(IFileOption) for ep in e.extensions(): ep.set_value("/dev/null", raw=True) #PluginGlobals.pprint() config.save(currdir + "config4.out") #print config self.assertFileEqualsBaseline(currdir + "config4.out", currdir + "config4.txt", filter=filter) pyutilib.misc.setup_redirect(currdir + "log2.out") config.pprint() pyutilib.misc.reset_redirect() self.assertFileEqualsBaseline(currdir + "log2.out", currdir + "log2.txt", filter=filter) PluginGlobals.remove_env("testing.config_loading", cleanup=True, singleton=False)
def connect_pyutilib_signal(signals, signal, *args, **kwargs): ''' Connect pyutilib callbacks to corresponding signal in blinker namespace. Allows code to be written using blinker signals for easier testing outside of MicroDrop, while maintaining compatibility with pyutilib. Parameters ---------- signals : blinker.Namespace signal : str Pyutilib signal name. *args, **kwargs Arguments passed to `pyutilib.component.core.ExtensionPoint()` Example ------- >>> from microdrop.interfaces import IPlugin >>> import microdrop.app >>> >>> signals = blinker.Namespace() >>> signal = 'get_schedule_requests' >>> args = ('on_plugin_enable', ) >>> connect_pyutilib_signal(signals, signal, IPlugin) >>> signals.signal(signal).send(*args) [(<bound method DmfDeviceController.get_schedule_requests of <Plugin DmfDeviceController 'microdrop.gui.dmf_device_controller'>>, [ScheduleRequest(before='microdrop.gui.config_controller', after='microdrop.gui.dmf_device_controller'), ScheduleRequest(before='microdrop.gui.main_window_controller', after='microdrop.gui.dmf_device_controller')])] ''' import functools as ft import inspect from microdrop.plugin_manager import ExtensionPoint callbacks = [ getattr(p, signal) for p in ExtensionPoint(*args, **kwargs) if hasattr(p, signal) ] for callback_i in callbacks: if len(inspect.getargspec(callback_i)[0]) < 2: # Blinker signals require _at least_ one argument (assumed to be sender). # Wrap pyutilib signals without any arguments to make them work with blinker. @ft.wraps(callback_i) def _callback(*args, **kwargs): return callback_i() else: _callback = callback_i signals.signal(signal).connect(_callback, weak=False)
def get_observers(function, interface=IPlugin): ''' Get dictionary of observers implementing the specified function. Parameters ---------- function : str Name of function to generate schedule for. interface : class, optional Plugin interface class. Returns ------- dict Mapping from service names to service instances. ''' observers = {} for obs in ExtensionPoint(interface): if hasattr(obs, function): observers[obs.name] = obs return observers
def _initialize_loaders(self): log.debug("_initialize_loaders()") # Import loaders so that they are registered in the PCA. import bumblebee.command.legacy_loader # Setup infrastructure for periodic updating of loaders. loaders = ExtensionPoint(ICommandSetLoader) def on_timer(event): reloaded = self._config.reload_if_modified() if reloaded: for participant in ExtensionPoint(ISystemParticipant): participant.config_changed() for loader in loaders: loader.update() self.Bind(wx.EVT_TIMER, on_timer) # Timer object must be bound to self, because otherwise it would # be garbage collected and therefore destroyed without ever # firing. self._timer = wx.Timer(self) self._timer.Start(500, oneShot=False)
class OptionPlugin(Plugin): """Manages the initialization of an Option.""" implements(IOption, service=True) def __init__(self): """ Declare an extension point for a data provider, and construct one if one hasn't already been provided. """ self.data = ExtensionPoint(IOptionDataProvider) if PluginGlobals._default_OptionData is None: PluginGlobals._default_OptionData = OptionData() # # This is a hack. We shouldn't need to test if len(self.data) is zero. # Somewhere in our tests, the weakref to the OptionData object is being # corrupted. Perhaps this is caused by 'nose' or 'import' logic? # if True and len(self.data) == 0: PluginGlobals.interface_services[IOptionDataProvider].add( PluginGlobals._default_OptionData._id) PluginGlobals.plugin_instances[ PluginGlobals._default_OptionData._id] = weakref.ref( PluginGlobals._default_OptionData) # if len(self.data) == 0: #if False: #print "ZZZ", ep.Xextensions() #print "HERE", PluginGlobals._default_OptionData._id, PluginGlobals._default_OptionData.ctr #print "HERE", PluginGlobals._default_OptionData #print "HERE - id", id(PluginGlobals._default_OptionData) #print "HERE", getattr(PluginGlobals._default_OptionData, '_HERE_', None) #print "HERE", PluginGlobals._default_OptionData.__interfaces__ #print "" #print "HERE", PluginGlobals.interface_services #print "HERE", PluginGlobals.plugin_instances.keys() #for exe_ in PluginGlobals._executables: #print exe_._id, exe_ #print "LEN", len(PluginGlobals.env) #for name_ in PluginGlobals.env: #env_ = PluginGlobals.env[name_] #print env_.name #print env_.nonsingleton_plugins #print [env_.singleton_services[cls_] for cls_ in env_.singleton_services] raise PluginError( "Problem constructing a global OptionData object %s" % self.name) def matches_section(self, section): """ This method returns true if the section name matches the option section, or if the option's section regular expression matches the section name. """ return (section == self.section) or (self.section_re != None and ( not self.section_p.match(section) is None)) def matches_name(self, name): """ This method returns true if the name matches the options' name. """ return (self.name == "") or (name == self.name) def convert(self, value, default): """Convert a value into a specific type. The default behavior is to take the list of values, and simply return the last one defined in the configuration.""" return value[-1] def get_value(self): """ Get the option value. """ return self.data.service().get(self.section, self.name) def set_value(self, _value_, raw=False): """ Set the option value. By default, the option is converted using the option-specific `convert` method, but the `raw` option can be specified to force the raw value to be inserted. """ if raw: self.data.service().set(self.section, self.name, _value_) else: if not type(_value_) is list or len(_value_) == 0: _value_ = [_value_] self.data.service().set(self.section, self.name, self.convert( _value_, self.default)) def load(self, _option_, _value_): """ Load an option value. This method assumes that the option value is provided in a list, which is the format used when interfacing with the Configure class. """ if type(_value_) is list and len(_value_) == 0: raise OptionError("Attempting to load option %r with empty data" % (self.name)) try: self.set_value(_value_) except OptionError: err = sys.exc_info()[1] raise OptionError("Error loading option %r: %s" % (str(_option_), str(err))) return True def reset(self): """Set option to its default value""" self.set_value(self.default, raw=True) def default_str(self): """Return a string value that describes the default option value""" return str(self.default)
def __init__(self, section, ignore_missing=False): self._section_ = section ep = ExtensionPoint(IOptionDataProvider) ep.service().ignore_missing = ignore_missing self.__dict__["data"] = ep
class Configuration(Plugin): """This class manages configuration data. Further, this configuration I/O is coordinated with Option objects. When configuration data is read in, associated Option plugins are populated. Similarly, when configuration data is writen, the configuration data is taken from Option data.""" def __init__(self, filename=None, parser="ConfigParser"): """Constructor. @param filename - The associated configuration file. @param parser - Specify the name of the parser used to read/write configuration files. """ self.parser_type = "Configuration_ConfigParser" self.filename = filename # # Define extension points # self.parsers = ExtensionPoint(IConfiguration) self.option_plugins = ExtensionPoint(IOption) self.option_data_plugin = ExtensionPoint(IOptionDataProvider) self.pathoption_plugins = ExtensionPoint(IFileOption) self.postconfig_actions = ExtensionPoint(IUpdatedOptionsAction) self.clear() def clear(self): """Clear local data.""" self.config = [] self.data = {} self.section = [] def __contains__(self, name): """Return whether the configuration contains a section of the given name. """ return name in self.data def __getitem__(self, name): """Return the configuration section with the specified name.""" if name not in self.data: raise ConfigurationError("No section " + name + " in data") return self.data[name] def sections(self): """Returns the names of all sections in the configuration data.""" return list(self.data.keys()) def load(self, filename=None): """Load configuration from a file.""" if len(self.parsers) == 0: #pragma:nocover raise ConfigurationError( "No IConfiguration parsers are registered") if not filename is None: self.filename = filename if self.filename is None: raise ConfigurationError("Cannot load without a filename") for option in self.pathoption_plugins: option.set_dir(os.path.dirname(self.filename)) # # By default, we simply take the first parser # self.config = self.parsers.service(self.parser_type).load( self.filename) self.data = {} self.section = [] for (s, o, v) in self.config: if not s in self.data: self.section.append(s) self.data[s] = {} if not o in self.data[s]: self.data[s][o] = [] self.data[s][o].append(v) # # Iterate through all sections, in the order they were # loaded. Load data for extensions that match each section name. # for sec in self.section: # # Find the option_plugins that match this section # plugins = [] for plugin in self.option_plugins: if plugin.matches_section(sec): plugins.append(plugin) for option in self.data[sec]: flag = False for plugin in plugins: if plugin.matches_name(option): flag = plugin.load(option, self.data[sec][option]) or flag if not flag: raise ConfigurationError( "Problem loading file %r. Option %r in section %r is not recognized!" % (self.filename, option, sec)) # # Finalize the configuration process # for plugin in self.postconfig_actions: plugin.reset_after_updates() def save(self, filename=None): """Save configuration to a file.""" if not filename is None: self.filename = filename if self.filename is None: raise ConfigurationError("Cannot save without a filename") # # Setup the list of tuples # self.clear() self.data = self.option_data_plugin.service().get_data() self.section = list(self.data.keys()) self.section.sort() flag = False header = "\nNote: the following configuration options have been omitted because their\nvalue is 'None':\n" for sec in self.section: plugins = [] for plugin in self.option_plugins: if plugin.matches_section(sec): plugins.append(plugin) # options = list(self.data[sec].keys()) options.sort() for option in options: for plugin in plugins: if plugin.matches_name(option): if not self.data[sec][option] is None: val = self.data[sec][option] self.config.append((sec, option, val)) else: flag = True header = header + " section=%r option=%r\n" % ( sec, option) break if flag: header = header + "\n" else: header = None # # Write config file # self.parsers.service(self.parser_type).save(self.filename, self.config, header) def pprint(self): """Print a simple summary of the configuration data.""" text = "" for (s, o, v) in self.config: text += "[%s] %s = %s\n" % (s, o, v) print(text) def summarize(self): """Summarize options""" tmp = {} for option in self.option_plugins: tmp.setdefault(option.section, {})[option.name] = option keys = list(tmp.keys()) keys.sort() for key in keys: print("[" + key + "]") print("") okeys = list(tmp[key].keys()) okeys.sort() for okey in okeys: print(" Option: " + tmp[key][okey].name) print(" Type: " + tmp[key][okey].__class__.__name__) print(" Default: " + tmp[key][okey].default_str()) print(" Doc: " + tmp[key][okey].__doc__) print("") print("")
def get_observers(function, interface=IPlugin): observers = {} for obs in ExtensionPoint(interface): if hasattr(obs, function): observers[obs.name] = obs return observers
class TMP_set_get3(Plugin): ep = ExtensionPoint(IOption) def __init__(self, section): declare_option("o1", section=section, default=4)
class TMP_set_get2(Plugin): ep = ExtensionPoint(IOption) declare_option("foo", local_name="o1", default=4) declare_option("foo", local_name="o2", default=4)
def registered_executable(name=None): ep = ExtensionPoint(IExternalExecutable) if name is None: return filter(lambda x: x.name, ep.extensions()) return ep.service(name)
def __init__(self, namespace): """Initialize logging information for a specified namespace""" self._hdlr = None self.namespace = namespace self.env_plugins = ExtensionPoint(IEnvironmentConfig) if self.namespace == "": section = "logging" section_re = None else: section = "logging." + namespace section_re = "^logging$" # declare_option("timestamp", section=section, section_re=section_re, default=False, doc="""Add timestamp to logging information.""") # declare_option("log_dir", section=section, section_re=section_re, default=None, doc="""The logging directory. The default directory is the application directory plus 'log'.""") # declare_option("log_type", section=section, section_re=section_re, default='none', doc="""Logging facility to use. Should be one of (`none`, `file`, `stderr`, `syslog`, `winlog`).""") # declare_option( "log_file", section=section, section_re=section_re, default=namespace + '.log', doc= """If `log_type` is `file`, this should be a path to the log-file.""" ) # declare_option("log_level", section=section, section_re=section_re, default='WARN', doc="""Level of verbosity in log. Should be one of (`CRITICAL`, `ERROR`, `WARN`, `INFO`, `DEBUG`).""") # declare_option("log_format", section=section, section_re=section_re, default=None, doc="""Custom logging format. If nothing is set, the following will be used: $(project)[$(env) $(module)] $(levelname): $(message) In addition to regular key names supported by the Python logger library library (see http://docs.python.org/lib/node422.html), one could use: - $(path)s the path for the current environment - $(basename)s the last path component of the current environment - $(app)s the name of the current application Note the usage of `$(...)s` instead of `%(...)s` as the latter form would be interpreted by the ConfigParser itself. """)
class TMP_set_get1(Plugin): ep = ExtensionPoint(IDummyOption) declare_option("foo", local_name="opt", default=4, cls=DummyOption1)
def test_repr(self): """Test string repn""" ep = ExtensionPoint(IOption) class TMP_repr(Plugin): declare_option("o1", default=4) declare_option("o2", section="foo", default=4) obj = TMP_repr() if re.match("\<Option \[globals\] 'o1'\>", str( ep.service("o1"))) is None: self.fail("Expected globals:o1, but this option is %s" % str(ep.service("o1"))) self.assertFalse( re.match("\<Option \[globals\] 'o1'\>", str(ep.service("o1"))) is None) self.assertFalse( re.match("\<Option \[foo\] 'o2'\>", str(ep.service("o2"))) is None) self.assertEqual(ep.service("o1").get_value(), 4) ep.service("o1").load("o1", ["new"]) self.assertEqual(ep.service("o1").get_value(), "new") ep.service("o1").load("o1", "old") self.assertEqual(ep.service("o1").get_value(), "old")
def test_repr(self): """Test string repn""" ep = ExtensionPoint(IOption) class TMP_repr(Plugin): declare_option("o1", default=4) declare_option("o2", section="foo", default=4) obj = TMP_repr() if re.match("\<Option \[globals\] 'o1'\>", str(ep.service("o1"))) is None: self.fail("Expected globals:o1, but this option is %s" % str(ep.service("o1"))) self.assertFalse( re.match("\<Option \[globals\] 'o1'\>", str(ep.service("o1"))) is None) self.assertFalse( re.match("\<Option \[foo\] 'o2'\>", str(ep.service("o2"))) is None) self.assertEqual(ep.service("o1").get_value(), 4) ep.service("o1").load("o1", ["new"]) self.assertEqual(ep.service("o1").get_value(), "new") ep.service("o1").load("o1", "old") self.assertEqual(ep.service("o1").get_value(), "old")
class Configuration(Plugin): """This class manages configuration data. Further, this configuration I/O is coordinated with Option objects. When configuration data is read in, associated Option plugins are populated. Similarly, when configuration data is writen, the configuration data is taken from Option data.""" def __init__(self, filename=None, parser="ConfigParser"): """Constructor. @param filename - The associated configuration file. @param parser - Specify the name of the parser used to read/write configuration files. """ self.parser_type = "Configuration_ConfigParser" self.filename = filename # # Define extension points # self.parsers = ExtensionPoint(IConfiguration) self.option_plugins = ExtensionPoint(IOption) self.option_data_plugin = ExtensionPoint(IOptionDataProvider) self.pathoption_plugins = ExtensionPoint(IFileOption) self.postconfig_actions = ExtensionPoint(IUpdatedOptionsAction) self.clear() def clear(self): """Clear local data.""" self.config = [] self.data = {} self.section = [] def __contains__(self, name): """Return whether the configuration contains a section of the given name. """ return name in self.data def __getitem__(self, name): """Return the configuration section with the specified name.""" if name not in self.data: raise ConfigurationError("No section " + name + " in data") return self.data[name] def sections(self): """Returns the names of all sections in the configuration data.""" return list(self.data.keys()) def load(self, filename=None): """Load configuration from a file.""" if len(self.parsers) == 0: #pragma:nocover raise ConfigurationError("No IConfiguration parsers are registered") if not filename is None: self.filename = filename if self.filename is None: raise ConfigurationError("Cannot load without a filename") for option in self.pathoption_plugins: option.set_dir(os.path.dirname(self.filename)) # # By default, we simply take the first parser # self.config = self.parsers.service(self.parser_type).load(self.filename) self.data = {} self.section = [] for (s, o, v) in self.config: if not s in self.data: self.section.append(s) self.data[s] = {} if not o in self.data[s]: self.data[s][o] = [] self.data[s][o].append(v) # # Iterate through all sections, in the order they were # loaded. Load data for extensions that match each section name. # for sec in self.section: # # Find the option_plugins that match this section # plugins = [] for plugin in self.option_plugins: if plugin.matches_section(sec): plugins.append(plugin) for option in self.data[sec]: flag = False for plugin in plugins: if plugin.matches_name(option): flag = plugin.load(option, self.data[sec][option]) or flag if not flag: raise ConfigurationError( "Problem loading file %r. Option %r in section %r is not recognized!" % (self.filename, option, sec)) # # Finalize the configuration process # for plugin in self.postconfig_actions: plugin.reset_after_updates() def save(self, filename=None): """Save configuration to a file.""" if not filename is None: self.filename = filename if self.filename is None: raise ConfigurationError("Cannot save without a filename") # # Setup the list of tuples # self.clear() self.data = self.option_data_plugin.service().get_data() self.section = list(self.data.keys()) self.section.sort() flag = False header = "\nNote: the following configuration options have been omitted because their\nvalue is 'None':\n" for sec in self.section: plugins = [] for plugin in self.option_plugins: if plugin.matches_section(sec): plugins.append(plugin) # options = list(self.data[sec].keys()) options.sort() for option in options: for plugin in plugins: if plugin.matches_name(option): if not self.data[sec][option] is None: val = self.data[sec][option] self.config.append((sec, option, val)) else: flag = True header = header + " section=%r option=%r\n" % ( sec, option) break if flag: header = header + "\n" else: header = None # # Write config file # self.parsers.service(self.parser_type).save(self.filename, self.config, header) def pprint(self): """Print a simple summary of the configuration data.""" text = "" for (s, o, v) in self.config: text += "[%s] %s = %s\n" % (s, o, v) print(text) def summarize(self): """Summarize options""" tmp = {} for option in self.option_plugins: tmp.setdefault(option.section, {})[option.name] = option keys = list(tmp.keys()) keys.sort() for key in keys: print("[" + key + "]") print("") okeys = list(tmp[key].keys()) okeys.sort() for okey in okeys: print(" Option: " + tmp[key][okey].name) print(" Type: " + tmp[key][okey].__class__.__name__) print(" Default: " + tmp[key][okey].default_str()) print(" Doc: " + tmp[key][okey].__doc__) print("") print("")
class OptionPlugin(Plugin): """Manages the initialization of an Option.""" implements(IOption, service=True) def __init__(self): """ Declare an extension point for a data provider, and construct one if one hasn't already been provided. """ self.data = ExtensionPoint(IOptionDataProvider) if PluginGlobals._default_OptionData is None: PluginGlobals._default_OptionData = OptionData() # # This is a hack. We shouldn't need to test if len(self.data) is zero. # Somewhere in our tests, the weakref to the OptionData object is being # corrupted. Perhaps this is caused by 'nose' or 'import' logic? # if True and len(self.data) == 0: PluginGlobals.interface_services[IOptionDataProvider].add( PluginGlobals._default_OptionData._id) PluginGlobals.plugin_instances[ PluginGlobals._default_OptionData._id] = weakref.ref( PluginGlobals._default_OptionData) # if len(self.data) == 0: #if False: #print "ZZZ", ep.Xextensions() #print "HERE", PluginGlobals._default_OptionData._id, PluginGlobals._default_OptionData.ctr #print "HERE", PluginGlobals._default_OptionData #print "HERE - id", id(PluginGlobals._default_OptionData) #print "HERE", getattr(PluginGlobals._default_OptionData, '_HERE_', None) #print "HERE", PluginGlobals._default_OptionData.__interfaces__ #print "" #print "HERE", PluginGlobals.interface_services #print "HERE", PluginGlobals.plugin_instances.keys() #for exe_ in PluginGlobals._executables: #print exe_._id, exe_ #print "LEN", len(PluginGlobals.env) #for name_ in PluginGlobals.env: #env_ = PluginGlobals.env[name_] #print env_.name #print env_.nonsingleton_plugins #print [env_.singleton_services[cls_] for cls_ in env_.singleton_services] raise PluginError( "Problem constructing a global OptionData object %s" % self.name) def matches_section(self, section): """ This method returns true if the section name matches the option section, or if the option's section regular expression matches the section name. """ return (section == self.section) or ( self.section_re != None and (not self.section_p.match(section) is None)) def matches_name(self, name): """ This method returns true if the name matches the options' name. """ return (self.name == "") or (name == self.name) def convert(self, value, default): """Convert a value into a specific type. The default behavior is to take the list of values, and simply return the last one defined in the configuration.""" return value[-1] def get_value(self): """ Get the option value. """ return self.data.service().get(self.section, self.name) def set_value(self, _value_, raw=False): """ Set the option value. By default, the option is converted using the option-specific `convert` method, but the `raw` option can be specified to force the raw value to be inserted. """ if raw: self.data.service().set(self.section, self.name, _value_) else: if not type(_value_) is list or len(_value_) == 0: _value_ = [_value_] self.data.service().set(self.section, self.name, self.convert(_value_, self.default)) def load(self, _option_, _value_): """ Load an option value. This method assumes that the option value is provided in a list, which is the format used when interfacing with the Configure class. """ if type(_value_) is list and len(_value_) == 0: raise OptionError("Attempting to load option %r with empty data" % (self.name)) try: self.set_value(_value_) except OptionError: err = sys.exc_info()[1] raise OptionError("Error loading option %r: %s" % (str(_option_), str(err))) return True def reset(self): """Set option to its default value""" self.set_value(self.default, raw=True) def default_str(self): """Return a string value that describes the default option value""" return str(self.default)
class ImportLoader(ManagedSingletonPlugin): """Loader that looks for Python source files in the plugins directories, which simply get imported, thereby registering them with the component manager if they define any components. """ ep_services = ExtensionPoint(IIgnorePluginWhenLoading) implements(IPluginLoader) def load(self, env, search_path, disable_re, name_re): generate_debug_messages = __debug__ and env.log.isEnabledFor( logging.DEBUG) env.log.info('Loading plugins with ImportLoader') for path in search_path: plugin_files = glob(os.path.join(path, '*.py')) # # Note: for reproducibility, this fixes the order that # files are loaded # for plugin_file in sorted(plugin_files): #print("ImportLoader:",plugin_file) # # Load the module # module = None plugin_name = os.path.basename(plugin_file[:-3]) if plugin_name not in sys.modules and name_re.match( plugin_name): try: module = imp.load_source(plugin_name, plugin_file) if generate_debug_messages: env.log.debug('Loading file plugin %s from %s' % \ (plugin_name, plugin_file)) except Exception: e = sys.exc_info()[1] env.log.error('Failed to load plugin from %s', plugin_file, exc_info=True) env.log.error('Load error: %r' % str(e)) # # Disable singleton plugins that match # if not module is None: if not disable_re.match(plugin_name) is None: if generate_debug_messages: env.log.debug('Disabling services in module %s' % plugin_name) for item in dir(module): # # This seems like a hack, but # without this we can disable pyutilib # functionality! # flag = False for service in ImportLoader.ep_services: if service.ignore(item): flag = True break if flag: continue cls = getattr(module, item) try: is_instance = isinstance(cls, Plugin) except TypeError: #pragma:nocover is_instance = False try: is_plugin = issubclass(cls, Plugin) except TypeError: is_plugin = False try: is_singleton = not (cls.__instance__ is None) except AttributeError: #pragma:nocover is_singleton = False if is_singleton and is_plugin: if generate_debug_messages: env.log.debug('Disabling service %s' % item) cls.__instance__._enable = False if is_instance: if generate_debug_messages: env.log.debug('Disabling service %s' % item) cls._enable = False elif generate_debug_messages: env.log.debug('All services in module %s are enabled' % plugin_name)