def create(self): """Create a new test environment. This sets up Trac, calls :meth:`create_repo` and sets up authentication. """ os.mkdir(self.dirname) # testing.log gets any unused output from subprocesses self.logfile = open(os.path.join(self.dirname, 'testing.log'), 'w') self.create_repo() config_file = os.path.join(self.dirname, 'config.ini') config = Configuration(config_file) config.set('repositories', '.dir', self.repo_path_for_initenv()) config.set('repositories', '.type', self.repotype) config.save() self._tracadmin('initenv', self.tracdir, self.dburi, '--config=%s' % config_file) if call([ sys.executable, os.path.join(self.trac_src, 'contrib', 'htpasswd.py'), "-c", "-b", self.htpasswd, "admin", "admin" ], close_fds=close_fds, cwd=self.command_cwd): raise Exception('Unable to setup admin password') self.adduser('user') self.adduser('joe') self.grant_perm('admin', 'TRAC_ADMIN') env = self.get_trac_environment() for component in self.get_enabled_components(): env.config.set('components', component, 'enabled') env.config.save() self.post_create(env)
def create(self, options=[], default_data=True): """Create the basic directory structure of the environment, initialize the database and populate the configuration file with default values. If options contains ('inherit', 'file'), default values will not be loaded; they are expected to be provided by that file or other options. :raises TracError: if the base directory of `path` does not exist. :raises TracError: if `path` exists and is not empty. """ base_dir = os.path.dirname(self.path) if not os.path.exists(base_dir): raise TracError( _( "Base directory '%(env)s' does not exist. Please create it " "and retry.", env=base_dir)) if os.path.exists(self.path) and os.listdir(self.path): raise TracError(_("Directory exists and is not empty.")) # Create the directory structure if not os.path.exists(self.path): os.mkdir(self.path) os.mkdir(self.htdocs_dir) os.mkdir(self.log_dir) os.mkdir(self.plugins_dir) os.mkdir(self.templates_dir) # Create a few files create_file(os.path.join(self.path, 'VERSION'), _VERSION + '\n') create_file( os.path.join(self.path, 'README'), 'This directory contains a Trac environment.\n' 'Visit https://trac.edgewall.org/ for more information.\n') # Setup the default configuration os.mkdir(self.conf_dir) config = Configuration(self.config_file_path) for section, name, value in options: config.set(section, name, value) config.save() self.setup_config() if not any((section, option) == ('inherit', 'file') for section, option, value in options): self.config.set_defaults(self) self.config.save() # Create the sample configuration create_file(self.config_file_path + '.sample') self._update_sample_config() # Create the database dbm = DatabaseManager(self) dbm.init_db() if default_data: dbm.insert_default_data()
def set_home_config(self, values): syspath = self.conf.getEnvironmentSysPath("home") setconf = Configuration(syspath + '/conf/trac.ini') try: for (main, sub, value) in values: setconf.set(main, sub, value) setconf.save() except: return False return True
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 _update_sample_config(self): filename = os.path.join(self.env.config_file_path + ".sample") if not os.path.isfile(filename): return config = Configuration(filename) for (section, name), option in Option.get_registry().iteritems(): config.set(section, name, option.dumps(option.default)) try: config.save() self.log.info( "Wrote sample configuration file with the new " "settings and their default values: %s", filename ) except IOError as e: self.log.warn("Couldn't write sample configuration file (%s)", e, exc_info=True)
def _update_sample_config(self): filename = os.path.join(self.env.path, 'conf', 'trac.ini.sample') if not os.path.isfile(filename): return config = Configuration(filename) for (section, name), option in Option.get_registry().iteritems(): config.set(section, name, option.dumps(option.default)) try: config.save() self.log.info("Wrote sample configuration file with the new " "settings and their default values: %s", filename) except IOError as e: self.log.warn("Couldn't write sample configuration file (%s)", e, exc_info=True)
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()
class EnvironmentStub(ComponentManager): """A stub of the trac.env.Environment object for testing.""" def __init__(self, default_data=False, enable=None): ComponentManager.__init__(self) self.enabled_components = enable self.db = InMemoryDatabase() from trac.config import Configuration self.config = Configuration(None) from trac.log import logger_factory self.log = logger_factory('test') from trac.web.href import Href self.href = Href('/trac.cgi') self.abs_href = Href('http://example.org/trac.cgi') from trac import db_default for section, name, value in db_default.default_config: self.config.set(section, name, value) if default_data: cursor = self.db.cursor() for table, cols, vals in db_default.data: cursor.executemany( "INSERT INTO %s (%s) VALUES (%s)" % (table, ','.join(cols), ','.join(['%s' for c in cols])), vals) self.db.commit() def component_activated(self, component): component.env = self component.config = self.config component.log = self.log def is_component_enabled(self, cls): if self.enabled_components is None: return True return cls in self.enabled_components def get_db_cnx(self): return self.db
def create(self, options=[]): """Create the basic directory structure of the environment, initialize the database and populate the configuration file with default values. If options contains ('inherit', 'file'), default values will not be loaded; they are expected to be provided by that file or other options. """ # Create the directory structure if not os.path.exists(self.path): os.mkdir(self.path) os.mkdir(self.get_log_dir()) os.mkdir(self.get_htdocs_dir()) os.mkdir(os.path.join(self.path, 'plugins')) # Create a few files create_file(os.path.join(self.path, 'VERSION'), _VERSION + '\n') create_file( os.path.join(self.path, 'README'), 'This directory contains a Trac environment.\n' 'Visit http://trac.edgewall.org/ 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.sample')) config = Configuration(os.path.join(self.path, 'conf', 'trac.ini')) for section, name, value in options: config.set(section, name, value) config.save() self.setup_config() if not any((section, option) == ('inherit', 'file') for section, option, value in options): self.config.set_defaults(self) self.config.save() # Create the database DatabaseManager(self).init_db()
def create(self, options=[]): """Create the basic directory structure of the environment, initialize the database and populate the configuration file with default values. If options contains ('inherit', 'file'), default values will not be loaded; they are expected to be provided by that file or other options. """ # Create the directory structure if not os.path.exists(self.path): os.mkdir(self.path) os.mkdir(self.get_log_dir()) os.mkdir(self.get_htdocs_dir()) os.mkdir(os.path.join(self.path, 'plugins')) # Create a few files create_file(os.path.join(self.path, 'VERSION'), _VERSION + '\n') create_file(os.path.join(self.path, 'README'), 'This directory contains a Trac environment.\n' 'Visit http://trac.edgewall.org/ for more information.\n') # Setup the default configuration os.mkdir(os.path.join(self.path, 'conf')) create_file(self.config_file_path + '.sample') config = Configuration(self.config_file_path) for section, name, value in options: config.set(section, name, value) config.save() self.setup_config() if not any((section, option) == ('inherit', 'file') for section, option, value in options): self.config.set_defaults(self) self.config.save() # Create the database DatabaseManager(self).init_db()
def do(self): # Make backup of trac.ini before configuring it try: shutil.copy(self.conf_file, self.conf_file_back) except Exception: conf.log.exception("Could not create trac.ini backup") return False # Open trac.ini for configuration config = None try: config = Configuration(self.conf_file) except Exception: conf.log.exception("Error while reading config file!") return False # Enable correct plugin for repository try: vcs_plugin = self.__vcs_plugin() if vcs_plugin: config.set('components', vcs_plugin, 'enabled') config.set('trac', 'repository_type', self.vcs_type) except Exception: conf.log.exception("Could not set static settings for trac") return False try: config.set('project', 'descr', self.project.description) except Exception: conf.log.exception("Couldn't set description") return False # Remove attachment size (to enable global setting) try: config.remove("attachment", "max_size") except Exception: conf.log.exception("Could not remove attachment config property for a new project") # Save configuration try: config.save() except Exception: conf.log.exception("Failed to save configuration") return False self.success = True return True
class EnvironmentStub(Environment): """A stub of the trac.env.Environment object for testing.""" href = abs_href = None global_databasemanager = None def __init__(self, default_data=False, enable=None, disable=None, path=None, destroying=False): """Construct a new Environment stub object. :param default_data: If True, populate the database with some defaults. :param enable: A list of component classes or name globs to activate in the stub environment. """ ComponentManager.__init__(self) Component.__init__(self) self.systeminfo = [] import trac self.path = path if self.path is None: self.path = os.path.dirname(trac.__file__) if not os.path.isabs(self.path): self.path = os.path.join(os.getcwd(), self.path) # -- configuration self.config = Configuration(None) # We have to have a ticket-workflow config for ''lots'' of things to # work. So insert the basic-workflow config here. There may be a # better solution than this. load_workflow_config_snippet(self.config, 'basic-workflow.ini') self.config.set('logging', 'log_level', 'DEBUG') self.config.set('logging', 'log_type', 'stderr') if enable is not None: self.config.set('components', 'trac.*', 'disabled') else: self.config.set('components', 'tracopt.versioncontrol.svn.*', 'enabled') for name_or_class in enable or (): config_key = self._component_name(name_or_class) self.config.set('components', config_key, 'enabled') for name_or_class in disable or (): config_key = self._component_name(name_or_class) self.config.set('components', config_key, 'disabled') # -- logging from trac.log import logger_handler_factory self.log, self._log_handler = logger_handler_factory('test') # -- database self.config.set('components', 'trac.db.*', 'enabled') self.dburi = get_dburi() init_global = False if self.global_databasemanager: self.components[DatabaseManager] = global_databasemanager else: self.config.set('trac', 'database', self.dburi) self.global_databasemanager = DatabaseManager(self) self.config.set('trac', 'debug_sql', True) self.config.set('logging', 'log_type', 'stderr') self.config.set('logging', 'log_level', 'DEBUG') init_global = not destroying if default_data or init_global: self.reset_db(default_data) from trac.web.href import Href self.href = Href('/trac.cgi') self.abs_href = Href('http://example.org/trac.cgi') self.known_users = [] translation.activate(locale_en) def reset_db(self, default_data=None): """Remove all data from Trac tables, keeping the tables themselves. :param default_data: after clean-up, initialize with default data :return: True upon success """ from trac import db_default scheme, db_prop = _parse_db_str(self.dburi) tables = [] remove_sqlite_db = False try: with self.db_transaction as db: db.rollback() # make sure there's no transaction in progress # check the database version database_version = db( "SELECT value FROM system WHERE name='database_version'") if database_version: database_version = int(database_version[0][0]) if database_version == db_default.db_version: # same version, simply clear the tables (faster) m = sys.modules[__name__] reset_fn = 'reset_%s_db' % scheme if hasattr(m, reset_fn): tables = getattr(m, reset_fn)(self, db_prop) else: # different version or version unknown, drop the tables remove_sqlite_db = True self.destroy_db(scheme, db_prop) except Exception, e: # "Database not found ...", # "OperationalError: no such table: system" or the like pass db = None # as we might shutdown the pool FIXME no longer needed! if scheme == 'sqlite' and remove_sqlite_db: path = db_prop['path'] if path != ':memory:': if not os.path.isabs(path): path = os.path.join(self.path, path) self.global_databasemanager.shutdown() os.remove(path) if not tables: self.global_databasemanager.init_db() # we need to make sure the next get_db_cnx() will re-create # a new connection aware of the new data model - see #8518. if self.dburi != 'sqlite::memory:': self.global_databasemanager.shutdown() with self.db_transaction as db: if default_data: for table, cols, vals in db_default.get_data(db): db.executemany( "INSERT INTO %s (%s) VALUES (%s)" % (table, ','.join(cols), ','.join(['%s' for c in cols])), vals) else: db("INSERT INTO system (name, value) VALUES (%s, %s)", ('database_version', str(db_default.db_version)))
class EnvironmentStub(Environment): """A stub of the trac.env.Environment object for testing.""" href = abs_href = None global_databasemanager = None def __init__(self, default_data=False, enable=None, disable=None, path=None, destroying=False): """Construct a new Environment stub object. :param default_data: If True, populate the database with some defaults. :param enable: A list of component classes or name globs to activate in the stub environment. """ ComponentManager.__init__(self) Component.__init__(self) self.systeminfo = [] import trac self.path = path if self.path is None: self.path = os.path.dirname(trac.__file__) if not os.path.isabs(self.path): self.path = os.path.join(os.getcwd(), self.path) # -- configuration self.config = Configuration(None) # We have to have a ticket-workflow config for ''lots'' of things to # work. So insert the basic-workflow config here. There may be a # better solution than this. load_workflow_config_snippet(self.config, 'basic-workflow.ini') self.config.set('logging', 'log_level', 'DEBUG') self.config.set('logging', 'log_type', 'stderr') if enable is not None: self.config.set('components', 'trac.*', 'disabled') else: self.config.set('components', 'tracopt.versioncontrol.svn.*', 'enabled') for name_or_class in enable or (): config_key = self._component_name(name_or_class) self.config.set('components', config_key, 'enabled') for name_or_class in disable or (): config_key = self._component_name(name_or_class) self.config.set('components', config_key, 'disabled') # -- logging from trac.log import logger_handler_factory self.log, self._log_handler = logger_handler_factory('test') # -- database self.config.set('components', 'trac.db.*', 'enabled') self.dburi = get_dburi() init_global = False if self.global_databasemanager: self.components[DatabaseManager] = self.global_databasemanager else: self.config.set('trac', 'database', self.dburi) self.global_databasemanager = DatabaseManager(self) self.config.set('trac', 'debug_sql', True) self.config.set('logging', 'log_type', 'stderr') self.config.set('logging', 'log_level', 'DEBUG') init_global = not destroying if default_data or init_global: self.reset_db(default_data) from trac.web.href import Href self.href = Href('/trac.cgi') self.abs_href = Href('http://example.org/trac.cgi') self.known_users = [] translation.activate(locale_en) def reset_db(self, default_data=None): """Remove all data from Trac tables, keeping the tables themselves. :param default_data: after clean-up, initialize with default data :return: True upon success """ from trac import db_default scheme, db_prop = _parse_db_str(self.dburi) tables = [] remove_sqlite_db = False try: with self.db_transaction as db: db.rollback() # make sure there's no transaction in progress # check the database version database_version = db( "SELECT value FROM system WHERE name='database_version'") if database_version: database_version = int(database_version[0][0]) if database_version == db_default.db_version: # same version, simply clear the tables (faster) m = sys.modules[__name__] reset_fn = 'reset_%s_db' % scheme if hasattr(m, reset_fn): tables = getattr(m, reset_fn)(self, db_prop) else: # different version or version unknown, drop the tables remove_sqlite_db = True self.destroy_db(scheme, db_prop) except Exception, e: # "Database not found ...", # "OperationalError: no such table: system" or the like pass db = None # as we might shutdown the pool FIXME no longer needed! if scheme == 'sqlite' and remove_sqlite_db: path = db_prop['path'] if path != ':memory:': if not os.path.isabs(path): path = os.path.join(self.path, path) self.global_databasemanager.shutdown() os.remove(path) if not tables: self.global_databasemanager.init_db() # we need to make sure the next get_db_cnx() will re-create # a new connection aware of the new data model - see #8518. if self.dburi != 'sqlite::memory:': self.global_databasemanager.shutdown() with self.db_transaction as db: if default_data: for table, cols, vals in db_default.get_data(db): db.executemany("INSERT INTO %s (%s) VALUES (%s)" % (table, ','.join(cols), ','.join(['%s' for c in cols])), vals) else: db("INSERT INTO system (name, value) VALUES (%s, %s)", ('database_version', str(db_default.db_version)))
class EnvironmentStub(Environment): """A stub of the trac.env.Environment class for testing.""" required = False abstract = True def __init__(self, default_data=False, enable=None, disable=None, path=None, destroying=False, config=None): """Construct a new Environment stub object. :param default_data: If True, populate the database with some defaults. :param enable: A list of component classes or name globs to activate in the stub environment. :param disable: A list of component classes or name globs to deactivate in the stub environment. :param path: The location of the environment in the file system. No files or directories are created when specifying this parameter. :param destroying: If True, the database will not be reset. This is useful for cases when the object is being constructed in order to call `destroy_db`. :param config: A list of (section, key, value) configuration tuples. """ if enable is not None and not isinstance(enable, (list, tuple)): raise TypeError('Keyword argument "enable" must be a list') if disable is not None and not isinstance(disable, (list, tuple)): raise TypeError('Keyword argument "disable" must be a list') ComponentManager.__init__(self) self._old_registry = None self._old_components = None import trac self.path = path if self.path is None: self.path = os.path.abspath(os.path.dirname(trac.__file__)) self.path = os.path.normpath(os.path.normcase(self.path)) # -- configuration self.config = Configuration(None) # We have to have a ticket-workflow config for ''lots'' of things to # work. So insert the basic-workflow config here. There may be a # better solution than this. load_workflow_config_snippet(self.config, 'basic-workflow.ini') self.config.set('logging', 'log_level', 'DEBUG') self.config.set('logging', 'log_type', 'none') # Ignored. if enable is not None: self.config.set('components', 'trac.*', 'disabled') else: self.config.set('components', 'tracopt.versioncontrol.*', 'enabled') for name_or_class in enable or (): config_key = self._component_name(name_or_class) self.config.set('components', config_key, 'enabled') for name_or_class in disable or (): config_key = self._component_name(name_or_class) self.config.set('components', config_key, 'disabled') self.config.set('trac', 'permission_policies', 'DefaultPermissionPolicy, LegacyAttachmentPolicy') for item in config or []: self.config.set(*item) # -- logging self.setup_log() # -- database self.dburi = get_dburi() self.config.set('components', 'trac.db.*', 'enabled') self.config.set('trac', 'database', self.dburi) if not destroying: self.reset_db(default_data) self.config.set('trac', 'base_url', 'http://example.org/trac.cgi') translation.activate(locale_en) def setup_log(self): self.log = logging.getLogger('trac.test') level = self.log_level.upper() level_as_int = trac.log.LOG_LEVEL_MAP.get(level) self.log.setLevel(level_as_int) handler_cls = logging.handlers.BufferingHandler if not self.log.handlers: log_handler = handler_cls(sys.maxsize) # Never flush implicitly. formatter = logging.Formatter(self.log_format) log_handler.setFormatter(formatter) self.log.addHandler(log_handler) elif len(self.log.handlers) == 1 and \ isinstance(self.log.handlers[0], handler_cls): self.log.handlers[0].flush() # Reset buffer. else: raise TracError("Logger has unexpected handler(s).") @property def log_messages(self): """Returns a list of tuples (level, message).""" return [(record.levelname, record.getMessage()) for record in self.log.handlers[0].buffer] def reset_db(self, default_data=None): """Remove all data from Trac tables, keeping the tables themselves. :param default_data: after clean-up, initialize with default data :return: True upon success """ from trac import db_default tables = [] dbm = DatabaseManager(self) try: db_version = dbm.get_database_version() except (TracError, self.db_exc.DatabaseError): pass else: if db_version == db_default.db_version: # same version, simply clear the tables (faster) tables = dbm.reset_tables() else: # different version or version unknown, drop the tables self.destroy_db() if not tables: dbm.init_db() # Make sure the next db_query()/db_transaction() will create # a new connection aware of the new data model - see #8518. if self.dburi != 'sqlite::memory:': dbm.shutdown() if default_data: dbm.insert_into_tables(db_default.get_data) else: dbm.set_database_version(db_default.db_version) def destroy_db(self): """Destroy the database.""" try: DatabaseManager(self).destroy_db() except (TracError, self.db_exc.DatabaseError): pass def clear_component_registry(self): """Clear the component registry. The registry entries are saved entries so they can be restored later using the `restore_component_registry` method. :since: 1.0.11 :since 1.3.2: Deprecated and will be removed in 1.5.1. Create components in `setUpClass` and remove them in `tearDownClass` using `ComponentMeta.deregister`. """ self._old_registry = ComponentMeta._registry self._old_components = ComponentMeta._components ComponentMeta._registry = {} def restore_component_registry(self): """Restore the component registry. The component registry must have been cleared and saved using the `clear_component_registry` method. :since: 1.0.11 :since 1.3.2: Deprecated and will be removed in 1.5.1. Create components in `setUpClass` and remove them in `tearDownClass` using `ComponentMeta.deregister`. """ if self._old_registry is None: raise TracError("The clear_component_registry method must be " "called first.") ComponentMeta._registry = self._old_registry ComponentMeta._components = self._old_components # tearDown helper def reset_db_and_disk(self): """Performs a complete environment reset in a robust way. The database is reset, then the connections are shut down, and finally all environment files are removed from the disk. """ self.reset_db() self.shutdown() # really closes the db connections rmtree(self.env.path) if self._old_registry is not None: self.restore_component_registry() # other utilities def insert_users(self, users): """Insert a tuple representing a user session to the `session` and `session_attributes` tables. The tuple can be length 3 with entries username, name and email, in which case an authenticated user is assumed. The tuple can also be length 4, with the last entry specifying `1` for an authenticated user or `0` for an unauthenticated user. """ with self.db_transaction as db: for row in users: if len(row) == 3: username, name, email = row authenticated = 1 else: # len(row) == 4 username, name, email, authenticated = row db("INSERT INTO session VALUES (%s, %s, %s)", (username, authenticated, int(time_now()))) db("INSERT INTO session_attribute VALUES (%s,%s,'name',%s)", (username, authenticated, name)) db("INSERT INTO session_attribute VALUES (%s,%s,'email',%s)", (username, authenticated, email)) # overridden def is_component_enabled(self, cls): if self._component_name(cls).startswith('__main__.'): return True return Environment.is_component_enabled(self, cls)
class EnvironmentStub(Environment): """A stub of the trac.env.Environment object for testing.""" global_databasemanager = None required = False def __init__(self, default_data=False, enable=None, disable=None, path=None, destroying=False): """Construct a new Environment stub object. :param default_data: If True, populate the database with some defaults. :param enable: A list of component classes or name globs to activate in the stub environment. :param disable: A list of component classes or name globs to deactivate in the stub environment. :param path: The location of the environment in the file system. No files or directories are created when specifying this parameter. :param destroying: If True, the database will not be reset. This is useful for cases when the object is being constructed in order to call `destroy_db`. """ if enable is not None and not isinstance(enable, (list, tuple)): raise TypeError('Keyword argument "enable" must be a list') if disable is not None and not isinstance(disable, (list, tuple)): raise TypeError('Keyword argument "disable" must be a list') ComponentManager.__init__(self) self.systeminfo = [] import trac self.path = path if self.path is None: self.path = os.path.dirname(trac.__file__) if not os.path.isabs(self.path): self.path = os.path.join(os.getcwd(), self.path) # -- configuration self.config = Configuration(None) # We have to have a ticket-workflow config for ''lots'' of things to # work. So insert the basic-workflow config here. There may be a # better solution than this. load_workflow_config_snippet(self.config, 'basic-workflow.ini') self.config.set('logging', 'log_level', 'DEBUG') self.config.set('logging', 'log_type', 'stderr') if enable is not None: self.config.set('components', 'trac.*', 'disabled') else: self.config.set('components', 'tracopt.versioncontrol.*', 'enabled') for name_or_class in enable or (): config_key = self._component_name(name_or_class) self.config.set('components', config_key, 'enabled') for name_or_class in disable or (): config_key = self._component_name(name_or_class) self.config.set('components', config_key, 'disabled') # -- logging from trac.log import logger_handler_factory self.log, self._log_handler = logger_handler_factory('test') # -- database self.config.set('components', 'trac.db.*', 'enabled') self.dburi = get_dburi() init_global = False if self.global_databasemanager: self.components[DatabaseManager] = self.global_databasemanager else: self.config.set('trac', 'database', self.dburi) self.global_databasemanager = DatabaseManager(self) self.config.set('trac', 'debug_sql', True) init_global = not destroying if default_data or init_global: self.reset_db(default_data) self.config.set('trac', 'base_url', 'http://example.org/trac.cgi') self.known_users = [] translation.activate(locale_en) def reset_db(self, default_data=None): """Remove all data from Trac tables, keeping the tables themselves. :param default_data: after clean-up, initialize with default data :return: True upon success """ from trac import db_default scheme, db_prop = _parse_db_str(self.dburi) tables = [] remove_sqlite_db = False try: with self.db_transaction as db: db.rollback() # make sure there's no transaction in progress # check the database version database_version = self.get_version() except Exception: # "Database not found ...", # "OperationalError: no such table: system" or the like pass else: if database_version == db_default.db_version: # same version, simply clear the tables (faster) m = sys.modules[__name__] reset_fn = 'reset_%s_db' % scheme if hasattr(m, reset_fn): tables = getattr(m, reset_fn)(self, db_prop) else: # different version or version unknown, drop the tables remove_sqlite_db = True self.destroy_db(scheme, db_prop) if scheme == 'sqlite' and remove_sqlite_db: path = db_prop['path'] if path != ':memory:': if not os.path.isabs(path): path = os.path.join(self.path, path) self.global_databasemanager.shutdown() os.remove(path) if not tables: self.global_databasemanager.init_db() # we need to make sure the next get_db_cnx() will re-create # a new connection aware of the new data model - see #8518. if self.dburi != 'sqlite::memory:': self.global_databasemanager.shutdown() with self.db_transaction as db: if default_data: for table, cols, vals in db_default.get_data(db): db.executemany("INSERT INTO %s (%s) VALUES (%s)" % (table, ','.join(cols), ','.join(['%s'] * len(cols))), vals) else: db("INSERT INTO system (name, value) VALUES (%s, %s)", ('database_version', str(db_default.db_version))) def destroy_db(self, scheme=None, db_prop=None): if not (scheme and db_prop): scheme, db_prop = _parse_db_str(self.dburi) try: with self.db_transaction as db: if scheme == 'postgres' and db.schema: db('DROP SCHEMA %s CASCADE' % db.quote(db.schema)) elif scheme == 'mysql': for table in db.get_table_names(): db("DROP TABLE IF EXISTS `%s`" % table) except Exception: # "TracError: Database not found...", # psycopg2.ProgrammingError: schema "tractest" does not exist pass return False # overridden def is_component_enabled(self, cls): if self._component_name(cls).startswith('__main__.'): return True return Environment.is_component_enabled(self, cls) def get_known_users(self, cnx=None): return self.known_users
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)
class EnvironmentStub(Environment): """A stub of the trac.env.Environment object for testing.""" href = abs_href = None dbenv = db = None def __init__(self, default_data=False, enable=None): """Construct a new Environment stub object. :param default_data: If True, populate the database with some defaults. :param enable: A list of component classes or name globs to activate in the stub environment. """ ComponentManager.__init__(self) Component.__init__(self) self.systeminfo = [] import trac self.path = os.path.dirname(trac.__file__) if not os.path.isabs(self.path): self.path = os.path.join(os.getcwd(), self.path) # -- configuration self.config = Configuration(None) # We have to have a ticket-workflow config for ''lots'' of things to # work. So insert the basic-workflow config here. There may be a # better solution than this. load_workflow_config_snippet(self.config, 'basic-workflow.ini') self.config.set('logging', 'log_level', 'DEBUG') self.config.set('logging', 'log_type', 'stderr') if enable is not None: self.config.set('components', 'trac.*', 'disabled') for name_or_class in enable or (): config_key = self._component_name(name_or_class) self.config.set('components', config_key, 'enabled') # -- logging from trac.log import logger_handler_factory self.log, self._log_handler = logger_handler_factory('test') # -- database self.dburi = get_dburi() if self.dburi.startswith('sqlite'): self.config.set('trac', 'database', 'sqlite::memory:') self.db = InMemoryDatabase() if default_data: self.reset_db(default_data) from trac.web.href import Href self.href = Href('/trac.cgi') self.abs_href = Href('http://example.org/trac.cgi') self.known_users = [] translation.activate(Locale and Locale('en', 'US')) def get_read_db(self): return self.get_db_cnx() def get_db_cnx(self, destroying=False): if self.db: return self.db # in-memory SQLite # As most of the EnvironmentStubs are built at startup during # the test suite formation and the creation of test cases, we can't # afford to create a real db connection for each instance. # So we create a special EnvironmentStub instance in charge of # getting the db connections for all the other instances. dbenv = EnvironmentStub.dbenv if not dbenv: dbenv = EnvironmentStub.dbenv = EnvironmentStub() dbenv.config.set('trac', 'database', self.dburi) if not destroying: self.reset_db() # make sure we get rid of previous garbage return DatabaseManager(dbenv).get_connection() def reset_db(self, default_data=None): """Remove all data from Trac tables, keeping the tables themselves. :param default_data: after clean-up, initialize with default data :return: True upon success """ from trac import db_default if EnvironmentStub.dbenv: db = self.get_db_cnx() scheme, db_prop = _parse_db_str(self.dburi) tables = [] db.rollback() # make sure there's no transaction in progress try: # check the database version cursor = db.cursor() cursor.execute("SELECT value FROM system " "WHERE name='database_version'") database_version = cursor.fetchone() if database_version: database_version = int(database_version[0]) if database_version == db_default.db_version: # same version, simply clear the tables (faster) m = sys.modules[__name__] reset_fn = 'reset_%s_db' % scheme if hasattr(m, reset_fn): tables = getattr(m, reset_fn)(db, db_prop) else: # different version or version unknown, drop the tables self.destroy_db(scheme, db_prop) except: db.rollback() # tables are likely missing if not tables: del db dm = DatabaseManager(EnvironmentStub.dbenv) dm.init_db() # we need to make sure the next get_db_cnx() will re-create # a new connection aware of the new data model - see #8518. dm.shutdown() db = self.get_db_cnx() cursor = db.cursor() if default_data: for table, cols, vals in db_default.get_data(db): cursor.executemany( "INSERT INTO %s (%s) VALUES (%s)" % (table, ','.join(cols), ','.join(['%s' for c in cols])), vals) elif EnvironmentStub.dbenv: cursor.execute( "INSERT INTO system (name, value) " "VALUES (%s, %s)", ('database_version', str(db_default.db_version))) db.commit() def destroy_db(self, scheme=None, db_prop=None): if not (scheme and db_prop): scheme, db_prop = _parse_db_str(self.dburi) db = self.get_db_cnx(destroying=True) cursor = db.cursor() try: if scheme == 'postgres' and db.schema: cursor.execute('DROP SCHEMA "%s" CASCADE' % db.schema) elif scheme == 'mysql': dbname = os.path.basename(db_prop['path']) cursor = db.cursor() cursor.execute( 'SELECT table_name FROM ' ' information_schema.tables ' 'WHERE table_schema=%s', (dbname, )) tables = cursor.fetchall() for t in tables: cursor.execute('DROP TABLE IF EXISTS `%s`' % t) db.commit() except Exception: db.rollback() # overriden def is_component_enabled(self, cls): if self._component_name(cls).startswith('__main__.'): return True return Environment.is_component_enabled(self, cls) def get_known_users(self, cnx=None): return self.known_users
def sync(self, project): """Sync the trac environment with cydra This sets the options returned by ``get_default_options`` and adds Trac's own defaults if necessary""" if not self.has_env(project): logger.warning('Project %s has no Trac Environment to sync', project.name) return tracini = os.path.join(self.get_env_path(project), 'conf', 'trac.ini') options = self.get_default_options(project) # if inherit is enabled, the default values are supposed to be in # the inherited file. Thus, we can truncate the config file to get a bare minimum if 'inherit_config' in self.component_config: options.append( ('inherit', 'file', self.component_config['inherit_config'])) with open(tracini, 'w') as f: f.truncate() # re-create the configuration file config = Configuration(tracini) for section, name, value in options: config.set(section, name, value) config.save() # load defaults if not any((section, option) == ('inherit', 'file') for section, option, value in options): config.set_defaults() config.save() # check if repositories in cydra match repositories in trac env = Environment(self.get_env_path(project)) rm = RepositoryManager(env) trac_repos = rm.get_real_repositories() trac_repo_names = [r.reponame for r in trac_repos] for repotype, repos in project.data.get('plugins', {}).get('trac', {}).items(): for repo, tracname in (repos or {}).items(): if tracname not in trac_repo_names: logger.warning( "Removing trac mapping from cydra for %s repo %s", repo, tracname) del repos[repo] if not repos: del project.data.get('plugins', {}).get('trac', {})[repotype] # Now do the reverse revmap = dict([(y, x) for (x, y) in self.typemap.items()]) for repo in trac_repos: logger.debug('Looking at trac repo %s', repo.reponame) try: baseparts = repo.get_base().split( ':' ) # This is extremely naiive and possibly breaks some time repotype, path = baseparts[0], baseparts[-1] except: logger.error("Unable to parse: " + repo.get_base()) reponame = os.path.basename(path) if repotype == 'git': reponame = reponame[:-4] try: repository = project.get_repository(revmap[repotype], reponame) except: logger.error("Unable to locate %s %s (%s)", repotype, reponame, path) repository = None logger.debug('Cydra repo %r', repository) if repository: # set this mapping if not there already project.data.setdefault('plugins', {}).setdefault( 'trac', {}).setdefault(repository.type, {})[repository.name] = repo.reponame logger.info('Setting trac mapping for %s %s -> %s', repository.type, repository.name, repo.reponame) else: logger.error("Unable to load %s %s (%s)", revmap[repotype], reponame, path) project.save()
def _do_update(self, req): """Update component enablement.""" components = req.args.getlist('component') oenabled = req.args.getlist('enable') penabled = req.args.getlist('prjenable') changes = False changed = {} # Set global project configuration prjconf = Configuration(conf.global_conf_path) for component in components: c_state = self.get_project_component_state(component, self.config) c_activated = (component in oenabled) if self.is_plugin_changed(c_state, c_activated): self.config.set('components', component, c_activated and 'enabled' or 'disabled') self.log.info('%sabling component %s', c_activated and 'En' or 'Dis', component) changes = True if prjconf: cip_state = self.get_project_component_state(component, prjconf) cip_activated = (component in penabled) if self.is_plugin_changed(cip_state, cip_activated): self.log.info('%sabling project component %s', cip_activated and 'En' or 'Dis', component) changed[component] = cip_activated and 'enabled' or 'disabled' changes = True if prjconf: scomponents = req.args.getlist('setting') static_items = req.args.getlist('static_setting') for scomponent in scomponents: values = self.get_project_component_value(scomponent, None, prjconf) saved_value = self.parse_value(0, values) saved_static = self.parse_value(1, values) current_value = req.args.get(scomponent + '.value').replace('|', '') current_static = (scomponent in static_items) if saved_value != current_value or saved_static != current_static: if current_static: final_value = current_value else: final_value = current_value + '|no' prjconf.set('settings', scomponent, final_value) changes = True if changes: self.config.save() if prjconf: for key in changed.keys(): prjconf.set('components', key, changed[key]) prjconf.save()
def _do_update(self, req): """Update component enablement.""" components = req.args.getlist('component') oenabled = req.args.getlist('enable') penabled = req.args.getlist('prjenable') changes = False changed = {} # Set global project configuration prjconf = Configuration(conf.global_conf_path) for component in components: c_state = self.get_project_component_state(component, self.config) c_activated = (component in oenabled) if self.is_plugin_changed(c_state, c_activated): self.config.set('components', component, c_activated and 'enabled' or 'disabled') self.log.info('%sabling component %s', c_activated and 'En' or 'Dis', component) changes = True if prjconf: cip_state = self.get_project_component_state( component, prjconf) cip_activated = (component in penabled) if self.is_plugin_changed(cip_state, cip_activated): self.log.info('%sabling project component %s', cip_activated and 'En' or 'Dis', component) changed[ component] = cip_activated and 'enabled' or 'disabled' changes = True if prjconf: scomponents = req.args.getlist('setting') static_items = req.args.getlist('static_setting') for scomponent in scomponents: values = self.get_project_component_value( scomponent, None, prjconf) saved_value = self.parse_value(0, values) saved_static = self.parse_value(1, values) current_value = req.args.get(scomponent + '.value').replace( '|', '') current_static = (scomponent in static_items) if saved_value != current_value or saved_static != current_static: if current_static: final_value = current_value else: final_value = current_value + '|no' prjconf.set('settings', scomponent, final_value) changes = True if changes: self.config.save() if prjconf: for key in changed.keys(): prjconf.set('components', key, changed[key]) prjconf.save()
class EnvironmentStub(Environment): """A stub of the trac.env.Environment class for testing.""" global_databasemanager = None required = False abstract = True def __init__(self, default_data=False, enable=None, disable=None, path=None, destroying=False): """Construct a new Environment stub object. :param default_data: If True, populate the database with some defaults. :param enable: A list of component classes or name globs to activate in the stub environment. :param disable: A list of component classes or name globs to deactivate in the stub environment. :param path: The location of the environment in the file system. No files or directories are created when specifying this parameter. :param destroying: If True, the database will not be reset. This is useful for cases when the object is being constructed in order to call `destroy_db`. """ if enable is not None and not isinstance(enable, (list, tuple)): raise TypeError('Keyword argument "enable" must be a list') if disable is not None and not isinstance(disable, (list, tuple)): raise TypeError('Keyword argument "disable" must be a list') ComponentManager.__init__(self) self.systeminfo = [] import trac self.path = path if self.path is None: self.path = os.path.dirname(trac.__file__) if not os.path.isabs(self.path): self.path = os.path.join(os.getcwd(), self.path) # -- configuration self.config = Configuration(None) # We have to have a ticket-workflow config for ''lots'' of things to # work. So insert the basic-workflow config here. There may be a # better solution than this. load_workflow_config_snippet(self.config, 'basic-workflow.ini') self.config.set('logging', 'log_level', 'DEBUG') self.config.set('logging', 'log_type', 'stderr') if enable is not None: self.config.set('components', 'trac.*', 'disabled') else: self.config.set('components', 'tracopt.versioncontrol.*', 'enabled') for name_or_class in enable or (): config_key = self._component_name(name_or_class) self.config.set('components', config_key, 'enabled') for name_or_class in disable or (): config_key = self._component_name(name_or_class) self.config.set('components', config_key, 'disabled') # -- logging from trac.log import logger_handler_factory self.log, self._log_handler = logger_handler_factory('test') # -- database self.config.set('components', 'trac.db.*', 'enabled') self.dburi = get_dburi() init_global = False if self.global_databasemanager: self.components[DatabaseManager] = self.global_databasemanager else: self.config.set('trac', 'database', self.dburi) self.global_databasemanager = DatabaseManager(self) self.config.set('trac', 'debug_sql', True) init_global = not destroying if default_data or init_global: self.reset_db(default_data) self.config.set('trac', 'base_url', 'http://example.org/trac.cgi') translation.activate(locale_en) def reset_db(self, default_data=None): """Remove all data from Trac tables, keeping the tables themselves. :param default_data: after clean-up, initialize with default data :return: True upon success """ from trac import db_default tables = [] dbm = self.global_databasemanager try: with self.db_transaction as db: db.rollback() # make sure there's no transaction in progress # check the database version db_version = dbm.get_database_version() except (TracError, self.env.db_exc.DatabaseError): pass else: if db_version == db_default.db_version: # same version, simply clear the tables (faster) tables = dbm.reset_tables() else: # different version or version unknown, drop the tables self.destroy_db() if not tables: dbm.init_db() # we need to make sure the next get_db_cnx() will re-create # a new connection aware of the new data model - see #8518. if self.dburi != 'sqlite::memory:': dbm.shutdown() if default_data: dbm.insert_into_tables(db_default.get_data) else: dbm.set_database_version(db_default.db_version) def destroy_db(self, scheme=None, db_prop=None): """Destroy the database. :since 1.1.5: the `scheme` and `db_prop` parameters are deprecated and will be removed in 1.3.1. """ try: self.global_databasemanager.destroy_db() except (TracError, self.db_exc.DatabaseError): pass return False def insert_known_users(self, users): with self.env.db_transaction as db: for username, name, email in users: db("INSERT INTO session VALUES (%s, %s, %s)", (username, 1, int(time.time()))) db("INSERT INTO session_attribute VALUES (%s,%s,'name',%s)", (username, 1, name)) db("INSERT INTO session_attribute VALUES (%s,%s,'email',%s)", (username, 1, email)) # overridden def is_component_enabled(self, cls): if self._component_name(cls).startswith('__main__.'): return True return Environment.is_component_enabled(self, cls)
class EnvironmentStub(Environment): """A stub of the trac.env.Environment object for testing.""" href = abs_href = None dbenv = db = None def __init__(self, default_data=False, enable=None): """Construct a new Environment stub object. :param default_data: If True, populate the database with some defaults. :param enable: A list of component classes or name globs to activate in the stub environment. """ ComponentManager.__init__(self) Component.__init__(self) self.systeminfo = [] import trac self.path = os.path.dirname(trac.__file__) if not os.path.isabs(self.path): self.path = os.path.join(os.getcwd(), self.path) # -- configuration self.config = Configuration(None) # We have to have a ticket-workflow config for ''lots'' of things to # work. So insert the basic-workflow config here. There may be a # better solution than this. load_workflow_config_snippet(self.config, 'basic-workflow.ini') self.config.set('logging', 'log_level', 'DEBUG') self.config.set('logging', 'log_type', 'stderr') if enable is not None: self.config.set('components', 'trac.*', 'disabled') for name_or_class in enable or (): config_key = self._component_name(name_or_class) self.config.set('components', config_key, 'enabled') # -- logging from trac.log import logger_handler_factory self.log, self._log_handler = logger_handler_factory('test') # -- database self.dburi = get_dburi() if self.dburi.startswith('sqlite'): self.config.set('trac', 'database', 'sqlite::memory:') self.db = InMemoryDatabase() if default_data: self.reset_db(default_data) from trac.web.href import Href self.href = Href('/trac.cgi') self.abs_href = Href('http://example.org/trac.cgi') self.known_users = [] translation.activate(Locale and Locale('en', 'US')) def get_read_db(self): return self.get_db_cnx() def get_db_cnx(self, destroying=False): if self.db: return self.db # in-memory SQLite # As most of the EnvironmentStubs are built at startup during # the test suite formation and the creation of test cases, we can't # afford to create a real db connection for each instance. # So we create a special EnvironmentStub instance in charge of # getting the db connections for all the other instances. dbenv = EnvironmentStub.dbenv if not dbenv: dbenv = EnvironmentStub.dbenv = EnvironmentStub() dbenv.config.set('trac', 'database', self.dburi) if not destroying: self.reset_db() # make sure we get rid of previous garbage return DatabaseManager(dbenv).get_connection() def reset_db(self, default_data=None): """Remove all data from Trac tables, keeping the tables themselves. :param default_data: after clean-up, initialize with default data :return: True upon success """ from trac import db_default if EnvironmentStub.dbenv: db = self.get_db_cnx() scheme, db_prop = _parse_db_str(self.dburi) tables = [] db.rollback() # make sure there's no transaction in progress try: # check the database version cursor = db.cursor() cursor.execute("SELECT value FROM system " "WHERE name='database_version'") database_version = cursor.fetchone() if database_version: database_version = int(database_version[0]) if database_version == db_default.db_version: # same version, simply clear the tables (faster) m = sys.modules[__name__] reset_fn = 'reset_%s_db' % scheme if hasattr(m, reset_fn): tables = getattr(m, reset_fn)(db, db_prop) else: # different version or version unknown, drop the tables self.destroy_db(scheme, db_prop) except: db.rollback() # tables are likely missing if not tables: del db dm = DatabaseManager(EnvironmentStub.dbenv) dm.init_db() # we need to make sure the next get_db_cnx() will re-create # a new connection aware of the new data model - see #8518. dm.shutdown() db = self.get_db_cnx() cursor = db.cursor() if default_data: for table, cols, vals in db_default.get_data(db): cursor.executemany("INSERT INTO %s (%s) VALUES (%s)" % (table, ','.join(cols), ','.join(['%s' for c in cols])), vals) elif EnvironmentStub.dbenv: cursor.execute("INSERT INTO system (name, value) " "VALUES (%s, %s)", ('database_version', str(db_default.db_version))) db.commit() def destroy_db(self, scheme=None, db_prop=None): if not (scheme and db_prop): scheme, db_prop = _parse_db_str(self.dburi) db = self.get_db_cnx(destroying=True) cursor = db.cursor() try: if scheme == 'postgres' and db.schema: cursor.execute('DROP SCHEMA "%s" CASCADE' % db.schema) elif scheme == 'mysql': dbname = os.path.basename(db_prop['path']) cursor = db.cursor() cursor.execute('SELECT table_name FROM ' ' information_schema.tables ' 'WHERE table_schema=%s', (dbname,)) tables = cursor.fetchall() for t in tables: cursor.execute('DROP TABLE IF EXISTS `%s`' % t) db.commit() except Exception: db.rollback() # overriden def is_component_enabled(self, cls): if self._component_name(cls).startswith('__main__.'): return True return Environment.is_component_enabled(self, cls) def get_known_users(self, cnx=None): return self.known_users
def sync(self, project): """Sync the trac environment with cydra This sets the options returned by ``get_default_options`` and adds Trac's own defaults if necessary""" if not self.has_env(project): logger.warning('Project %s has no Trac Environment to sync', project.name) return tracini = os.path.join(self.get_env_path(project), 'conf', 'trac.ini') options = self.get_default_options(project) # if inherit is enabled, the default values are supposed to be in # the inherited file. Thus, we can truncate the config file to get a bare minimum if 'inherit_config' in self.component_config: options.append(('inherit', 'file', self.component_config['inherit_config'])) with open(tracini, 'w') as f: f.truncate() # re-create the configuration file config = Configuration(tracini) for section, name, value in options: config.set(section, name, value) config.save() # load defaults if not any((section, option) == ('inherit', 'file') for section, option, value in options): config.set_defaults() config.save() # check if repositories in cydra match repositories in trac env = Environment(self.get_env_path(project)) rm = RepositoryManager(env) trac_repos = rm.get_real_repositories() trac_repo_names = [r.reponame for r in trac_repos] for repotype, repos in project.data.get('plugins', {}).get('trac', {}).items(): for repo, tracname in (repos or {}).items(): if tracname not in trac_repo_names: logger.warning("Removing trac mapping from cydra for %s repo %s", repo, tracname) del repos[repo] if not repos: del project.data.get('plugins', {}).get('trac', {})[repotype] # Now do the reverse revmap = dict([(y, x) for (x, y) in self.typemap.items()]) for repo in trac_repos: logger.debug('Looking at trac repo %s', repo.reponame) try: baseparts = repo.get_base().split(':') # This is extremely naiive and possibly breaks some time repotype, path = baseparts[0], baseparts[-1] except: logger.error("Unable to parse: " + repo.get_base()) reponame = os.path.basename(path) if repotype == 'git': reponame = reponame[:-4] try: repository = project.get_repository(revmap[repotype], reponame) except: logger.error("Unable to locate %s %s (%s)", repotype, reponame, path) repository = None logger.debug('Cydra repo %r', repository) if repository: # set this mapping if not there already project.data.setdefault('plugins', {}).setdefault('trac', {}).setdefault(repository.type, {})[repository.name] = repo.reponame logger.info('Setting trac mapping for %s %s -> %s', repository.type, repository.name, repo.reponame) else: logger.error("Unable to load %s %s (%s)", revmap[repotype], reponame, path) project.save()
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)
class EnvironmentStub(Environment): """A stub of the trac.env.Environment object for testing.""" href = abs_href = None global_databasemanager = None required = False def __init__(self, default_data=False, enable=None, disable=None, path=None, destroying=False): """Construct a new Environment stub object. :param default_data: If True, populate the database with some defaults. :param enable: A list of component classes or name globs to activate in the stub environment. :param disable: A list of component classes or name globs to deactivate in the stub environment. :param path: The location of the environment in the file system. No files or directories are created when specifying this parameter. :param destroying: If True, the database will not be reset. This is useful for cases when the object is being constructed in order to call `destroy_db`. """ if enable is not None and not isinstance(enable, (list, tuple)): raise TypeError('Keyword argument "enable" must be a list') if disable is not None and not isinstance(disable, (list, tuple)): raise TypeError('Keyword argument "disable" must be a list') ComponentManager.__init__(self) Component.__init__(self) self.systeminfo = [] import trac self.path = path if self.path is None: self.path = os.path.dirname(trac.__file__) if not os.path.isabs(self.path): self.path = os.path.join(os.getcwd(), self.path) # -- configuration self.config = Configuration(None) # We have to have a ticket-workflow config for ''lots'' of things to # work. So insert the basic-workflow config here. There may be a # better solution than this. load_workflow_config_snippet(self.config, 'basic-workflow.ini') self.config.set('logging', 'log_level', 'DEBUG') self.config.set('logging', 'log_type', 'stderr') if enable is not None: self.config.set('components', 'trac.*', 'disabled') else: self.config.set('components', 'tracopt.versioncontrol.*', 'enabled') for name_or_class in enable or (): config_key = self._component_name(name_or_class) self.config.set('components', config_key, 'enabled') for name_or_class in disable or (): config_key = self._component_name(name_or_class) self.config.set('components', config_key, 'disabled') # -- logging from trac.log import logger_handler_factory self.log, self._log_handler = logger_handler_factory('test') # -- database self.config.set('components', 'trac.db.*', 'enabled') self.dburi = get_dburi() init_global = False if self.global_databasemanager: self.components[DatabaseManager] = self.global_databasemanager else: self.config.set('trac', 'database', self.dburi) self.global_databasemanager = DatabaseManager(self) self.config.set('trac', 'debug_sql', True) self.config.set('logging', 'log_type', 'stderr') self.config.set('logging', 'log_level', 'DEBUG') init_global = not destroying if default_data or init_global: self.reset_db(default_data) from trac.web.href import Href self.href = Href('/trac.cgi') self.abs_href = Href('http://example.org/trac.cgi') self.known_users = [] translation.activate(locale_en) def reset_db(self, default_data=None): """Remove all data from Trac tables, keeping the tables themselves. :param default_data: after clean-up, initialize with default data :return: True upon success """ from trac import db_default scheme, db_prop = _parse_db_str(self.dburi) tables = [] remove_sqlite_db = False try: with self.db_transaction as db: db.rollback() # make sure there's no transaction in progress # check the database version database_version = self.get_version() except Exception: # "Database not found ...", # "OperationalError: no such table: system" or the like pass else: if database_version == db_default.db_version: # same version, simply clear the tables (faster) m = sys.modules[__name__] reset_fn = 'reset_%s_db' % scheme if hasattr(m, reset_fn): tables = getattr(m, reset_fn)(self, db_prop) else: # different version or version unknown, drop the tables remove_sqlite_db = True self.destroy_db(scheme, db_prop) db = None # as we might shutdown the pool FIXME no longer needed! if scheme == 'sqlite' and remove_sqlite_db: path = db_prop['path'] if path != ':memory:': if not os.path.isabs(path): path = os.path.join(self.path, path) self.global_databasemanager.shutdown() os.remove(path) if not tables: self.global_databasemanager.init_db() # we need to make sure the next get_db_cnx() will re-create # a new connection aware of the new data model - see #8518. if self.dburi != 'sqlite::memory:': self.global_databasemanager.shutdown() with self.db_transaction as db: if scheme == 'sqlite': # Speed-up tests with SQLite database db("PRAGMA synchronous = OFF") if default_data: for table, cols, vals in db_default.get_data(db): db.executemany( "INSERT INTO %s (%s) VALUES (%s)" % (table, ','.join(cols), ','.join(['%s'] * len(cols))), vals) else: db("INSERT INTO system (name, value) VALUES (%s, %s)", ('database_version', str(db_default.db_version))) def destroy_db(self, scheme=None, db_prop=None): if not (scheme and db_prop): scheme, db_prop = _parse_db_str(self.dburi) try: with self.db_transaction as db: if scheme == 'postgres' and db.schema: db('DROP SCHEMA %s CASCADE' % db.quote(db.schema)) elif scheme == 'mysql': for table in db.get_table_names(): db("DROP TABLE IF EXISTS `%s`" % table) except Exception: # "TracError: Database not found...", # psycopg2.ProgrammingError: schema "tractest" does not exist pass return False # overridden def is_component_enabled(self, cls): if self._component_name(cls).startswith('__main__.'): return True return Environment.is_component_enabled(self, cls) def get_known_users(self, cnx=None): return self.known_users
class EnvironmentStub(Environment): """A stub of the trac.env.Environment class for testing.""" global_databasemanager = None required = False def __init__(self, default_data=False, enable=None, disable=None, path=None, destroying=False): """Construct a new Environment stub object. :param default_data: If True, populate the database with some defaults. :param enable: A list of component classes or name globs to activate in the stub environment. :param disable: A list of component classes or name globs to deactivate in the stub environment. :param path: The location of the environment in the file system. No files or directories are created when specifying this parameter. :param destroying: If True, the database will not be reset. This is useful for cases when the object is being constructed in order to call `destroy_db`. """ if enable is not None and not isinstance(enable, (list, tuple)): raise TypeError('Keyword argument "enable" must be a list') if disable is not None and not isinstance(disable, (list, tuple)): raise TypeError('Keyword argument "disable" must be a list') ComponentManager.__init__(self) self.systeminfo = [] import trac self.path = path if self.path is None: self.path = os.path.dirname(trac.__file__) if not os.path.isabs(self.path): self.path = os.path.join(os.getcwd(), self.path) # -- configuration self.config = Configuration(None) # We have to have a ticket-workflow config for ''lots'' of things to # work. So insert the basic-workflow config here. There may be a # better solution than this. load_workflow_config_snippet(self.config, 'basic-workflow.ini') self.config.set('logging', 'log_level', 'DEBUG') self.config.set('logging', 'log_type', 'stderr') if enable is not None: self.config.set('components', 'trac.*', 'disabled') else: self.config.set('components', 'tracopt.versioncontrol.*', 'enabled') for name_or_class in enable or (): config_key = self._component_name(name_or_class) self.config.set('components', config_key, 'enabled') for name_or_class in disable or (): config_key = self._component_name(name_or_class) self.config.set('components', config_key, 'disabled') # -- logging from trac.log import logger_handler_factory self.log, self._log_handler = logger_handler_factory('test') # -- database self.config.set('components', 'trac.db.*', 'enabled') self.dburi = get_dburi() init_global = False if self.global_databasemanager: self.components[DatabaseManager] = self.global_databasemanager else: self.config.set('trac', 'database', self.dburi) self.global_databasemanager = DatabaseManager(self) self.config.set('trac', 'debug_sql', True) init_global = not destroying if default_data or init_global: self.reset_db(default_data) self.config.set('trac', 'base_url', 'http://example.org/trac.cgi') translation.activate(locale_en) def reset_db(self, default_data=None): """Remove all data from Trac tables, keeping the tables themselves. :param default_data: after clean-up, initialize with default data :return: True upon success """ from trac import db_default tables = [] dbm = self.global_databasemanager try: with self.db_transaction as db: db.rollback() # make sure there's no transaction in progress # check the database version db_version = dbm.get_database_version() except (TracError, self.env.db_exc.DatabaseError): pass else: if db_version == db_default.db_version: # same version, simply clear the tables (faster) tables = dbm.reset_tables() else: # different version or version unknown, drop the tables self.destroy_db() if not tables: dbm.init_db() # we need to make sure the next get_db_cnx() will re-create # a new connection aware of the new data model - see #8518. if self.dburi != 'sqlite::memory:': dbm.shutdown() if default_data: dbm.insert_into_tables(db_default.get_data) else: dbm.set_database_version(db_default.db_version) def destroy_db(self, scheme=None, db_prop=None): """Destroy the database. :since 1.1.5: the `scheme` and `db_prop` parameters are deprecated and will be removed in 1.3.1. """ try: self.global_databasemanager.destroy_db() except (TracError, self.db_exc.DatabaseError): pass return False def insert_known_users(self, users): with self.env.db_transaction as db: for username, name, email in users: db("INSERT INTO session VALUES (%s, %s, %s)", (username, 1, int(time.time()))) db("INSERT INTO session_attribute VALUES (%s,%s,'name',%s)", (username, 1, name)) db("INSERT INTO session_attribute VALUES (%s,%s,'email',%s)", (username, 1, email)) # overridden def is_component_enabled(self, cls): if self._component_name(cls).startswith('__main__.'): return True return Environment.is_component_enabled(self, cls)
class EnvironmentStub(Environment): """A stub of the trac.env.Environment class for testing.""" global_databasemanager = None # Deprecated, will be removed in 1.3.1 required = False abstract = True def __init__(self, default_data=False, enable=None, disable=None, path=None, destroying=False): """Construct a new Environment stub object. :param default_data: If True, populate the database with some defaults. :param enable: A list of component classes or name globs to activate in the stub environment. :param disable: A list of component classes or name globs to deactivate in the stub environment. :param path: The location of the environment in the file system. No files or directories are created when specifying this parameter. :param destroying: If True, the database will not be reset. This is useful for cases when the object is being constructed in order to call `destroy_db`. """ if enable is not None and not isinstance(enable, (list, tuple)): raise TypeError('Keyword argument "enable" must be a list') if disable is not None and not isinstance(disable, (list, tuple)): raise TypeError('Keyword argument "disable" must be a list') ComponentManager.__init__(self) self.systeminfo = [] self._old_registry = None self._old_components = None import trac self.path = path if self.path is None: self.path = os.path.dirname(trac.__file__) if not os.path.isabs(self.path): self.path = os.path.join(os.getcwd(), self.path) # -- configuration self.config = Configuration(None) # We have to have a ticket-workflow config for ''lots'' of things to # work. So insert the basic-workflow config here. There may be a # better solution than this. load_workflow_config_snippet(self.config, 'basic-workflow.ini') self.config.set('logging', 'log_level', 'DEBUG') self.config.set('logging', 'log_type', 'stderr') if enable is not None: self.config.set('components', 'trac.*', 'disabled') else: self.config.set('components', 'tracopt.versioncontrol.*', 'enabled') for name_or_class in enable or (): config_key = self._component_name(name_or_class) self.config.set('components', config_key, 'enabled') for name_or_class in disable or (): config_key = self._component_name(name_or_class) self.config.set('components', config_key, 'disabled') self.config.set('trac', 'permission_policies', 'DefaultPermissionPolicy, LegacyAttachmentPolicy') # -- logging from trac.log import logger_handler_factory self.log, self._log_handler = logger_handler_factory('test') self.log.addHandler(self._log_handler) # -- database self.dburi = get_dburi() self.config.set('components', 'trac.db.*', 'enabled') self.config.set('trac', 'database', self.dburi) self.config.set('trac', 'debug_sql', True) self.global_databasemanager = DatabaseManager(self) # Remove in 1.3.1 if not destroying: self.reset_db(default_data) self.config.set('trac', 'base_url', 'http://example.org/trac.cgi') translation.activate(locale_en) def reset_db(self, default_data=None): """Remove all data from Trac tables, keeping the tables themselves. :param default_data: after clean-up, initialize with default data :return: True upon success """ from trac import db_default tables = [] dbm = DatabaseManager(self) try: with self.db_transaction as db: db.rollback() # make sure there's no transaction in progress # check the database version db_version = dbm.get_database_version() except (TracError, self.db_exc.DatabaseError): pass else: if db_version == db_default.db_version: # same version, simply clear the tables (faster) tables = dbm.reset_tables() else: # different version or version unknown, drop the tables self.destroy_db() if not tables: dbm.init_db() # we need to make sure the next get_db_cnx() will re-create # a new connection aware of the new data model - see #8518. if self.dburi != 'sqlite::memory:': dbm.shutdown() if default_data: dbm.insert_into_tables(db_default.get_data) else: dbm.set_database_version(db_default.db_version) def destroy_db(self, scheme=None, db_prop=None): """Destroy the database. :since 1.1.5: the `scheme` and `db_prop` parameters are deprecated and will be removed in 1.3.1. """ try: DatabaseManager(self).destroy_db() except (TracError, self.db_exc.DatabaseError): pass def clear_component_registry(self): """Clear the component registry. The registry entries are saved entries so they can be restored later using the `restore_component_registry` method. :since: 1.0.11 """ self._old_registry = ComponentMeta._registry self._old_components = ComponentMeta._components ComponentMeta._registry = {} def restore_component_registry(self): """Restore the component registry. The component registry must have been cleared and saved using the `clear_component_registry` method. :since: 1.0.11 """ if self._old_registry is None: raise TracError("The clear_component_registry method must be " "called first.") ComponentMeta._registry = self._old_registry ComponentMeta._components = self._old_components # tearDown helper def reset_db_and_disk(self): """Performs a complete environment reset in a robust way. The database is reset, then the connections are shut down, and finally all environment files are removed from the disk. """ self.reset_db() self.shutdown() # really closes the db connections rmtree(self.env.path) if self._old_registry is not None: self.restore_component_registry() # other utilities def insert_users(self, users): """Insert a tuple representing a user session to the `session` and `session_attributes` tables. The tuple can be length 3 with entries username, name and email, in which case an authenticated user is assumed. The tuple can also be length 4, with the last entry specifying `1` for an authenticated user or `0` for an unauthenticated user. """ with self.db_transaction as db: for row in users: if len(row) == 3: username, name, email = row authenticated = 1 else: # len(row) == 4 username, name, email, authenticated = row db("INSERT INTO session VALUES (%s, %s, %s)", (username, authenticated, int(time_now()))) db("INSERT INTO session_attribute VALUES (%s,%s,'name',%s)", (username, authenticated, name)) db("INSERT INTO session_attribute VALUES (%s,%s,'email',%s)", (username, authenticated, email)) # overridden def is_component_enabled(self, cls): if self._component_name(cls).startswith('__main__.'): return True return Environment.is_component_enabled(self, cls)