def _start_managing(self): """ Start managing applications passed via argv. """ managing = False if self._nsargs.install: dependency = self._nsargs.install task = ParallelTask( self._avc.install, dependency) task.name = "AppInstall-%s" % (dependency,) task.daemon = True task.start() managing = True if self._nsargs.remove: dependency = self._nsargs.remove task = ParallelTask( self._avc.remove, dependency) task.name = "AppRemove-%s" % (dependency,) task.daemon = True task.start() managing = True if self._nsargs.package: path = self._nsargs.package.name self._nsargs.package.close() # no need, unfortunately task = ParallelTask( self._avc.install_package, path) task.name = "AppInstallPackage-%s" % (path,) task.daemon = True task.start() managing = True return managing
def _diff_handler(path): try: if path == "": sem_data['res'] = False return if not const_file_writable(path): sem_data['res'] = False return proc = open_editor(path) if proc is None: self._ignore_remove(path) sem_data['res'] = False return task = ParallelTask(proc.wait) task.name = "Diff-%s" % (self, ) task.daemon = True task.start() sem_data['res'] = True except Exception as exc: sem_data['exc'] = exc finally: sem_data['sem'].release()
def _diff_handler(path): try: if path == "": sem_data['res'] = False return if not const_file_writable(path): sem_data['res'] = False return proc = open_editor(path) if proc is None: self._ignore_remove(path) sem_data['res'] = False return task = ParallelTask(proc.wait) task.name = "Diff-%s" % (self,) task.daemon = True task.start() sem_data['res'] = True except Exception as exc: sem_data['exc'] = exc finally: sem_data['sem'].release()
def launch_package_manager(self, *data, **kwargs): args = ["/usr/bin/rigo"] if kwargs.get("other_args"): args += kwargs["other_args"] task = ParallelTask(subprocess.call, args) task.daemon = True task.start()
def setup(self): """ Reimplemented from NotificationViewController. """ th = ParallelTask(self.__check_connectivity) th.daemon = True th.name = "CheckConnectivity" th.start()
def _vote_submit(self, app, username, vote): """ Do the actual vote submit. """ task = ParallelTask(self._vote_submit_thread_body, app, username, vote) task.name = "VoteSubmitThreadBody" task.daemon = True task.start()
def _comment_submit(self, app, username, text): """ Actual Comment submit to Web Service. Here we arrive from the MainThread. """ task = ParallelTask(self._comment_submit_thread_body, app, username, text) task.name = "CommentSubmitThreadBody" task.daemon = True task.start()
def _on_remove_comment(self, widget): """ We are requested to remove this comment, spawn the request. """ self.hide() task = ParallelTask(self._remove_comment) task.name = "RemoveComment{%s}" % (self._comment,) task.daemon = True task.start() return True
def open_url(url): """ Open the given URL using xdg-open """ def _open_url(url): subprocess.call(["xdg-open", url]) task = ParallelTask(_open_url, url) task.name = "UrlOpen" task.daemon = True task.start()
def _on_remove_comment(self, widget): """ We are requested to remove this comment, spawn the request. """ self.hide() task = ParallelTask(self._remove_comment) task.name = "RemoveComment{%s}" % (self._comment, ) task.daemon = True task.start() return True
def _on_license_activate(self, widget, uri): """ License link clicked. """ license_apps = self._licenses.get(uri) if not license_apps: return True task = ParallelTask(self._show_license, uri, license_apps) task.name = "ShowLicense" task.daemon = True task.start() return True
def _on_application_activated(self, avc, app): """ Event received from Gtk widgets requesting us to load package information. Once we're done loading the shit, we just emit 'application-show' and let others do the UI switch. """ self._visible = True self._last_app = app task = ParallelTask(self.__application_activate, app) task.name = "ApplicationActivate" task.daemon = True task.start()
def _on_reload_state(self, srv, app, daemon_action, app_outcome=None): """ Reload Application state due to a transaction event. """ if not self._visible: return last_app = self._last_app if last_app is not None: app = last_app task = ParallelTask(self._reload_application_state, app) task.daemon = True task.name = "OnReloadAppState" task.start()
def _start_managing(self): """ Start managing applications passed via argv. """ managing = False if self._nsargs.install: dependency = self._nsargs.install task = ParallelTask(self._avc.install, dependency) task.name = "AppInstall-%s" % (dependency, ) task.daemon = True task.start() managing = True if self._nsargs.remove: dependency = self._nsargs.remove task = ParallelTask(self._avc.remove, dependency) task.name = "AppRemove-%s" % (dependency, ) task.daemon = True task.start() managing = True if self._nsargs.package: path = self._nsargs.package.name self._nsargs.package.close() # no need, unfortunately task = ParallelTask(self._avc.install_package, path) task.name = "AppInstallPackage-%s" % (path, ) task.daemon = True task.start() managing = True if self._nsargs.upgrade: task = ParallelTask(self._avc.upgrade) task.name = "SystemUpgrade" task.daemon = True task.start() managing = True return managing
def add_downloads(self): """ Add downloads stats for package. """ try: repository_id = self._get_repository_id() self._validate_repository_id(repository_id) except AttributeError: return self._generic_invalid_request() try: package_names = self._get_package_names() except AttributeError: return self._generic_invalid_request() # validate branch branch = (request.params.get('branch') or "").strip() if not branch: return self._generic_invalid_request() if not entropy.tools.validate_branch_name(branch): return self._generic_invalid_request() # validate release_string release_string = (request.params.get('release_string') or "").strip() if not release_string: return self._generic_invalid_request() if not entropy.tools.is_valid_string(release_string): return self._generic_invalid_request() # validate hw_hash hw_hash = (request.params.get('hw_hash') or "").strip() if not hw_hash: return self._generic_invalid_request() if not entropy.tools.is_valid_string(hw_hash): return self._generic_invalid_request() ip_addr = self._get_ip_address(request) task = ParallelTask( self._add_downloads, package_names, branch, release_string, hw_hash, ip_addr) task.name = "AddDownloadsThread" task.daemon = True task.start() response = self._api_base_response( WebService.WEB_SERVICE_RESPONSE_CODE_OK) response['r'] = True return self._service_render(response)
def add_downloads(self): """ Add downloads stats for package. """ try: repository_id = self._get_repository_id() self._validate_repository_id(repository_id) except AttributeError: return self._generic_invalid_request() try: package_names = self._get_package_names() except AttributeError: return self._generic_invalid_request() # validate branch branch = (request.params.get('branch') or "").strip() if not branch: return self._generic_invalid_request() if not entropy.tools.validate_branch_name(branch): return self._generic_invalid_request() # validate release_string release_string = (request.params.get('release_string') or "").strip() if not release_string: return self._generic_invalid_request() if not entropy.tools.is_valid_string(release_string): return self._generic_invalid_request() # validate hw_hash hw_hash = (request.params.get('hw_hash') or "").strip() if not hw_hash: return self._generic_invalid_request() if not entropy.tools.is_valid_string(hw_hash): return self._generic_invalid_request() ip_addr = self._get_ip_address(request) task = ParallelTask(self._add_downloads, package_names, branch, release_string, hw_hash, ip_addr) task.name = "AddDownloadsThread" task.daemon = True task.start() response = self._api_base_response( WebService.WEB_SERVICE_RESPONSE_CODE_OK) response['r'] = True return self._service_render(response)
def _on_source_edit(self, widget, path, cu): """ Source File Edit request. """ const_debug_write(__name__, "Confc: _on_source_edit: %s" % (cu,)) def _edit(): if not cu.edit(): def _notify(): msg = "%s: <i>%s</i>" % ( _("Cannot <b>edit</b> configuration file"), cu.source(),) self._notify_error(msg) GLib.idle_add(_notify) task = ParallelTask(_edit) task.name = "OnSourceEdit" task.daemon = True task.start()
def load(self): """ Request a content (re)load. """ const_debug_write(__name__, "GroupVC: load() called") def _load(): groups = self._service.groups() objs = [] for identifier, data in groups.items(): group = Group(self._avc, identifier, data["name"], data["description"], data["categories"]) objs.append(group) self.set_many_safe(objs) task = ParallelTask(_load) task.name = "OnGroupLoadRequest" task.daemon = True task.start()
def load(self): """ Request a content (re)load. """ const_debug_write(__name__, "RepoVC: load() called") def _load(): repositories = self._service.list_repositories() objs = [] for repository_id, description, enabled in repositories: obj = Repository(repository_id, description, enabled) objs.append(obj) self.set_many_safe(objs) task = ParallelTask(_load) task.name = "OnRepositoryLoadRequest" task.daemon = True task.start()
def _on_show_diff(self, widget, path, cu): """ Diff request. """ const_debug_write(__name__, "Confc: _on_show_diff: %s" % (cu,)) def _diff(): if not cu.diff(): def _notify(): msg = "%s: <i>%s</i>" % (_("Cannot <b>show</b> configuration " "files difference"), cu.source()) self._notify_error(msg) GLib.idle_add(_notify) task = ParallelTask(_diff) task.name = "OnShowDiff" task.daemon = True task.start()
def load(self): """ Request a content (re)load. """ const_debug_write(__name__, "GroupVC: load() called") def _load(): groups = self._service.groups() objs = [] for identifier, data in groups.items(): group = Group(self._avc, identifier, data['name'], data['description'], data['categories']) objs.append(group) self.set_many_safe(objs) task = ParallelTask(_load) task.name = "OnGroupLoadRequest" task.daemon = True task.start()
def _on_show_diff(self, widget, path, cu): """ Diff request. """ const_debug_write(__name__, "Confc: _on_show_diff: %s" % (cu,)) def _diff(): if not cu.diff(): def _notify(): msg = "%s: <i>%s</i>" % ( _("Cannot <b>show</b> configuration " "files difference"), cu.source(),) self._notify_error(msg) GLib.idle_add(_notify) task = ParallelTask(_diff) task.name = "OnShowDiff" task.daemon = True task.start()
def _on_source_merge(self, widget, path, cu): """ Source file merge request. """ const_debug_write(__name__, "Confc: _on_source_merge: %s" % (cu,)) def _merge(): if not cu.merge(): def _notify(): msg = "%s: <i>%s</i>" % ( _("Cannot <b>merge</b> configuration file"), cu.source(),) self._notify_error(msg) GLib.idle_add(_notify) else: GLib.idle_add(self._remove_path, path) task = ParallelTask(_merge) task.name = "OnSourceMerge" task.daemon = True task.start()
def _login(self, button): """ Try to login to Entropy Web Services. """ username = self._username_entry.get_text() password = self._password_entry.get_text() try: username = const_convert_to_unicode( username, enctype=etpConst['conf_encoding']) password = const_convert_to_unicode( password, enctype=etpConst['conf_encoding']) except UnicodeDecodeError as err: const_debug_write( __name__, "LoginNotificationBox._login: %s" % (repr(err), )) return task = ParallelTask(self._login_thread_body, username, password) task.name = "LoginNotificationThreadBody" task.daemon = True task.start()
def _login(self, button): """ Try to login to Entropy Web Services. """ username = self._username_entry.get_text() password = self._password_entry.get_text() try: username = const_convert_to_unicode( username, enctype=etpConst['conf_encoding']) password = const_convert_to_unicode( password, enctype=etpConst['conf_encoding']) except UnicodeDecodeError as err: const_debug_write( __name__, "LoginNotificationBox._login: %s" % (repr(err),)) return task = ParallelTask( self._login_thread_body, username, password) task.name = "LoginNotificationThreadBody" task.daemon = True task.start()
def load_url(self, url): task = ParallelTask( subprocess.call, ['xdg-open', url]) task.daemon = True task.start()
def _permissions_setup(self): """ Check execution privileges and spawn the Rigo UI. """ if not entropy.tools.is_user_in_entropy_group(): # otherwise the lock handling would potentially # fail. self._show_ok_dialog( None, escape_markup(_("Not authorized")), escape_markup(_("You are not authorized to run Rigo"))) entropy.tools.kill_threads() Gtk.main_quit() return if not self._service.service_available(): self._show_ok_dialog( None, escape_markup(_("Rigo")), escape_markup(_("RigoDaemon service is not available"))) entropy.tools.kill_threads() Gtk.main_quit() return supported_apis = self._service.supported_apis() daemon_api = self._service.api() if daemon_api not in supported_apis: self._show_ok_dialog( None, escape_markup(_("Rigo")), escape_markup( _("API mismatch, please update Rigo and RigoDaemon"))) entropy.tools.kill_threads() Gtk.main_quit() return lock = EntropyResourcesLock(output=self._entropy) # always execute this from the MainThread, since the lock uses TLS acquired = lock.try_acquire_shared() is_exclusive = False if not acquired: # check whether RigoDaemon is running in excluive mode # and ignore non-atomicity here (failing with error # is acceptable) if not self._service.exclusive(): self._show_ok_dialog( None, escape_markup(_("Rigo")), escape_markup(_("Another Application Manager is active"))) entropy.tools.kill_threads() Gtk.main_quit() return is_exclusive = True # otherwise we can go ahead and handle our state later # check RigoDaemon, don't worry about races between Rigo Clients # it is fine to have multiple Rigo Clients connected. Mutual # exclusion is handled via Entropy Resources Lock (which is a file # based rwsem). activity = self._service.activity() if activity != DaemonActivityStates.AVAILABLE: msg = "" show_dialog = True if activity == DaemonActivityStates.NOT_AVAILABLE: msg = _("Background Service is currently not available") elif activity == DaemonActivityStates.UPDATING_REPOSITORIES: show_dialog = False task = ParallelTask(self._service._update_repositories, [], False, master=False) task.daemon = True task.name = "UpdateRepositoriesUnlocked" task.start() elif activity == DaemonActivityStates.MANAGING_APPLICATIONS: show_dialog = False task = ParallelTask(self._service._application_request, None, None, master=False) task.daemon = True task.name = "ApplicationRequestUnlocked" task.start() elif activity == DaemonActivityStates.UPGRADING_SYSTEM: show_dialog = False task = ParallelTask(self._service._upgrade_system, False, master=False) task.daemon = True task.name = "UpgradeSystemUnlocked" task.start() elif activity == DaemonActivityStates.INTERNAL_ROUTINES: msg = _("Background Service is currently busy") else: msg = _("Background Service is incompatible with Rigo") if show_dialog: self._show_ok_dialog(None, escape_markup(_("Rigo")), escape_markup(msg)) entropy.tools.kill_threads() Gtk.main_quit() return elif is_exclusive: msg = _("Background Service is currently unavailable") # no lock acquired, cannot continue the initialization self._show_ok_dialog(None, escape_markup(_("Rigo")), escape_markup(msg)) entropy.tools.kill_threads() Gtk.main_quit() return parser = argparse.ArgumentParser( description=_("Rigo Application Browser")) parser.add_argument("package", nargs='?', type=file, metavar="<path>", help="package path") parser.add_argument("--install", metavar="<dep string>", help="install given dependency") parser.add_argument("--remove", metavar="<dep string>", help="remove given dependency") parser.add_argument("--upgrade", help="upgrade the system", action="store_true", default=False) parser.add_argument("--dumper", help="enable the main thread dumper (debug)", action="store_true", default=False) parser.add_argument("--debug", help="enable Entropy Library debug mode", action="store_true", default=False) try: self._nsargs = parser.parse_args(sys.argv[1:]) except IOError as err: self._show_ok_dialog(None, escape_markup(_("Rigo")), escape_markup("%s" % (err, ))) entropy.tools.kill_threads() Gtk.main_quit() return self._thread_dumper() self._pref_view_c.setup() self._group_view_c.setup() self._config_view_c.setup() self._repo_view_c.setup() self._notice_view_c.setup() self._app_view_c.setup() self._avc.setup() self._nc.setup() self._work_view_c.setup() self._service.setup(acquired) self._easter_eggs() self._window.show() managing = self._start_managing() if not managing: self._change_view_state(RigoViewStates.GROUPS_VIEW_STATE) self._service.hello()
def download(self): """ Start downloading URL given at construction time. @return: dict containing UrlFetcher.get_id() as key and download status as value, which can be either one of: UrlFetcher.GENERIC_FETCH_ERROR means error. UrlFetcher.TIMEOUT_FETCH_ERROR means timeout error. UrlFetcher.GENERIC_FETCH_WARN means warning, downloaded fine but unable to calculate the md5 hash. @rtype: dict """ self._init_vars() speed_limit = 0 dsl = self.__system_settings['repositories']['transfer_limit'] if isinstance(dsl, int) and self._url_path_list: speed_limit = dsl/len(self._url_path_list) class MyFetcher(self.__url_fetcher): def __init__(self, klass, multiple, *args, **kwargs): klass.__init__(self, *args, **kwargs) self.__multiple_fetcher = multiple def update(self): return self.__multiple_fetcher.update() def _push_progress_to_output(self, *args): return def handle_statistics(self, *args, **kwargs): return self.__multiple_fetcher.handle_statistics(*args, **kwargs) th_id = 0 for url, path_to_save in self._url_path_list: th_id += 1 downloader = MyFetcher( self.__url_fetcher, self, url, path_to_save, checksum = self.__checksum, show_speed = self.__show_speed, resume = self.__resume, abort_check_func = self.__abort_check_func, disallow_redirect = self.__disallow_redirect, thread_stop_func = self.__handle_threads_stop, speed_limit = speed_limit, timeout = self.__timeout, download_context_func = self.__download_context_func, pre_download_hook = self.__pre_download_hook, post_download_hook = self.__post_download_hook ) downloader.set_id(th_id) def do_download(ds, dth_id, downloader): ds[dth_id] = downloader.download() t = ParallelTask(do_download, self.__download_statuses, th_id, downloader) t.name = "UrlFetcher{%s}" % (url,) t.daemon = True self.__thread_pool[th_id] = t t.start() self._push_progress_to_output(force = True) self.__show_download_files_info() self.__show_progress = True # wait until all the threads are done # do not block the main thread # but rather use timeout and check try: while True: _all_joined = True for th_id, th in self.__thread_pool.items(): th.join(0.3) if th.is_alive(): # timeout then _all_joined = False if _all_joined: break except (SystemExit, KeyboardInterrupt): self.__stop_threads = True raise if len(self._url_path_list) != len(self.__download_statuses): # there has been an error (exception) # complete download_statuses with error info for th_id, th in self.__thread_pool.items(): if th_id not in self.__download_statuses: self.__download_statuses[th_id] = \ UrlFetcher.GENERIC_FETCH_ERROR return self.__download_statuses
def _permissions_setup(self): """ Check execution privileges and spawn the Rigo UI. """ if not entropy.tools.is_user_in_entropy_group(): # otherwise the lock handling would potentially # fail. self._show_ok_dialog( None, escape_markup(_("Not authorized")), escape_markup(_("You are not authorized to run Rigo"))) entropy.tools.kill_threads() Gtk.main_quit() return if not self._service.service_available(): self._show_ok_dialog( None, escape_markup(_("Rigo")), escape_markup(_("RigoDaemon service is not available"))) entropy.tools.kill_threads() Gtk.main_quit() return supported_apis = self._service.supported_apis() daemon_api = self._service.api() if daemon_api not in supported_apis: self._show_ok_dialog( None, escape_markup(_("Rigo")), escape_markup( _("API mismatch, please update Rigo and RigoDaemon"))) entropy.tools.kill_threads() Gtk.main_quit() return lock = EntropyResourcesLock(output=self._entropy) # always execute this from the MainThread, since the lock uses TLS acquired = lock.try_acquire_shared() is_exclusive = False if not acquired: # check whether RigoDaemon is running in excluive mode # and ignore non-atomicity here (failing with error # is acceptable) if not self._service.exclusive(): self._show_ok_dialog( None, escape_markup(_("Rigo")), escape_markup(_("Another Application Manager is active"))) entropy.tools.kill_threads() Gtk.main_quit() return is_exclusive = True # otherwise we can go ahead and handle our state later # check RigoDaemon, don't worry about races between Rigo Clients # it is fine to have multiple Rigo Clients connected. Mutual # exclusion is handled via Entropy Resources Lock (which is a file # based rwsem). activity = self._service.activity() if activity != DaemonActivityStates.AVAILABLE: msg = "" show_dialog = True if activity == DaemonActivityStates.NOT_AVAILABLE: msg = _("Background Service is currently not available") elif activity == DaemonActivityStates.UPDATING_REPOSITORIES: show_dialog = False task = ParallelTask( self._service._update_repositories, [], False, master=False) task.daemon = True task.name = "UpdateRepositoriesUnlocked" task.start() elif activity == DaemonActivityStates.MANAGING_APPLICATIONS: show_dialog = False task = ParallelTask( self._service._application_request, None, None, master=False) task.daemon = True task.name = "ApplicationRequestUnlocked" task.start() elif activity == DaemonActivityStates.UPGRADING_SYSTEM: show_dialog = False task = ParallelTask( self._service._upgrade_system, False, master=False) task.daemon = True task.name = "UpgradeSystemUnlocked" task.start() elif activity == DaemonActivityStates.INTERNAL_ROUTINES: msg = _("Background Service is currently busy") else: msg = _("Background Service is incompatible with Rigo") if show_dialog: self._show_ok_dialog( None, escape_markup(_("Rigo")), escape_markup(msg)) entropy.tools.kill_threads() Gtk.main_quit() return elif is_exclusive: msg = _("Background Service is currently unavailable") # no lock acquired, cannot continue the initialization self._show_ok_dialog( None, escape_markup(_("Rigo")), escape_markup(msg)) entropy.tools.kill_threads() Gtk.main_quit() return parser = argparse.ArgumentParser( description=_("Rigo Application Browser")) parser.add_argument( "package", nargs='?', type=file, metavar="<path>", help="package path") parser.add_argument( "--install", metavar="<dep string>", help="install given dependency") parser.add_argument( "--remove", metavar="<dep string>", help="remove given dependency") parser.add_argument( "--upgrade", help="upgrade the system", action="store_true", default=False) parser.add_argument( "--dumper", help="enable the main thread dumper (debug)", action="store_true", default=False) parser.add_argument( "--debug", help="enable Entropy Library debug mode", action="store_true", default=False) try: self._nsargs = parser.parse_args(sys.argv[1:]) except IOError as err: self._show_ok_dialog( None, escape_markup(_("Rigo")), escape_markup("%s" % (err,))) entropy.tools.kill_threads() Gtk.main_quit() return self._thread_dumper() self._pref_view_c.setup() self._group_view_c.setup() self._config_view_c.setup() self._repo_view_c.setup() self._notice_view_c.setup() self._app_view_c.setup() self._avc.setup() self._nc.setup() self._work_view_c.setup() self._service.setup(acquired) self._easter_eggs() self._window.show() managing = self._start_managing() if not managing: self._change_view_state(RigoViewStates.GROUPS_VIEW_STATE) self._service.hello()
def __cacher(self, run_until_empty=False, sync=False, _loop=False): """ This is where the actual asynchronous copy takes place. __cacher runs on a different threads and all the operations done by this are atomic and thread-safe. It just loops over and over until __alive becomes False. """ try: if self.__inside_with_stmt != 0: return except AttributeError: # interpreter shutdown pass # make sure our set delay is respected try: self.__cache_writer.set_delay(EntropyCacher.WRITEBACK_TIMEOUT) except AttributeError: # can be None pass # sleep if there's nothing to do if _loop: try: # CANBLOCK self.__worker_sem.acquire() # we just consumed one acquire() # that was dedicated to actual data, # put it back self.__worker_sem.release() except AttributeError: pass def _commit_data(_massive_data): for (key, cache_dir), data in _massive_data: d_o = entropy.dump.dumpobj if d_o is not None: d_o(key, data, dump_dir=cache_dir) while self.__alive or run_until_empty: if const_debug_enabled(): const_debug_write( __name__, "EntropyCacher.__cacher: loop: %s, alive: %s, empty: %s" % (_loop, self.__alive, run_until_empty), ) with self.__enter_context_lock: massive_data = [] try: massive_data_count = EntropyCacher._OBJS_WRITTEN_AT_ONCE except AttributeError: # interpreter shutdown break while massive_data_count > 0: if _loop: # extracted an item from worker_sem # call down() on the semaphore without caring # can't sleep here because we're in a critical region # holding __enter_context_lock self.__worker_sem.acquire(False) massive_data_count -= 1 try: data = self.__cache_buffer.pop() except (ValueError, TypeError): # TypeError is when objects are being destroyed break # stack empty massive_data.append(data) if not massive_data: break task = ParallelTask(_commit_data, massive_data) task.name = "EntropyCacherCommitter" task.daemon = not sync task.start() if sync: task.join() if const_debug_enabled(): const_debug_write( __name__, "EntropyCacher.__cacher [%s], writing %s objs" % (task, len(massive_data)) ) if EntropyCacher.STASHING_CACHE: for (key, cache_dir), data in massive_data: try: del self.__stashing_cache[(key, cache_dir)] except (AttributeError, KeyError): continue del massive_data[:] del massive_data
def __init__(self): self._current_state_lock = False self._current_state = RigoViewStates.STATIC_VIEW_STATE self._state_transitions = { RigoViewStates.BROWSER_VIEW_STATE: (self._enter_browser_state, self._exit_browser_state), RigoViewStates.STATIC_VIEW_STATE: (self._enter_static_state, self._exit_static_state), RigoViewStates.APPLICATION_VIEW_STATE: (self._enter_application_state, self._exit_application_state), RigoViewStates.WORK_VIEW_STATE: (self._enter_work_state, self._exit_work_state), RigoViewStates.CONFUPDATES_VIEW_STATE: (self._enter_confupdates_state, self._exit_confupdates_state), RigoViewStates.NOTICEBOARD_VIEW_STATE: (self._enter_noticeboard_state, self._exit_noticeboard_state), RigoViewStates.PREFERENCES_VIEW_STATE: (self._enter_preferences_state, self._exit_preferences_state), RigoViewStates.REPOSITORY_VIEW_STATE: (self._enter_repository_state, self._exit_repository_state), RigoViewStates.GROUPS_VIEW_STATE: (self._enter_groups_state, self._exit_groups_state) } self._state_metadata = { RigoViewStates.BROWSER_VIEW_STATE: { "title": _("Search"), }, RigoViewStates.STATIC_VIEW_STATE: { "title": _("Rigo Application Browser"), }, RigoViewStates.APPLICATION_VIEW_STATE: { "title": _("Application"), }, RigoViewStates.WORK_VIEW_STATE: { "title": _("Working Hard"), }, RigoViewStates.CONFUPDATES_VIEW_STATE: { "title": _("Wake Up"), }, RigoViewStates.NOTICEBOARD_VIEW_STATE: { "title": _("Important Stuff"), }, RigoViewStates.PREFERENCES_VIEW_STATE: { "title": _("Breaking Stuff"), }, RigoViewStates.REPOSITORY_VIEW_STATE: { "title": _("Repository Stuff"), }, RigoViewStates.GROUPS_VIEW_STATE: { "title": _("Application Groups"), }, } self._state_mutex = Lock() icons = get_sc_icon_theme(DATA_DIR) self._activity_rwsem = ReadersWritersSemaphore() # This relies on the fact that the installed packages repository # is lazily loaded (thus, schema update code is). self._entropy = Client() self._entropy_ws = EntropyWebService(self._entropy) preload_task = ParallelTask(self._entropy_ws.preload) preload_task.name = "PreloadEntropyWebService" preload_task.daemon = True preload_task.start() self._service = RigoServiceController(self, self._activity_rwsem, self._entropy, self._entropy_ws) app_handler = Rigo.RigoHandler(self, self._service) self._builder = Gtk.Builder() self._builder.add_from_file(os.path.join(DATA_DIR, "ui/gtk3/rigo.ui")) self._builder.connect_signals(app_handler) self._window = self._builder.get_object("rigoWindow") self._window.set_name("rigo-view") self._apps_view = self._builder.get_object("appsViewVbox") self._scrolled_view = self._builder.get_object( "appsViewScrolledWindow") self._app_view = self._builder.get_object("appViewScrollWin") self._app_view.set_name("rigo-view") self._app_view_port = self._builder.get_object("appViewVport") self._app_view_port.set_name("rigo-view") self._not_found_box = self._builder.get_object("appsViewNotFoundVbox") self._config_scrolled_view = self._builder.get_object( "configViewScrolledWindow") self._config_view = self._builder.get_object("configViewVbox") self._config_view.set_name("rigo-view") self._repo_scrolled_view = self._builder.get_object( "repoViewScrolledWindow") self._repo_view = self._builder.get_object("repoViewVbox") self._repo_view.set_name("rigo-view") self._notice_scrolled_view = self._builder.get_object( "noticeViewScrolledWindow") self._notice_view = self._builder.get_object("noticeViewVbox") self._notice_view.set_name("rigo-view") self._pref_scrolled_view = self._builder.get_object( "preferencesViewScrolledWindow") self._pref_view = self._builder.get_object("preferencesViewVbox") self._pref_view.set_name("rigo-view") self._group_scrolled_view = self._builder.get_object( "groupViewScrolledWindow") self._group_view = self._builder.get_object("groupViewVbox") self._group_view.set_name("rigo-view") self._search_entry = self._builder.get_object("searchEntry") self._search_entry_completion = self._builder.get_object( "searchEntryCompletion") self._search_entry_store = self._builder.get_object("searchEntryStore") self._static_view = self._builder.get_object("staticViewVbox") self._notification = self._builder.get_object("notificationBox") self._bottom_notification = \ self._builder.get_object("bottomNotificationBox") self._work_view = self._builder.get_object("workViewVbox") self._work_view.set_name("rigo-view") self._pref_button = self._builder.get_object("prefButton") def _pref_button_activate(widget): self._change_view_state(RigoViewStates.PREFERENCES_VIEW_STATE) self._pref_button.connect("clicked", _pref_button_activate) # Preferences model, view and controller self._pref_store = PreferencesListStore() self._view_pref = PreferencesTreeView(icons, PreferencesListStore.ICON_SIZE) self._pref_scrolled_view.add(self._view_pref) def _pref_queue_draw(*args): self._view_pref.queue_draw() self._pref_store.connect("redraw-request", _pref_queue_draw) self._pref_view_c = PreferenceViewController(self._pref_store, self._view_pref) self._app_view_c = ApplicationViewController(self._entropy, self._entropy_ws, self._pref_view_c, self._service, self._builder) self._view = AppTreeView(self._entropy, self._service, self._app_view_c, icons, True, AppListStore.ICON_SIZE, store=None) self._scrolled_view.add(self._view) self._view.set_scrolled_view(self._scrolled_view) self._app_store = AppListStore(self._entropy, self._entropy_ws, self._service, self._view, icons) def _queue_draw(*args): self._view.queue_draw() self._app_store.connect("redraw-request", _queue_draw) self._app_view_c.set_store(self._app_store) self._app_view_c.connect("application-show", self._on_application_show) # Configuration file updates model, view and controller self._config_store = ConfigUpdatesListStore() self._view_config = ConfigUpdatesTreeView( icons, ConfigUpdatesListStore.ICON_SIZE) self._config_scrolled_view.add(self._view_config) def _config_queue_draw(*args): self._view_config.queue_draw() self._config_store.connect("redraw-request", _config_queue_draw) self._config_view_c = ConfigUpdatesViewController( self._entropy, self._config_store, self._view_config) self._config_view_c.connect("view-cleared", self._on_view_cleared) self._service.set_configuration_controller(self._config_view_c) # Repository model, view and controller self._repo_store = RepositoryListStore() self._view_repo = RepositoryTreeView(icons, RepositoryListStore.ICON_SIZE) self._repo_scrolled_view.add(self._view_repo) def _repo_queue_draw(*args): self._view_repo.queue_draw() self._repo_store.connect("redraw-request", _repo_queue_draw) self._repo_view_c = RepositoryViewController(self._pref_view_c, self._service, self._repo_store, self._view_repo) # NoticeBoard model, view and controller self._notice_store = NoticeBoardListStore() self._view_notice = NoticeBoardTreeView(icons, NoticeBoardListStore.ICON_SIZE) self._notice_scrolled_view.add(self._view_notice) def _notice_queue_draw(*args): self._view_notice.queue_draw() self._notice_store.connect("redraw-request", _notice_queue_draw) self._notice_view_c = NoticeBoardViewController( self._notice_store, self._view_notice) self._service.set_noticeboard_controller(self._notice_view_c) # Group model, view and controller self._group_store = GroupListStore() self._view_group = GroupTreeView(icons, GroupListStore.ICON_SIZE) self._group_scrolled_view.add(self._view_group) def _group_queue_draw(*args): self._view_group.queue_draw() self._group_store.connect("redraw-request", _group_queue_draw) self._group_view_c = GroupViewController(self._service, self._group_store, self._view_group, self._pref_view_c) self._welcome_box = WelcomeBox() settings = Gtk.Settings.get_default() settings.set_property("gtk-error-bell", False) # wire up the css provider to reconfigure on theme-changes self._window.connect("style-updated", self._on_style_updated, init_sc_css_provider, settings, Gdk.Screen.get_default(), DATA_DIR) # Force the initialization of the css provider asap. # This fixes a glitch with GTK 3.10 init_sc_css_provider(self._window, settings, Gdk.Screen.get_default(), DATA_DIR) self._nc = UpperNotificationViewController(self._entropy, self._entropy_ws, self._notification) # Bottom NotificationBox controller. # Bottom notifications are only used for # providing Activity control to User during # the Activity itself. self._bottom_nc = BottomNotificationViewController( self._window, self._bottom_notification, self._pref_button) self._avc = ApplicationsViewController( self._activity_rwsem, self._entropy, self._entropy_ws, self._nc, self._bottom_nc, self._service, self._pref_view_c, icons, self._not_found_box, self._search_entry, self._search_entry_completion, self._search_entry_store, self._app_store, self._view) self._avc.connect("view-cleared", self._on_view_cleared) self._avc.connect("view-filled", self._on_view_filled) self._avc.connect("view-want-change", self._on_view_change) self._service.set_bottom_notification_controller(self._bottom_nc) self._app_view_c.set_notification_controller(self._nc) self._app_view_c.set_applications_controller(self._avc) self._config_view_c.set_notification_controller(self._nc) self._config_view_c.set_applications_controller(self._avc) self._repo_view_c.set_notification_controller(self._nc) self._repo_view_c.set_applications_controller(self._avc) self._notice_view_c.set_notification_controller(self._nc) self._notice_view_c.set_applications_controller(self._avc) self._group_view_c.set_applications_controller(self._avc) self._service.set_applications_controller(self._avc) self._service.set_application_controller(self._app_view_c) self._service.set_notification_controller(self._nc) self._service.connect("start-working", self._on_start_working) self._service.connect("repositories-updated", self._on_repo_updated) self._service.connect("applications-managed", self._on_applications_managed) self._work_view_c = WorkViewController(icons, self._service, self._work_view) self._service.set_work_controller(self._work_view_c) self._bottom_nc.connect("show-work-view", self._on_show_work_view) self._bottom_nc.connect("work-interrupt", self._on_work_interrupt)
def __cacher(self, run_until_empty=False, sync=False, _loop=False): """ This is where the actual asynchronous copy takes place. __cacher runs on a different threads and all the operations done by this are atomic and thread-safe. It just loops over and over until __alive becomes False. """ try: if self.__inside_with_stmt != 0: return except AttributeError: # interpreter shutdown pass # make sure our set delay is respected try: self.__cache_writer.set_delay(EntropyCacher.WRITEBACK_TIMEOUT) except AttributeError: # can be None pass # sleep if there's nothing to do if _loop: try: # CANBLOCK self.__worker_sem.acquire() # we just consumed one acquire() # that was dedicated to actual data, # put it back self.__worker_sem.release() except AttributeError: pass def _commit_data(_massive_data): for (key, cache_dir), data in _massive_data: d_o = entropy.dump.dumpobj if d_o is not None: d_o(key, data, dump_dir=cache_dir) while self.__alive or run_until_empty: if const_debug_enabled(): const_debug_write( __name__, "EntropyCacher.__cacher: loop: %s, alive: %s, empty: %s" % ( _loop, self.__alive, run_until_empty, )) with self.__enter_context_lock: massive_data = [] try: massive_data_count = EntropyCacher._OBJS_WRITTEN_AT_ONCE except AttributeError: # interpreter shutdown break while massive_data_count > 0: if _loop: # extracted an item from worker_sem # call down() on the semaphore without caring # can't sleep here because we're in a critical region # holding __enter_context_lock self.__worker_sem.acquire(False) massive_data_count -= 1 try: data = self.__cache_buffer.pop() except ( ValueError, TypeError, ): # TypeError is when objects are being destroyed break # stack empty massive_data.append(data) if not massive_data: break task = ParallelTask(_commit_data, massive_data) task.name = "EntropyCacherCommitter" task.daemon = not sync task.start() if sync: task.join() if const_debug_enabled(): const_debug_write( __name__, "EntropyCacher.__cacher [%s], writing %s objs" % ( task, len(massive_data), )) if EntropyCacher.STASHING_CACHE: for (key, cache_dir), data in massive_data: try: del self.__stashing_cache[(key, cache_dir)] except ( AttributeError, KeyError, ): continue del massive_data[:] del massive_data
def __init__(self): self._current_state_lock = False self._current_state = RigoViewStates.STATIC_VIEW_STATE self._state_transitions = { RigoViewStates.BROWSER_VIEW_STATE: ( self._enter_browser_state, self._exit_browser_state), RigoViewStates.STATIC_VIEW_STATE: ( self._enter_static_state, self._exit_static_state), RigoViewStates.APPLICATION_VIEW_STATE: ( self._enter_application_state, self._exit_application_state), RigoViewStates.WORK_VIEW_STATE: ( self._enter_work_state, self._exit_work_state), RigoViewStates.CONFUPDATES_VIEW_STATE: ( self._enter_confupdates_state, self._exit_confupdates_state), RigoViewStates.NOTICEBOARD_VIEW_STATE: ( self._enter_noticeboard_state, self._exit_noticeboard_state), RigoViewStates.PREFERENCES_VIEW_STATE: ( self._enter_preferences_state, self._exit_preferences_state), RigoViewStates.REPOSITORY_VIEW_STATE: ( self._enter_repository_state, self._exit_repository_state), RigoViewStates.GROUPS_VIEW_STATE: ( self._enter_groups_state, self._exit_groups_state) } self._state_metadata = { RigoViewStates.BROWSER_VIEW_STATE: { "title": _("Search"), }, RigoViewStates.STATIC_VIEW_STATE: { "title": _("Rigo Application Browser"), }, RigoViewStates.APPLICATION_VIEW_STATE: { "title": _("Application"), }, RigoViewStates.WORK_VIEW_STATE: { "title": _("Working Hard"), }, RigoViewStates.CONFUPDATES_VIEW_STATE: { "title": _("Wake Up"), }, RigoViewStates.NOTICEBOARD_VIEW_STATE: { "title": _("Important Stuff"), }, RigoViewStates.PREFERENCES_VIEW_STATE: { "title": _("Breaking Stuff"), }, RigoViewStates.REPOSITORY_VIEW_STATE: { "title": _("Repository Stuff"), }, RigoViewStates.GROUPS_VIEW_STATE: { "title": _("Application Groups"), }, } self._state_mutex = Lock() icons = get_sc_icon_theme(DATA_DIR) self._activity_rwsem = ReadersWritersSemaphore() # This relies on the fact that the installed packages repository # is lazily loaded (thus, schema update code is). self._entropy = Client() self._entropy_ws = EntropyWebService(self._entropy) preload_task = ParallelTask(self._entropy_ws.preload) preload_task.name = "PreloadEntropyWebService" preload_task.daemon = True preload_task.start() self._service = RigoServiceController( self, self._activity_rwsem, self._entropy, self._entropy_ws) app_handler = Rigo.RigoHandler(self, self._service) self._builder = Gtk.Builder() self._builder.add_from_file(os.path.join(DATA_DIR, "ui/gtk3/rigo.ui")) self._builder.connect_signals(app_handler) self._window = self._builder.get_object("rigoWindow") self._window.set_name("rigo-view") self._apps_view = self._builder.get_object("appsViewVbox") self._scrolled_view = self._builder.get_object("appsViewScrolledWindow") self._app_view = self._builder.get_object("appViewScrollWin") self._app_view.set_name("rigo-view") self._app_view_port = self._builder.get_object("appViewVport") self._app_view_port.set_name("rigo-view") self._not_found_box = self._builder.get_object("appsViewNotFoundVbox") self._config_scrolled_view = self._builder.get_object( "configViewScrolledWindow") self._config_view = self._builder.get_object("configViewVbox") self._config_view.set_name("rigo-view") self._repo_scrolled_view = self._builder.get_object( "repoViewScrolledWindow") self._repo_view = self._builder.get_object("repoViewVbox") self._repo_view.set_name("rigo-view") self._notice_scrolled_view = self._builder.get_object( "noticeViewScrolledWindow") self._notice_view = self._builder.get_object("noticeViewVbox") self._notice_view.set_name("rigo-view") self._pref_scrolled_view = self._builder.get_object( "preferencesViewScrolledWindow") self._pref_view = self._builder.get_object("preferencesViewVbox") self._pref_view.set_name("rigo-view") self._group_scrolled_view = self._builder.get_object( "groupViewScrolledWindow") self._group_view = self._builder.get_object("groupViewVbox") self._group_view.set_name("rigo-view") self._search_entry = self._builder.get_object("searchEntry") self._search_entry_completion = self._builder.get_object( "searchEntryCompletion") self._search_entry_store = self._builder.get_object( "searchEntryStore") self._static_view = self._builder.get_object("staticViewVbox") self._notification = self._builder.get_object("notificationBox") self._bottom_notification = \ self._builder.get_object("bottomNotificationBox") self._work_view = self._builder.get_object("workViewVbox") self._work_view.set_name("rigo-view") self._pref_button = self._builder.get_object( "prefButton") def _pref_button_activate(widget): self._change_view_state( RigoViewStates.PREFERENCES_VIEW_STATE) self._pref_button.connect( "clicked", _pref_button_activate) # Preferences model, view and controller self._pref_store = PreferencesListStore() self._view_pref = PreferencesTreeView( icons, PreferencesListStore.ICON_SIZE) self._pref_scrolled_view.add(self._view_pref) def _pref_queue_draw(*args): self._view_pref.queue_draw() self._pref_store.connect("redraw-request", _pref_queue_draw) self._pref_view_c = PreferenceViewController( self._pref_store, self._view_pref) self._app_view_c = ApplicationViewController( self._entropy, self._entropy_ws, self._pref_view_c, self._service, self._builder) self._view = AppTreeView( self._entropy, self._service, self._app_view_c, icons, True, AppListStore.ICON_SIZE, store=None) self._scrolled_view.add(self._view) self._view.set_scrolled_view(self._scrolled_view) self._app_store = AppListStore( self._entropy, self._entropy_ws, self._service, self._view, icons) def _queue_draw(*args): self._view.queue_draw() self._app_store.connect("redraw-request", _queue_draw) self._app_view_c.set_store(self._app_store) self._app_view_c.connect("application-show", self._on_application_show) # Configuration file updates model, view and controller self._config_store = ConfigUpdatesListStore() self._view_config = ConfigUpdatesTreeView( icons, ConfigUpdatesListStore.ICON_SIZE) self._config_scrolled_view.add(self._view_config) def _config_queue_draw(*args): self._view_config.queue_draw() self._config_store.connect("redraw-request", _config_queue_draw) self._config_view_c = ConfigUpdatesViewController( self._entropy, self._config_store, self._view_config) self._config_view_c.connect( "view-cleared", self._on_view_cleared) self._service.set_configuration_controller(self._config_view_c) # Repository model, view and controller self._repo_store = RepositoryListStore() self._view_repo = RepositoryTreeView( icons, RepositoryListStore.ICON_SIZE) self._repo_scrolled_view.add(self._view_repo) def _repo_queue_draw(*args): self._view_repo.queue_draw() self._repo_store.connect("redraw-request", _repo_queue_draw) self._repo_view_c = RepositoryViewController( self._pref_view_c, self._service, self._repo_store, self._view_repo) # NoticeBoard model, view and controller self._notice_store = NoticeBoardListStore() self._view_notice = NoticeBoardTreeView( icons, NoticeBoardListStore.ICON_SIZE) self._notice_scrolled_view.add(self._view_notice) def _notice_queue_draw(*args): self._view_notice.queue_draw() self._notice_store.connect("redraw-request", _notice_queue_draw) self._notice_view_c = NoticeBoardViewController( self._notice_store, self._view_notice) self._service.set_noticeboard_controller(self._notice_view_c) # Group model, view and controller self._group_store = GroupListStore() self._view_group = GroupTreeView( icons, GroupListStore.ICON_SIZE) self._group_scrolled_view.add(self._view_group) def _group_queue_draw(*args): self._view_group.queue_draw() self._group_store.connect("redraw-request", _group_queue_draw) self._group_view_c = GroupViewController( self._service, self._group_store, self._view_group, self._pref_view_c) self._welcome_box = WelcomeBox() settings = Gtk.Settings.get_default() settings.set_property("gtk-error-bell", False) # wire up the css provider to reconfigure on theme-changes self._window.connect("style-updated", self._on_style_updated, init_sc_css_provider, settings, Gdk.Screen.get_default(), DATA_DIR) # Force the initialization of the css provider asap. # This fixes a glitch with GTK 3.10 init_sc_css_provider( self._window, settings, Gdk.Screen.get_default(), DATA_DIR) self._nc = UpperNotificationViewController( self._entropy, self._entropy_ws, self._notification) # Bottom NotificationBox controller. # Bottom notifications are only used for # providing Activity control to User during # the Activity itself. self._bottom_nc = BottomNotificationViewController( self._window, self._bottom_notification, self._pref_button) self._avc = ApplicationsViewController( self._activity_rwsem, self._entropy, self._entropy_ws, self._nc, self._bottom_nc, self._service, self._pref_view_c, icons, self._not_found_box, self._search_entry, self._search_entry_completion, self._search_entry_store, self._app_store, self._view) self._avc.connect("view-cleared", self._on_view_cleared) self._avc.connect("view-filled", self._on_view_filled) self._avc.connect("view-want-change", self._on_view_change) self._service.set_bottom_notification_controller( self._bottom_nc) self._app_view_c.set_notification_controller(self._nc) self._app_view_c.set_applications_controller(self._avc) self._config_view_c.set_notification_controller(self._nc) self._config_view_c.set_applications_controller(self._avc) self._repo_view_c.set_notification_controller(self._nc) self._repo_view_c.set_applications_controller(self._avc) self._notice_view_c.set_notification_controller(self._nc) self._notice_view_c.set_applications_controller(self._avc) self._group_view_c.set_applications_controller(self._avc) self._service.set_applications_controller(self._avc) self._service.set_application_controller(self._app_view_c) self._service.set_notification_controller(self._nc) self._service.connect("start-working", self._on_start_working) self._service.connect("repositories-updated", self._on_repo_updated) self._service.connect("applications-managed", self._on_applications_managed) self._work_view_c = WorkViewController( icons, self._service, self._work_view) self._service.set_work_controller(self._work_view_c) self._bottom_nc.connect("show-work-view", self._on_show_work_view) self._bottom_nc.connect("work-interrupt", self._on_work_interrupt)