def test_default(self): config = Configuration(self.filename) self.assertEquals('', config.get('a', 'option')) self.assertEquals('value', config.get('a', 'option', 'value')) config.setdefault('a', 'option', 'value') self.assertEquals('value', config.get('a', 'option'))
def test_read_and_get(self): configfile = open(self.filename, 'w') configfile.writelines(['[a]\n', 'option = x\n', '\n']) configfile.close() config = Configuration(self.filename) self.assertEquals('x', config.get('a', 'option')) self.assertEquals('x', config.get('a', 'option', 'y'))
def test_reparse(self): configfile = open(self.filename, 'w') configfile.writelines(['[a]\n', 'option = x\n', '\n']) configfile.close() config = Configuration(self.filename) self.assertEquals('x', config.get('a', 'option')) time.sleep(1) # needed because of low mtime granularity configfile = open(self.filename, 'w') configfile.write('[a]\noption = y') configfile.close() config.parse_if_needed() self.assertEquals('y', config.get('a', 'option'))
def test_set_and_save(self): configfile = open(self.filename, 'w') configfile.close() config = Configuration(self.filename) config.set('a', 'option', 'x') self.assertEquals('x', config.get('a', 'option')) config.save() configfile = open(self.filename, 'r') self.assertEquals(['[a]\n', 'option = x\n', '\n'], configfile.readlines()) configfile.close()
def writeconfig(self, filepath, dicts=[]): """Writes or updates a config file. A list of dictionaries is used so that options for different aspects of the configuration can be kept separate while being able to update the same sections. Note that the result is order dependent where two dictionaries update the same option. """ config = Configuration(filepath) file_changed = False for data in dicts: for section, options in data.iteritems(): for key, value in options.iteritems(): if config.get(section, key, None) != value: # This should be expected to generate a false positive # when two dictionaries update the same option file_changed = True config.set(section, key, value) if file_changed: if os.path.exists(filepath): backupfile(filepath) config.save()
def diff(file1, file2, ignored_sections=None, ignore_absent=False): """ :param file1: Filename :param file2: Filename :param list ignored_sections: List of ignored sections :param bool ignore_absent: Disables absent key reporting """ if ignored_sections is None: ignored_sections = [] if not os.path.exists(file1): raise ValueError('file %s does not exists' % file1) if not os.path.exists(file2): raise ValueError('file %s does not exists' % file2) conf1 = Configuration(file1) conf2 = Configuration(file2) fn1 = os.path.split(file1)[1] fn2 = os.path.split(file2)[1] conf1_sections = set(conf1.sections()) - set(ignored_sections) conf2_sections = set(conf2.sections()) - set(ignored_sections) for section in conf1.sections(): if section not in conf2_sections: print 'SECTION: %s not in %s' % (section, fn2) default = object() for section in conf1_sections: for key, value1 in conf1.options(section): if not conf2.has_option(section, key): if not ignore_absent: print '[%s] %s = %s is ABSENT from %s (but exists in %s)' % ( section, key, value1, fn2, fn1) else: value2 = conf2.get(section, key, default) if value2 != value1 and value2 is not default: print '[%s] %s = %s -> %s (%s -> %s)' % ( section, key, value1, value2, fn1, fn2)
def diff(file1, file2, ignored_sections=None, ignore_absent=False): """ :param file1: Filename :param file2: Filename :param list ignored_sections: List of ignored sections :param bool ignore_absent: Disables absent key reporting """ if ignored_sections is None: ignored_sections = [] if not os.path.exists(file1): raise ValueError("file %s does not exists" % file1) if not os.path.exists(file2): raise ValueError("file %s does not exists" % file2) conf1 = Configuration(file1) conf2 = Configuration(file2) fn1 = os.path.split(file1)[1] fn2 = os.path.split(file2)[1] conf1_sections = set(conf1.sections()) - set(ignored_sections) conf2_sections = set(conf2.sections()) - set(ignored_sections) for section in conf1.sections(): if section not in conf2_sections: print "SECTION: %s not in %s" % (section, fn2) default = object() for section in conf1_sections: for key, value1 in conf1.options(section): if not conf2.has_option(section, key): if not ignore_absent: print "[%s] %s = %s is ABSENT from %s (but exists in %s)" % (section, key, value1, fn2, fn1) else: value2 = conf2.get(section, key, default) if value2 != value1 and value2 is not default: print "[%s] %s = %s -> %s (%s -> %s)" % (section, key, value1, value2, fn1, fn2)
def get_home_config(self, mainchapter, subchapter): syspath = self.conf.getEnvironmentSysPath("home") getconf = Configuration(syspath + '/conf/trac.ini') return getconf.get(mainchapter, subchapter)
class Environment(Component, ComponentManager): """Trac stores project information in a Trac environment. A Trac environment consists of a directory structure containing among other things: * a configuration file. * an SQLite database (stores tickets, wiki pages...) * Project specific templates and wiki macros. * wiki and ticket attachments. """ setup_participants = ExtensionPoint(IEnvironmentSetupParticipant) def __init__(self, path, create=False, db_str=None): """Initialize the Trac environment. @param path: the absolute path to the Trac environment @param create: if `True`, the environment is created and populated with default data; otherwise, the environment is expected to already exist. @param db_str: the database connection string """ ComponentManager.__init__(self) self.path = path self.__cnx_pool = None if create: self.create(db_str) else: self.verify() self.load_config() self.setup_log() from trac.loader import load_components load_components(self) if create: for setup_participant in self.setup_participants: setup_participant.environment_created() def component_activated(self, component): """Initialize additional member variables for components. Every component activated through the `Environment` object gets three member variables: `env` (the environment object), `config` (the environment configuration) and `log` (a logger object).""" component.env = self component.config = self.config component.log = self.log def is_component_enabled(self, cls): """Implemented to only allow activation of components that are not disabled in the configuration. This is called by the `ComponentManager` base class when a component is about to be activated. If this method returns false, the component does not get activated.""" if not isinstance(cls, (str, unicode)): component_name = (cls.__module__ + '.' + cls.__name__).lower() else: component_name = cls.lower() rules = [(name.lower(), value.lower() in ('enabled', 'on')) for name, value in self.config.options('components')] rules.sort(lambda a, b: -cmp(len(a[0]), len(b[0]))) for pattern, enabled in rules: if component_name == pattern or pattern.endswith('*') \ and component_name.startswith(pattern[:-1]): return enabled # By default, all components in the trac package are enabled return component_name.startswith('trac.') def verify(self): """Verify that the provided path points to a valid Trac environment directory.""" fd = open(os.path.join(self.path, 'VERSION'), 'r') assert fd.read(26) == 'Trac Environment Version 1' fd.close() def get_db_cnx(self): """Return a database connection from the connection pool.""" if not self.__cnx_pool: self.__cnx_pool = db.get_cnx_pool(self) return self.__cnx_pool.get_cnx() def shutdown(self): """Close the environment.""" if self.__cnx_pool: self.__cnx_pool.shutdown() self.__cnx_pool = None def get_repository(self, authname=None): """Return the version control repository configured for this environment. The repository is wrapped in a `CachedRepository`. @param authname: user name for authorization """ from trac.versioncontrol.cache import CachedRepository from trac.versioncontrol.svn_authz import SubversionAuthorizer from trac.versioncontrol.svn_fs import SubversionRepository repos_dir = self.config.get('trac', 'repository_dir') if not repos_dir: raise EnvironmentError, 'Path to repository not configured' authz = None if authname: authz = SubversionAuthorizer(self, authname) repos = SubversionRepository(repos_dir, authz, self.log) return CachedRepository(self.get_db_cnx(), repos, authz, self.log) def create(self, db_str=None): """Create the basic directory structure of the environment, initialize the database and populate the configuration file with default values.""" def _create_file(fname, data=None): fd = open(fname, 'w') if data: fd.write(data) fd.close() # Create the directory structure os.mkdir(self.path) os.mkdir(self.get_log_dir()) os.mkdir(self.get_htdocs_dir()) os.mkdir(os.path.join(self.path, 'plugins')) os.mkdir(os.path.join(self.path, 'wiki-macros')) # Create a few files _create_file(os.path.join(self.path, 'VERSION'), 'Trac Environment Version 1\n') _create_file(os.path.join(self.path, 'README'), 'This directory contains a Trac environment.\n' 'Visit http://trac.edgewall.com/ for more information.\n') # Setup the default configuration os.mkdir(os.path.join(self.path, 'conf')) _create_file(os.path.join(self.path, 'conf', 'trac.ini')) self.load_config() for section, name, value in db_default.default_config: self.config.set(section, name, value) self.config.set('trac', 'database', db_str) self.config.save() # Create the database db.init_db(self.path, db_str) def get_version(self, db=None): """Return the current version of the database.""" if not db: db = self.get_db_cnx() cursor = db.cursor() cursor.execute("SELECT value FROM system WHERE name='database_version'") row = cursor.fetchone() return row and int(row[0]) def load_config(self): """Load the configuration file.""" self.config = Configuration(os.path.join(self.path, 'conf', 'trac.ini')) for section, name, value in db_default.default_config: self.config.setdefault(section, name, value) def get_templates_dir(self): """Return absolute path to the templates directory.""" return os.path.join(self.path, 'templates') def get_htdocs_dir(self): """Return absolute path to the htdocs directory.""" return os.path.join(self.path, 'htdocs') def get_log_dir(self): """Return absolute path to the log directory.""" return os.path.join(self.path, 'log') def setup_log(self): """Initialize the logging sub-system.""" from trac.log import logger_factory logtype = self.config.get('logging', 'log_type') loglevel = self.config.get('logging', 'log_level') logfile = self.config.get('logging', 'log_file') if not os.path.isabs(logfile): logfile = os.path.join(self.get_log_dir(), logfile) logid = self.path # Env-path provides process-unique ID self.log = logger_factory(logtype, logfile, loglevel, logid) def get_known_users(self, cnx=None): """Generator that yields information about all known users, i.e. users that have logged in to this Trac environment and possibly set their name and email. This function generates one tuple for every user, of the form (username, name, email) ordered alpha-numerically by username. @param cnx: the database connection; if ommitted, a new connection is retrieved """ if not cnx: cnx = self.get_db_cnx() cursor = cnx.cursor() cursor.execute("SELECT DISTINCT s.sid, n.var_value, e.var_value " "FROM session AS s " " LEFT JOIN session AS n ON (n.sid=s.sid " " AND n.authenticated=1 AND n.var_name = 'name') " " LEFT JOIN session AS e ON (e.sid=s.sid " " AND e.authenticated=1 AND e.var_name = 'email') " "WHERE s.authenticated=1 ORDER BY s.sid") for username,name,email in cursor: yield username, name, email def backup(self, dest=None): """Simple SQLite-specific backup of the database. @param dest: Destination file; if not specified, the backup is stored in a file called db_name.trac_version.bak """ import shutil db_str = self.config.get('trac', 'database') if not db_str.startswith('sqlite:'): raise EnvironmentError, 'Can only backup sqlite databases' db_name = os.path.join(self.path, db_str[7:]) if not dest: dest = '%s.%i.bak' % (db_name, self.get_version()) shutil.copy (db_name, dest) def needs_upgrade(self): """Return whether the environment needs to be upgraded.""" db = self.get_db_cnx() for participant in self.setup_participants: if participant.environment_needs_upgrade(db): self.log.warning('Component %s requires environment upgrade', participant) return True return False def upgrade(self, backup=False, backup_dest=None): """Upgrade database. Each db version should have its own upgrade module, names upgrades/dbN.py, where 'N' is the version number (int). @param backup: whether or not to backup before upgrading @param backup_dest: name of the backup file @return: whether the upgrade was performed """ db = self.get_db_cnx() upgraders = [] for participant in self.setup_participants: if participant.environment_needs_upgrade(db): upgraders.append(participant) if not upgraders: return False if backup: self.backup(backup_dest) for participant in upgraders: participant.upgrade_environment(db) db.commit() # Database schema may have changed, so close all connections self.shutdown() return True
class MockEnvironment(Environment): """ An mock Environment object which does not need real environment. Useful for when needing real environment like access to loaded plugins etc but no environment is at hand. Looks like normal project by default but can be made to look like home project environment or hybrid of both. .. WARNING:: Be careful when using this! """ def __init__(self, config_file=None, enabled_plugins=None, path=None): """ :param str config_file: path to the main configuration file. Defaults to ``/etc/trac/project.ini`` which is normal project configuration. :param list enabled_plugins: An explicit list of plugins to load, instead of reading enabled [components] from ``config_file``. If empty list is given no plugins are loaded. However plugins can be still listed via ``extra_plugins`` arg. :param str path: path to the imaginary trac location. """ if config_file is None: # TODO: switch to some constant when can be done without conf from multiproject.core.configuration import conf config_file = conf.config_file # From Environment.setup_config: self.config = Configuration(config_file) if path is None: path = os.path.join( self.config.get('multiproject', 'sys_projects_root'), '__mock_environment__') if enabled_plugins is not None: # explicit list given, disable all from configuration for key, val in self.config.options('components'): self.config.remove('components', key) # and replace with the given list for plugin in enabled_plugins: self.config.set('components', plugin, u'enabled') # We override the Environment.__init__ here. # Environment.__init__ is as follows: # ComponentManager.__init__(self) # # self.path = path # self.systeminfo = [] # self._href = self._abs_href = None # # if create: # self.create(options) # else: # self.verify() # self.setup_config() # # if create: # for setup_participant in self.setup_participants: # setup_participant.environment_created() # The Environment.setup_config is as follows: # self.config = Configuration(os.path.join(self.path, 'conf', # 'trac.ini')) # self.setup_log() # from trac.loader import load_components # plugins_dir = self.shared_plugins_dir # load_components(self, plugins_dir and (plugins_dir,)) # We use suitable lines from these as follows: # From Environment.__init__: ComponentManager.__init__(self) self.path = path self.systeminfo = [] self._href = self._abs_href = None # Our own plugin hack ends here, and setup_config lines continue here self.setup_log() from trac.loader import load_components load_components(self)
def __init__(self): self.port = str(Configuration.get(self.config, "trachat", "port", "8081")) t = TraChatPlugin.thread() t.port(self.port) t.start()
class MockEnvironment(Environment): """ An mock Environment object which does not need real environment. Useful for when needing real environment like access to loaded plugins etc but no environment is at hand. Looks like normal project by default but can be made to look like home project environment or hybrid of both. .. WARNING:: Be careful when using this! """ def __init__(self, config_file=None, enabled_plugins=None, path=None): """ :param str config_file: path to the main configuration file. Defaults to ``/etc/trac/project.ini`` which is normal project configuration. :param list enabled_plugins: An explicit list of plugins to load, instead of reading enabled [components] from ``config_file``. If empty list is given no plugins are loaded. However plugins can be still listed via ``extra_plugins`` arg. :param str path: path to the imaginary trac location. """ if config_file is None: # TODO: switch to some constant when can be done without conf from multiproject.core.configuration import conf config_file = conf.config_file # From Environment.setup_config: self.config = Configuration(config_file) if path is None: path = os.path.join(self.config.get('multiproject', 'sys_projects_root'), '__mock_environment__') if enabled_plugins is not None: # explicit list given, disable all from configuration for key, val in self.config.options('components'): self.config.remove('components', key) # and replace with the given list for plugin in enabled_plugins: self.config.set('components', plugin, u'enabled') # We override the Environment.__init__ here. # Environment.__init__ is as follows: # ComponentManager.__init__(self) # # self.path = path # self.systeminfo = [] # self._href = self._abs_href = None # # if create: # self.create(options) # else: # self.verify() # self.setup_config() # # if create: # for setup_participant in self.setup_participants: # setup_participant.environment_created() # The Environment.setup_config is as follows: # self.config = Configuration(os.path.join(self.path, 'conf', # 'trac.ini')) # self.setup_log() # from trac.loader import load_components # plugins_dir = self.shared_plugins_dir # load_components(self, plugins_dir and (plugins_dir,)) # We use suitable lines from these as follows: # From Environment.__init__: ComponentManager.__init__(self) self.path = path self.systeminfo = [] self._href = self._abs_href = None # Our own plugin hack ends here, and setup_config lines continue here self.setup_log() from trac.loader import load_components load_components(self)