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 _on_stars_clicked(self, widget, app=None): """ Stars clicked, user wants to vote. """ if app is None: app = self._last_app if app is None: # wtf return def _sender(app, vote): if not app.is_webservice_available(): GLib.idle_add(self._notify_webservice_na, app, self.VOTE_NOTIFICATION_CONTEXT_ID) return ws_user = app.get_webservice_username() if ws_user is not None: GLib.idle_add(self._notify_vote_submit, app, ws_user, vote) else: GLib.idle_add(self._notify_login_request, app, vote, self._on_stars_login_success, self._on_stars_login_failed, self.VOTE_NOTIFICATION_CONTEXT_ID) vote = int(self._stars.get_rating()) # is float task = ParallelTask(_sender, app, vote) task.name = "AppViewSendVote" task.start()
def __init__(self, app, avc, callback, app_downloader_method): self._app = app self._avc = avc self._offset = 0 self._callback = callback self._task = ParallelTask(self._download) self._app_downloader = app_downloader_method
def on_dbBackupButton_clicked(self, widget): def _run_backup(callback): cl_repo_name = etpConst.get( 'clientdbid', getattr(InstalledPackagesRepository, "NAME", None)) with self._privileges: with self._async_event_execution_lock: status, err_msg = self._entropy.backup_repository( cl_repo_name, os.path.dirname(etpConst['etpdatabaseclientfilepath'])) gobject.idle_add(callback, status, err_msg) def _backup_complete(status, err_msg): self.ui_lock(False) self.end_working() if not status: okDialog( self.ui.main, "%s: %s" % (_("Error during backup"), err_msg,) ) return okDialog(self.ui.main, "%s" % (_("Backup complete"),)) self.fill_pref_db_backup_page() self.dbBackupView.queue_draw() self.start_working() self.ui_lock(True) with self._privileges: # make sure to commit any transaction before backing-up self._entropy.installed_repository().commit() t = ParallelTask(_run_backup, _backup_complete) t.start()
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 _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 setup(self): """ Reimplemented from NotificationViewController. """ th = ParallelTask(self.__check_connectivity) th.daemon = True th.name = "CheckConnectivity" th.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 _search(self, old_text, _force=False): cur_text = self._search_entry.get_text() if (cur_text == old_text and cur_text) or _force: search_text = copy.copy(old_text) search_text = const_convert_to_unicode( search_text, enctype=etpConst['conf_encoding']) if _force: self._search_entry.set_text(search_text) th = ParallelTask(self.__search_thread, search_text) th.name = "SearchThread" th.start()
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 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 get_ugc_package_vote_delayed(self): if self.pkgset: return -1 atom = self.get_name() if not atom: return None pkg_key = entropy.dep.dep_getkey(atom) try: vote_raw = self._get_vote_cache_2(pkg_key) except KeyError: vote_raw = None # schedule a new task th = ParallelTask(self._get_vote_raw, pkg_key) th.start() return vote_raw
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 _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 on_dbRestoreButton_clicked(self, widget): model, myiter = self.dbBackupView.get_selection().get_selected() if myiter is None: return dbpath = model.get_value(myiter, 0) def _run_restore(callback): cl_repo_name = etpConst.get( 'clientdbid', getattr(InstalledPackagesRepository, "NAME", None)) with self._async_event_execution_lock: with self._privileges: status, err_msg = self._entropy.restore_repository(dbpath, etpConst['etpdatabaseclientfilepath'], cl_repo_name) gobject.idle_add(callback, status, err_msg) def _restore_done(status, err_msg): self.ui_lock(False) self.end_working() self._entropy.reopen_installed_repository() self.reset_cache_status() self.show_packages() if not status: okDialog(self.ui.main, "%s: %s" % (_("Error during restore"), err_msg,)) return self.fill_pref_db_backup_page() self.dbBackupView.queue_draw() okDialog(self.ui.main, "%s" % (_("Restore complete"),)) self.start_working() self.ui_lock(True) with self._privileges: # make sure to commit any transaction before restoring self._entropy.installed_repository().commit() t = ParallelTask(_run_restore, _restore_done) t.start()
def _on_send_comment(self, widget, app=None): """ Send comment to Web Service. """ if app is None: app = self._last_app if app is None: # we're hiding return text = self._app_comment_text_buffer.get_text( self._app_comment_text_buffer.get_start_iter(), self._app_comment_text_buffer.get_end_iter(), False ) if not text.strip(): return # make it utf-8 text = const_convert_to_unicode(text, enctype=etpConst["conf_encoding"]) def _sender(app, text): if not app.is_webservice_available(): GLib.idle_add(self._notify_webservice_na, app, self.COMMENT_NOTIFICATION_CONTEXT_ID) return ws_user = app.get_webservice_username() if ws_user is not None: GLib.idle_add(self._notify_comment_submit, app, ws_user, text) else: GLib.idle_add( self._notify_login_request, app, text, self._on_comment_login_success, self._on_comment_login_failed, self.COMMENT_NOTIFICATION_CONTEXT_ID, ) task = ParallelTask(_sender, app, text) task.name = "AppViewSendComment" 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 _on_send_comment(self, widget, app=None): """ Send comment to Web Service. """ if app is None: app = self._last_app if app is None: # we're hiding return text = self._app_comment_text_buffer.get_text( self._app_comment_text_buffer.get_start_iter(), self._app_comment_text_buffer.get_end_iter(), False) if not text.strip(): return # make it utf-8 text = const_convert_to_unicode(text, enctype=etpConst['conf_encoding']) def _sender(app, text): if not app.is_webservice_available(): GLib.idle_add(self._notify_webservice_na, app, self.COMMENT_NOTIFICATION_CONTEXT_ID) return ws_user = app.get_webservice_username() if ws_user is not None: GLib.idle_add(self._notify_comment_submit, app, ws_user, text) else: GLib.idle_add(self._notify_login_request, app, text, self._on_comment_login_success, self._on_comment_login_failed, self.COMMENT_NOTIFICATION_CONTEXT_ID) task = ParallelTask(_sender, app, text) task.name = "AppViewSendComment" 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 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 _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_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()
class MetadataDownloader(GObject.Object): """ Automated Application comments downloader. """ def __init__(self, app, avc, callback, app_downloader_method): self._app = app self._avc = avc self._offset = 0 self._callback = callback self._task = ParallelTask(self._download) self._app_downloader = app_downloader_method def start(self): """ Start downloading comments and send them to callback. Loop over until we have more of them to download. """ self._offset = 0 self._task.start() def _download_callback(self, document_list): """ Callback called by download_<something>() once data is arrived from web service. document_list can be None! """ has_more = 0 if document_list is not None: has_more = document_list.has_more() # stash more data? if has_more and (document_list is not None): self._offset += len(document_list) # download() will be called externally if const_debug_enabled(): const_debug_write( __name__, "MetadataDownloader._download_callback: %s, more: %s" % (document_list, has_more)) if document_list is not None: const_debug_write( __name__, "MetadataDownloader._download_callback: " "has_more: %s, offset: %s" % (document_list.has_more(), document_list.offset())) self._callback(self, self._app, document_list, has_more) def reset_offset(self): """ Reset Metadata download offset to 0. """ self._offset = 0 def get_offset(self): """ Get current Metadata download offset. """ return self._offset def enqueue_download(self): """ Enqueue a new download, starting from current offset """ self._task = ParallelTask(self._download) self._task.start() def _download(self): """ Thread body of the initial Metadata downloader. """ self._app_downloader(self._download_callback, offset=self._offset)
def load_url(self, url): task = ParallelTask( subprocess.call, ['xdg-open', url]) task.daemon = True 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
class MetadataDownloader(GObject.Object): """ Automated Application comments downloader. """ def __init__(self, app, avc, callback, app_downloader_method): self._app = app self._avc = avc self._offset = 0 self._callback = callback self._task = ParallelTask(self._download) self._app_downloader = app_downloader_method def start(self): """ Start downloading comments and send them to callback. Loop over until we have more of them to download. """ self._offset = 0 self._task.start() def _download_callback(self, document_list): """ Callback called by download_<something>() once data is arrived from web service. document_list can be None! """ has_more = 0 if document_list is not None: has_more = document_list.has_more() # stash more data? if has_more and (document_list is not None): self._offset += len(document_list) # download() will be called externally if const_debug_enabled(): const_debug_write( __name__, "MetadataDownloader._download_callback: %s, more: %s" % ( document_list, has_more)) if document_list is not None: const_debug_write( __name__, "MetadataDownloader._download_callback: " "has_more: %s, offset: %s" % ( document_list.has_more(), document_list.offset())) self._callback(self, self._app, document_list, has_more) def reset_offset(self): """ Reset Metadata download offset to 0. """ self._offset = 0 def get_offset(self): """ Get current Metadata download offset. """ return self._offset def enqueue_download(self): """ Enqueue a new download, starting from current offset """ self._task = ParallelTask(self._download) self._task.start() def _download(self): """ Thread body of the initial Metadata downloader. """ self._app_downloader(self._download_callback, offset=self._offset)
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 enqueue_download(self): """ Enqueue a new download, starting from current offset """ self._task = ParallelTask(self._download) self._task.start()
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 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 _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 __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 _install_action(self, entropy_client, deps, recursive, pretend, ask, verbose, quiet, empty, config_files, deep, fetch, bdeps, onlydeps, relaxed, multifetch, packages, package_matches=None): """ Solo Install action implementation. Packages passed in the packages argument (as opposed to package_matches) will be marked as installed by user. """ inst_repo = entropy_client.installed_repository() action_factory = entropy_client.PackageActionFactory() packages_by_user = set() with inst_repo.shared(): self._advise_repository_update(entropy_client) if self._check_critical_updates: self._advise_packages_update(entropy_client) if package_matches is None: packages = self._match_packages_for_installation( entropy_client, onlydeps, packages) if not packages: return 1, False packages_by_user = set(packages) else: packages = package_matches run_queue, removal_queue = self._generate_install_queue( entropy_client, packages, deps, empty, deep, relaxed, onlydeps, bdeps, recursive) if (run_queue is None) or (removal_queue is None): return 1, False elif not (run_queue or removal_queue): entropy_client.output( "%s." % (blue(_("Nothing to do")),), level="warning", header=darkgreen(" @@ ")) return 0, True self._show_install_queue( entropy_client, inst_repo, run_queue, removal_queue, ask, pretend, quiet, verbose) installed_pkg_sources = self._get_installed_packages_sources( entropy_client, inst_repo, run_queue) if ask: rc = entropy_client.ask_question( " %s" % (_("Would you like to continue ?"),)) if rc == _("No"): return 1, False if pretend: return 0, True # yes, tell user if self._interactive: exit_st = self._accept_license( entropy_client, inst_repo, run_queue) if exit_st != 0: return 1, False ugc_thread = None down_data = {} exit_st = self._download_packages( entropy_client, run_queue, down_data, multifetch) if exit_st == 0: ugc_thread = ParallelTask( self._signal_ugc, entropy_client, down_data) ugc_thread.name = "UgcThread" ugc_thread.start() elif exit_st != 0: return 1, False # is --fetch on? then quit. if fetch: if ugc_thread is not None: ugc_thread.join() entropy_client.output( "%s." % ( blue(_("Download complete")),), header=darkred(" @@ ")) return 0, False notification_lock = UpdatesNotificationResourceLock( output=entropy_client) total = len(run_queue) notif_acquired = False try: # this is a best effort, we will not sleep if the lock # is not acquired because we may get blocked for an eternity # (well, for a very long time) in this scenario: # 1. RigoDaemon is running some action queue # 2. Another thread in RigoDaemon is stuck on the activity # mutex with the notification lock held. # 3. We cannot move on here because of 2. # Nothing bad will happen if we just ignore the acquisition # state. notif_acquired = notification_lock.try_acquire_shared() metaopts = { 'removeconfig': config_files, # This can be used by PackageAction based classes # to know what's the overall package schedule for # both upgrade and install actions. This way, we # can better handle conflicts. 'install_queue' : run_queue, } for count, pkg_match in enumerate(run_queue, 1): source_id = installed_pkg_sources.get(pkg_match, None) if not onlydeps and pkg_match in packages_by_user: metaopts['install_source'] = \ etpConst['install_sources']['user'] elif source_id is not None: # Retain the information. # Install action can upgrade packages, their source # should not be changed to automatic_dependency. metaopts['install_source'] = source_id else: metaopts['install_source'] = \ etpConst['install_sources']['automatic_dependency'] package_id, repository_id = pkg_match atom = entropy_client.open_repository( repository_id).retrieveAtom(package_id) pkg = None try: pkg = action_factory.get( action_factory.INSTALL_ACTION, pkg_match, opts=metaopts) xterm_header = "equo (%s) :: %d of %d ::" % ( _("install"), count, total) pkg.set_xterm_header(xterm_header) entropy_client.output( purple(atom), count=(count, total), header=darkgreen(" +++ ") + ">>> ") exit_st = pkg.start() if exit_st != 0: if ugc_thread is not None: ugc_thread.join() return 1, True finally: if pkg is not None: pkg.finalize() finally: if notif_acquired: notification_lock.release() if ugc_thread is not None: ugc_thread.join() entropy_client.output( "%s." % ( blue(_("Installation complete")),), header=darkred(" @@ ")) return 0, True