Exemple #1
0
 def run_listener(self):
     """Creates and runs a simple socket server."""
     try:
         from cf.ipc import IPCListener
     except AttributeError:
         self.ipc_listener = None
         return
     self.ipc_listener = IPCListener(self)
     t = threading.Thread(target=self.ipc_listener.serve_forever)
     t.setDaemon(True)
     gobject.idle_add(t.start)
Exemple #2
0
 def run_listener(self):
     """Creates and runs a simple socket server."""
     try:
         from cf.ipc import IPCListener
     except AttributeError:
         self.ipc_listener = None
         return
     self.ipc_listener = IPCListener(self)
     t = threading.Thread(target=self.ipc_listener.serve_forever)
     t.setDaemon(True)
     gobject.idle_add(t.start)
Exemple #3
0
class CFApplication(gobject.GObject):
    """Main application object.

    An instance of this class is accessible in almost every class in
    CrunchyFrog through the app attribute. It provides access
    to application internals.

    Instance attributes:
      config: Configuration
      userdb: User database
      plugins: Plugin registry
      datasources: Datasource information
    """
    def __init__(self, options):
        """Constructor.

        Parameter:
          options: Command line options as returned by optparse.
        """
        self.__gobject_init__()
        self._instances = []
        self.__icount = 0
        self.options = options
        self.icon_theme = gtk.icon_theme_get_default()

    def init(self):
        """Initializes the application"""
        self.cb = CFAppCallbacks()
        self.__shutdown_tasks = []
        self.config = Config(self, self.options.config)
        self.userdb = UserDB(self)
        try:
            self._check_version()
        except:
            logging.exception('Exception during version update:')
        self.run_listener()
        self.plugins = PluginManager(self)
        self.datasources = DatasourceManager(self)
        self.recent_manager = gtk.recent_manager_get_default()
        autocompletion.setup(self)

    def _check_version(self):
        """Run possible version updates."""
        version_file = os.path.join(USER_CONFIG_DIR, 'VERSION')
        if os.path.isfile(version_file):
            dir_version = open(version_file).read()
        else:
            # Upgrade to 0.4.0 regression
            version_file = os.path.expanduser('~/.crunchyfrog/VERSION')
            if os.path.isfile(version_file):
                dir_version = open(version_file).read()
            else:
                dir_version = None
            version_file = os.path.join(USER_CONFIG_DIR, 'VERSION')
        if dir_version is not None and dir_version != release.version:
            self._move_user_files(dir_version, release.version)
            self._datasources_db2url(dir_version, release.version)
            # Do upgrades here
            f = open(version_file, "w")
            f.write(release.version)
            f.close()

    def _move_user_files(self, dir_version, release):
        """Move user specific files and directories to new locations."""
        # Upgrade to 0.4.0: User specific files now life in proper locations
        #   following XDG Base Directory specification.
        import shutil
        import cf
        old_user_dir = os.path.expanduser('~/.crunchyfrog')
        logging.info('Migrating user dir %s', old_user_dir)
        if not os.path.exists(old_user_dir):
            return
        for fname in ['user.db', 'VERSION']:
            path = os.path.join(old_user_dir, fname)
            if not os.path.exists(path):
                continue
            shutil.move(path, os.path.join(cf.USER_CONFIG_DIR, fname))
        if os.path.exists(os.path.join(old_user_dir, 'plugins')):
            shutil.move(os.path.join(old_user_dir, 'plugins'),
                        os.path.join(cf.USER_DIR, 'plugins'))
        shutil.rmtree(old_user_dir)

    def _datasources_db2url(self, dir_version, release):
        """Move data source config from userdb to datasources.cfg."""
        # Upgrade to 0.4.0: Data source are no longer stored in userdb.
        # TODO: Remove this hook in some upcoming major version.
        import cPickle
        from cf.db import Datasource
        from cf.db.url import URL
        from cf.ui import dialogs
        manager = DatasourceManager(self)
        x = self.userdb.get_table_version('datasource')
        if x is None:  # already dropped
            return
        sql = ('select name, description, backend, options, password '
               'from datasource')
        attribs = ['database', 'host', 'port', 'user']
        try:
            self.userdb.cursor.execute(sql)
            attribs.append('password')
        except sqlite3.OperationalError:  # password column is already gone
            sql = ('select name, description, backend, options '
                   'from datasource')
            self.userdb.cursor.execute(sql)
        ldap_found = False
        for item in self.userdb.cursor.fetchall():
            old_backend = item[2].split('.')[-1]
            opts = cPickle.loads(str(item[3]))
            if old_backend in ('mssql', 'oracle', 'mysql', 'postgres'):
                url = URL(old_backend)
                for name in attribs:
                    if name in opts:
                        setattr(url, name, opts[name])
                if len(item) == 5:
                    url.password = item[4]
                ds = Datasource(manager)
                ds.url = url
                ds.name = item[0]
                ds.decription = item[1]
                ds.ask_for_password = opts.get('ask_for_password', False)
                manager.save(ds)
            elif old_backend == 'sqlite':
                url = URL(old_backend)
                url.database = opts.get('filename', None)
                ds = Datasource(manager)
                ds.url = url
                ds.name = item[0]
                ds.description = item[1]
                ds.ask_for_password = opts.get('ask_for_password', False)
                manager.save(ds)
            elif old_backend == 'ldap':
                ldap_found = True
        if ldap_found:
            dialogs.warning('Warning',
                            'Sorry, LDAP server are no longer supported.')
        self.userdb.drop_table('datasource')

    # ---
    # Instance handling
    # ---

    def new_instance(self, args=None, show=True):
        """Creates a new instances.

        Arguments:
          args: List of filenames to open in new instance (default: None).
          show: If True, instance.show() is called here (default: True).

        Returns:
          The new instance.
        """
        create_editor = not args
        instance = MainWindow(self, create_editor=create_editor)
        if args is None:
            args = []
        for arg in args:
            instance.editor_create(arg)
        self._instances.append(instance)
        instance.connect("destroy", self.on_instance_destroyed)
        if show:
            instance.show()
        self.cb.emit("instance-created", instance)
        return instance

    def on_instance_destroyed(self, instance):
        instances = self.get_data("instances")
        if instance in self._instances:
            self._instances.remove(instance)
        if len(self._instances) == 0:
            self.shutdown()

    def get_instance(self, instance_id):
        """Returns an instances identified by ID."""
        for instance in self._instances:
            if id(instance) == instance_id:
                return instance

    def get_instances(self):
        """Returns a list of active instances."""
        return self._instances

    # ---

    def manage_connections(self, win):
        """Displays a dialog to manage connections."""
        dlg = ConnectionsDialog(win)
        dlg.run()
        dlg.destroy()

    def preferences_show(self, win, mode="editor"):
        """Displays the preferences dialog"""
        dlg = PreferencesDialog(win, mode=mode)
        dlg.run()  # IGNORE:E1101 - Generic method
        dlg.destroy()  # IGNORE:E1101 - Generic method

    def register_shutdown_task(self, func, msg, *args, **kwargs):
        """Registers a new shutdown task.

        Shutdown tasks are executed when the shutdown method is
        called.

        Parameter:
          func: A callable to execute.
          msg:  A human readable message explaining what the task does.
          args, kwargs: arguments and keyword arguments for func (optional).
        """
        self.__shutdown_tasks.append((func, msg, args, kwargs))

    def run_listener(self):
        """Creates and runs a simple socket server."""
        try:
            from cf.ipc import IPCListener
        except AttributeError:
            self.ipc_listener = None
            return
        self.ipc_listener = IPCListener(self)
        t = threading.Thread(target=self.ipc_listener.serve_forever)
        t.setDaemon(True)
        gobject.idle_add(t.start)

    def show_help(self, topic='index'):
        """Opens a webbrowser with a help page.

        Arguments:
          topic: If given, the URL points to the selected topic.
        """
        url = os.path.join(MANUAL_URL, '%s.html' % topic)
        parsed = urlparse.urlparse(url)
        if parsed[0] in ('file', ''):
            local_path = parsed[2]
        else:
            local_path = url
        if not os.path.exists(local_path):
            dialogs.error(_(u"Documentation not found"),
                          _(u"Could not find documentation at %s.") % url)
        else:
            webbrowser.open(url)

    def shutdown(self):
        """Execute all shutdown tasks and quit application

        Shutdown tasks are called within a try-except-block. If an
        exception is raised it will be printed to stdout.
        """
        while self.__shutdown_tasks:
            func, msg, args, kwargs = self.__shutdown_tasks.pop()
            logging.info(msg)
            try:
                func(*args, **kwargs)
            except:
                import traceback
                traceback.print_exc()
                logging.error("Task failed: %s" % str(sys.exc_info()[1]))
        if self.ipc_listener is not None:
            self.ipc_listener.shutdown()
        gtk.main_quit()

    # ------------------
    # Resources helpers
    # ------------------

    def get_glade_file(self, filename):
        """Expands filename to full path pointing to Glade file."""
        return os.path.join(DATA_DIR, 'glade', filename)

    def load_icon(self, icon_name, size=None, lookup_method=None):
        """Wrapper for gtk.IconTheme.load_icon that catches GError."""
        if size is None:
            size = gtk.ICON_SIZE_MENU
        if lookup_method is None:
            lookup_method = (gtk.ICON_LOOKUP_FORCE_SVG
                             | gtk.ICON_LOOKUP_USE_BUILTIN
                             | gtk.ICON_LOOKUP_GENERIC_FALLBACK)
        if sys.platform.startswith('win'):
            lookup_method = (gtk.ICON_LOOKUP_NO_SVG
                             | gtk.ICON_LOOKUP_USE_BUILTIN
                             | gtk.ICON_LOOKUP_GENERIC_FALLBACK)
        try:
            return self.icon_theme.load_icon(icon_name, size, lookup_method)
        except gobject.GError, err:
            logging.warning(err)
            return self.icon_theme.load_icon('gtk-missing-image', size,
                                             lookup_method)
Exemple #4
0
class CFApplication(gobject.GObject):
    """Main application object.

    An instance of this class is accessible in almost every class in
    CrunchyFrog through the app attribute. It provides access
    to application internals.

    Instance attributes:
      config: Configuration
      userdb: User database
      plugins: Plugin registry
      datasources: Datasource information
    """

    def __init__(self, options):
        """Constructor.

        Parameter:
          options: Command line options as returned by optparse.
        """
        self.__gobject_init__()
        self._instances = []
        self.__icount = 0
        self.options = options
        self.icon_theme = gtk.icon_theme_get_default()

    def init(self):
        """Initializes the application"""
        self.cb = CFAppCallbacks()
        self.__shutdown_tasks = []
        self.config = Config(self, self.options.config)
        self.userdb = UserDB(self)
        try:
            self._check_version()
        except:
            logging.exception("Exception during version update:")
        self.run_listener()
        self.plugins = PluginManager(self)
        self.datasources = DatasourceManager(self)
        self.recent_manager = gtk.recent_manager_get_default()
        autocompletion.setup(self)

    def _check_version(self):
        """Run possible version updates."""
        version_file = os.path.join(USER_CONFIG_DIR, "VERSION")
        if os.path.isfile(version_file):
            dir_version = open(version_file).read()
        else:
            # Upgrade to 0.4.0 regression
            version_file = os.path.expanduser("~/.crunchyfrog/VERSION")
            if os.path.isfile(version_file):
                dir_version = open(version_file).read()
            else:
                dir_version = None
            version_file = os.path.join(USER_CONFIG_DIR, "VERSION")
        if dir_version is not None and dir_version != release.version:
            self._move_user_files(dir_version, release.version)
            self._datasources_db2url(dir_version, release.version)
            # Do upgrades here
            f = open(version_file, "w")
            f.write(release.version)
            f.close()

    def _move_user_files(self, dir_version, release):
        """Move user specific files and directories to new locations."""
        # Upgrade to 0.4.0: User specific files now life in proper locations
        #   following XDG Base Directory specification.
        import shutil
        import cf

        old_user_dir = os.path.expanduser("~/.crunchyfrog")
        logging.info("Migrating user dir %s", old_user_dir)
        if not os.path.exists(old_user_dir):
            return
        for fname in ["user.db", "VERSION"]:
            path = os.path.join(old_user_dir, fname)
            if not os.path.exists(path):
                continue
            shutil.move(path, os.path.join(cf.USER_CONFIG_DIR, fname))
        if os.path.exists(os.path.join(old_user_dir, "plugins")):
            shutil.move(os.path.join(old_user_dir, "plugins"), os.path.join(cf.USER_DIR, "plugins"))
        shutil.rmtree(old_user_dir)

    def _datasources_db2url(self, dir_version, release):
        """Move data source config from userdb to datasources.cfg."""
        # Upgrade to 0.4.0: Data source are no longer stored in userdb.
        # TODO: Remove this hook in some upcoming major version.
        import cPickle
        from cf.db import Datasource
        from cf.db.url import URL
        from cf.ui import dialogs

        manager = DatasourceManager(self)
        x = self.userdb.get_table_version("datasource")
        if x is None:  # already dropped
            return
        sql = "select name, description, backend, options, password " "from datasource"
        attribs = ["database", "host", "port", "user"]
        try:
            self.userdb.cursor.execute(sql)
            attribs.append("password")
        except sqlite3.OperationalError:  # password column is already gone
            sql = "select name, description, backend, options " "from datasource"
            self.userdb.cursor.execute(sql)
        ldap_found = False
        for item in self.userdb.cursor.fetchall():
            old_backend = item[2].split(".")[-1]
            opts = cPickle.loads(str(item[3]))
            if old_backend in ("mssql", "oracle", "mysql", "postgres"):
                url = URL(old_backend)
                for name in attribs:
                    if name in opts:
                        setattr(url, name, opts[name])
                if len(item) == 5:
                    url.password = item[4]
                ds = Datasource(manager)
                ds.url = url
                ds.name = item[0]
                ds.decription = item[1]
                ds.ask_for_password = opts.get("ask_for_password", False)
                manager.save(ds)
            elif old_backend == "sqlite":
                url = URL(old_backend)
                url.database = opts.get("filename", None)
                ds = Datasource(manager)
                ds.url = url
                ds.name = item[0]
                ds.description = item[1]
                ds.ask_for_password = opts.get("ask_for_password", False)
                manager.save(ds)
            elif old_backend == "ldap":
                ldap_found = True
        if ldap_found:
            dialogs.warning("Warning", "Sorry, LDAP server are no longer supported.")
        self.userdb.drop_table("datasource")

    # ---
    # Instance handling
    # ---

    def new_instance(self, args=None, show=True):
        """Creates a new instances.

        Arguments:
          args: List of filenames to open in new instance (default: None).
          show: If True, instance.show() is called here (default: True).

        Returns:
          The new instance.
        """
        create_editor = not args
        instance = MainWindow(self, create_editor=create_editor)
        if args is None:
            args = []
        for arg in args:
            instance.editor_create(arg)
        self._instances.append(instance)
        instance.connect("destroy", self.on_instance_destroyed)
        if show:
            instance.show()
        self.cb.emit("instance-created", instance)
        return instance

    def on_instance_destroyed(self, instance):
        instances = self.get_data("instances")
        if instance in self._instances:
            self._instances.remove(instance)
        if len(self._instances) == 0:
            self.shutdown()

    def get_instance(self, instance_id):
        """Returns an instances identified by ID."""
        for instance in self._instances:
            if id(instance) == instance_id:
                return instance

    def get_instances(self):
        """Returns a list of active instances."""
        return self._instances

    # ---

    def manage_connections(self, win):
        """Displays a dialog to manage connections."""
        dlg = ConnectionsDialog(win)
        dlg.run()
        dlg.destroy()

    def preferences_show(self, win, mode="editor"):
        """Displays the preferences dialog"""
        dlg = PreferencesDialog(win, mode=mode)
        dlg.run()  # IGNORE:E1101 - Generic method
        dlg.destroy()  # IGNORE:E1101 - Generic method

    def register_shutdown_task(self, func, msg, *args, **kwargs):
        """Registers a new shutdown task.

        Shutdown tasks are executed when the shutdown method is
        called.

        Parameter:
          func: A callable to execute.
          msg:  A human readable message explaining what the task does.
          args, kwargs: arguments and keyword arguments for func (optional).
        """
        self.__shutdown_tasks.append((func, msg, args, kwargs))

    def run_listener(self):
        """Creates and runs a simple socket server."""
        try:
            from cf.ipc import IPCListener
        except AttributeError:
            self.ipc_listener = None
            return
        self.ipc_listener = IPCListener(self)
        t = threading.Thread(target=self.ipc_listener.serve_forever)
        t.setDaemon(True)
        gobject.idle_add(t.start)

    def show_help(self, topic="index"):
        """Opens a webbrowser with a help page.

        Arguments:
          topic: If given, the URL points to the selected topic.
        """
        url = os.path.join(MANUAL_URL, "%s.html" % topic)
        parsed = urlparse.urlparse(url)
        if parsed[0] in ("file", ""):
            local_path = parsed[2]
        else:
            local_path = url
        if not os.path.exists(local_path):
            dialogs.error(_(u"Documentation not found"), _(u"Could not find documentation at %s.") % url)
        else:
            webbrowser.open(url)

    def shutdown(self):
        """Execute all shutdown tasks and quit application

        Shutdown tasks are called within a try-except-block. If an
        exception is raised it will be printed to stdout.
        """
        while self.__shutdown_tasks:
            func, msg, args, kwargs = self.__shutdown_tasks.pop()
            logging.info(msg)
            try:
                func(*args, **kwargs)
            except:
                import traceback

                traceback.print_exc()
                logging.error("Task failed: %s" % str(sys.exc_info()[1]))
        if self.ipc_listener is not None:
            self.ipc_listener.shutdown()
        gtk.main_quit()

    # ------------------
    # Resources helpers
    # ------------------

    def get_glade_file(self, filename):
        """Expands filename to full path pointing to Glade file."""
        return os.path.join(DATA_DIR, "glade", filename)

    def load_icon(self, icon_name, size=None, lookup_method=None):
        """Wrapper for gtk.IconTheme.load_icon that catches GError."""
        if size is None:
            size = gtk.ICON_SIZE_MENU
        if lookup_method is None:
            lookup_method = gtk.ICON_LOOKUP_FORCE_SVG | gtk.ICON_LOOKUP_USE_BUILTIN | gtk.ICON_LOOKUP_GENERIC_FALLBACK
        if sys.platform.startswith("win"):
            lookup_method = gtk.ICON_LOOKUP_NO_SVG | gtk.ICON_LOOKUP_USE_BUILTIN | gtk.ICON_LOOKUP_GENERIC_FALLBACK
        try:
            return self.icon_theme.load_icon(icon_name, size, lookup_method)
        except gobject.GError, err:
            logging.warning(err)
            return self.icon_theme.load_icon("gtk-missing-image", size, lookup_method)