[sulfur] migrate the whole application to the use of the new Entropy Web Services interface

This commit is contained in:
Fabio Erculiani
2011-04-10 23:15:13 +02:00
parent 97c0f986aa
commit 7c62ffa29b
9 changed files with 1288 additions and 753 deletions
+1
View File
@@ -38,6 +38,7 @@ install:
install -m755 $(MISCDIR)/sulfur-uri-handler $(DESTDIR)$(BINDIR)/.
install -m644 $(MISCDIR)/entropy-handler.schemas $(DESTDIR)/etc/gconf/schemas/.
install -m644 $(PIXDIR)/*.png $(DESTDIR)/usr/share/pixmaps/sulfur/.
install -m644 $(PIXDIR)/*.gif $(DESTDIR)/usr/share/pixmaps/sulfur/.
install -m644 $(PIXDIR)/packages/*.png $(DESTDIR)/usr/share/pixmaps/sulfur/packages/.
install -m644 $(PIXDIR)/ugc/*.png $(DESTDIR)/usr/share/pixmaps/sulfur/ugc/.
install -m644 $(MISCDIR)/kde_x-sulfur.desktop $(DESTDIR)/usr/share/mimelnk/application/.
+136 -79
View File
@@ -43,13 +43,14 @@ from entropy.services.exceptions import EntropyServicesError
import entropy.tools
from entropy.const import etpConst, const_get_stringtype, \
initconfig_entropy_constants, const_convert_to_unicode, \
const_drop_privileges, const_regain_privileges
const_debug_write
from entropy.i18n import _
from entropy.misc import ParallelTask
from entropy.cache import EntropyCacher, MtimePingus
from entropy.output import print_generic
from entropy.db.exceptions import ProgrammingError, OperationalError
from entropy.core.settings.base import SystemSettings
from entropy.services.client import WebService
# Sulfur Imports
import gtk, gobject
@@ -59,7 +60,7 @@ from sulfur.setup import SulfurConf, const, fakeoutfile, fakeinfile, \
cleanMarkupString
from sulfur.widgets import SulfurConsole
from sulfur.core import UI, Controller, FORK_PIDS, fork_function, \
busy_cursor, normal_cursor
busy_cursor, normal_cursor, get_entropy_webservice, Privileges
from sulfur.views import *
from sulfur.filters import Filter
from sulfur.dialogs import *
@@ -67,59 +68,14 @@ from sulfur.progress import Base as BaseProgress
from sulfur.events import SulfurApplicationEventsMixin
from sulfur.event import SulfurSignals
class SulfurApplication(Controller, SulfurApplicationEventsMixin):
class Privileges:
def __init__(self, drop_privs = True):
self.__drop_privs = drop_privs
self.__drop_privs_lock = threading.RLock()
self.__with_stmt = 0
def __enter__(self):
"""
Hold the lock.
"""
self.__drop_privs_lock.acquire()
if self.__with_stmt < 1:
self.regain()
self.__with_stmt += 1
def __exit__(self, exc_type, exc_value, traceback):
"""
Drop the lock.
"""
if self.__with_stmt == 1:
self.drop()
self.__with_stmt -= 1
self.__drop_privs_lock.release()
def drop(self):
"""
Drop process privileges. Setting unpriv_gid to etpConst['entropygid']
makes Entropy UGC/Data cache handling working.
"""
if self.__drop_privs:
with self.__drop_privs_lock:
const_drop_privileges(unpriv_gid = etpConst['entropygid'])
const.setup()
def regain(self):
"""
Regain previously dropped process privileges.
"""
if self.__drop_privs:
with self.__drop_privs_lock:
const_regain_privileges()
const.setup()
def __init__(self, drop_privs = True):
def __init__(self):
self.do_debug = False
self._ugc_status = "--nougc" not in sys.argv
self._privileges = self.Privileges(drop_privs = drop_privs)
self._privileges = Privileges()
# Use this lock when you want to make sure that no other asynchronous
# executions are being run.
self._async_event_execution_lock = threading.Lock()
@@ -127,6 +83,7 @@ class SulfurApplication(Controller, SulfurApplicationEventsMixin):
self._entropy = Equo()
self._cacher = EntropyCacher()
self._settings = SystemSettings()
self._webserv_map = {}
# support for packages installation on startup
packages_install, atoms_install, do_fetch = \
@@ -187,6 +144,40 @@ class SulfurApplication(Controller, SulfurApplicationEventsMixin):
# init the Controller Class to connect signals.
Controller.__init__( self, ui )
def _get_webservice(self, repository_id):
webserv = self._webserv_map.get(repository_id)
if webserv == -1:
# not available
return None
if webserv is not None:
return webserv
with self._privileges:
# so that the correct auth store file is fetched
# NOTE: other areas don't do the same, be careful
try:
webserv = get_entropy_webservice(self._entropy, repository_id)
except WebService.UnsupportedService as err:
webserv = None
if webserv is None:
self._webserv_map[repository_id] = -1
# not available
return
try:
available = webserv.service_available()
except WebService.WebServiceException:
available = False
if not available:
self._webserv_map[repository_id] = -1
# not available
return
self._webserv_map[repository_id] = webserv
return webserv
def __scan_packages_install(self):
packages_install = os.environ.get("SULFUR_PACKAGES", '').split(";")
atoms_install = []
@@ -896,23 +887,38 @@ class SulfurApplication(Controller, SulfurApplicationEventsMixin):
if self.do_debug:
print_generic("entering UGC")
if self._entropy.UGC is None:
return
if self._spawning_ugc or self.disable_ugc:
return
if not force:
eapi3_repos = []
webserv_repos = []
for repoid in self._entropy.repositories():
aware = self._entropy.UGC.is_repository_eapi3_aware(repoid)
webserv = self._get_webservice(repoid)
if webserv is None:
continue
try:
aware = webserv.service_available()
except WebService.WebServiceException:
continue
if aware:
eapi3_repos.append(repoid)
webserv_repos.append((webserv, repoid))
cache_available = True
for repoid in eapi3_repos:
if not self._entropy.is_ugc_cached(repoid):
for webserv, repoid in webserv_repos:
cache_available = True
try:
webserv.get_available_votes(cache = True, cached = True)
except WebService.CacheMiss:
cache_available = False
if cache_available:
# also check downloads
try:
webserv.get_available_downloads(cache = True,
cached = True)
except WebService.CacheMiss:
cache_available = False
if not cache_available:
break
pingus_id = "sulfur_ugc_content_spawn"
# check if at least 2 hours are passed since last check
@@ -936,9 +942,11 @@ class SulfurApplication(Controller, SulfurApplicationEventsMixin):
self._ugc_update()
self._cacher.sync()
print_generic("UGC child process done")
emit_ugc_update()
self._spawning_ugc = True
fork_function(do_ugc_sync, emit_ugc_update)
th = ParallelTask(do_ugc_sync)
th.start()
if self.do_debug:
print_generic("quitting UGC")
@@ -947,15 +955,46 @@ class SulfurApplication(Controller, SulfurApplicationEventsMixin):
def _ugc_update(self):
def _update_available_cache(repository_id):
webserv = self._get_webservice(repository_id)
if webserv is None:
return
# fetch vote cache first
try:
webserv.get_available_votes(cache = False)
except WebService.WebServiceException as err:
const_debug_write(__name__,
"_ugc_update.get_available_votes: ouch %s" % (err,))
return
# drop get_votes cache completely
# this enforces EntropyPackage.vote* to use available_votes cache
webserv._drop_cached("get_votes")
try:
webserv.get_available_downloads(cache = False)
except WebService.WebServiceException as err:
const_debug_write(__name__,
"_ugc_update.get_available_downloads: ouch %s" % (err,))
return
# drop get_downloads cache completely
# this enforces EntropyPackage.down* to use available_downloads
# cache
webserv._drop_cached("get_downloads")
for repo in self._entropy.repositories():
if self.do_debug:
t1 = time.time()
print_generic("working UGC update for", repo)
self._entropy.update_ugc_cache(repo)
if self.do_debug:
t2 = time.time()
td = t2 - t1
print_generic("completed UGC update for", repo, "took", td)
t1 = time.time()
const_debug_write(__name__,
"working UGC update for %s" % (repo,))
_update_available_cache(repo)
t2 = time.time()
td = t2 - t1
const_debug_write(__name__,
"completed UGC update for %s, took %s" % (
repo, td,))
def fill_pref_db_backup_page(self):
self.dbBackupStore.clear()
@@ -1030,22 +1069,31 @@ class SulfurApplication(Controller, SulfurApplicationEventsMixin):
obj = model.get_value( myiter, 0 )
if obj:
t = "<i>%s</i>" % (_("Not logged in"),)
if self._entropy.UGC != None:
logged_data = self._entropy.UGC.read_login(obj['repoid'])
if logged_data != None:
t = "<i>%s</i>" % (logged_data[0],)
webserv = self._get_webservice(obj['repoid'])
if webserv is None:
t = "<i>%s</i>" % (_("Not available"),)
else:
with self._privileges:
username = webserv.get_credentials()
if username is not None:
t = "<i>%s</i>" % (username,)
cell.set_property('markup', t)
def get_ugc_status_pix( column, cell, model, myiter ):
if self._entropy.UGC == None:
cell.set_property('icon-name', 'gtk-cancel')
return
if not self._ugc_status:
cell.set_property('icon-name', 'gtk-cancel')
return
obj = model.get_value( myiter, 0 )
if obj:
if self._entropy.UGC.is_repository_eapi3_aware(obj['repoid']):
webserv = self._get_webservice(obj['repoid'])
if webserv is None:
cell.set_property( 'icon-name', 'gtk-cancel' )
return
try:
available = webserv.service_available()
except WebService.WebServiceException:
available = False
if available:
cell.set_property( 'icon-name', 'gtk-apply' )
else:
cell.set_property( 'icon-name', 'gtk-cancel' )
@@ -1436,11 +1484,20 @@ class SulfurApplication(Controller, SulfurApplicationEventsMixin):
for repo in repos:
# inform UGC that we are syncing this repo
if self._entropy.UGC is not None:
try:
self._entropy.UGC.add_download_stats(repo, [repo])
except EntropyServicesError:
continue
webserv = self._get_webservice(repository)
if webserv is None:
continue
try:
available = webserv.service_available()
except WebService.WebServiceException:
continue
if not available:
continue
try:
webserv.add_downloads([repo])
except WebService.WebServiceException as err:
const_debug_write(__name__, repr(err))
continue
if repoConn.sync_errors or (rc != 0):
self.progress.set_mainLabel(_('Errors updating repositories.'))
+88 -1
View File
@@ -19,12 +19,16 @@
import os
import sys
import shutil
import threading
import gtk
import gtk.glade
import gobject
from entropy.const import const_debug_write
from entropy.const import const_debug_write, const_drop_privileges, \
const_regain_privileges, etpConst
from entropy.misc import ParallelTask
from entropy.core import Singleton
from sulfur.setup import const
FORK_PIDS = []
@@ -164,6 +168,26 @@ def resize_image(max_width, image_path, new_image_path):
del img_buf
del img
def resize_image_height(max_height, image_path, new_image_path):
shutil.copy2(image_path, new_image_path)
img = gtk.Image()
img.set_from_file(new_image_path)
img_buf = img.get_pixbuf()
w, h = img_buf.get_width(), img_buf.get_height()
if h > max_height:
# resize pix
new_h = max_height
new_w = new_h*w/h
img_buf = img_buf.scale_simple(int(new_w),
int(new_h), gtk.gdk.INTERP_BILINEAR)
try:
img_buf.save(new_image_path, "png")
except gobject.GError:
# libpng issue? try jpeg
img_buf.save(new_image_path, "jpeg")
del img_buf
del img
def load_url(url):
xdg_open = '/usr/bin/xdg-open'
if os.access(xdg_open, os.X_OK):
@@ -172,3 +196,66 @@ def load_url(url):
# child
os.execv(xdg_open, [xdg_open, url])
os._exit(0)
def get_entropy_webservice(entropy_client, repository_id, tx_cb = None):
"""
Get Entropy Web Services service object (ClientWebService).
@param entropy_client: Entropy Client interface
@type entropy_client: entropy.client.interfaces.Client
@param repository_id: repository identifier
@type repository_id: string
@return: the ClientWebService instance
@rtype: entropy.client.services.interfaces.ClientWebService
@raise WebService.UnsupportedService: if service is unsupported by
repository
"""
factory = entropy_client.WebServices()
webserv = factory.new(repository_id)
if tx_cb is not None:
webserv._set_transfer_callback(tx_cb)
return webserv
class Privileges(Singleton):
def init_singleton(self):
self.__drop_privs = True
self.__drop_privs_lock = threading.RLock()
self.__with_stmt = 0
def __enter__(self):
"""
Hold the lock.
"""
self.__drop_privs_lock.acquire()
if self.__with_stmt < 1:
self.regain()
self.__with_stmt += 1
def __exit__(self, exc_type, exc_value, traceback):
"""
Drop the lock.
"""
if self.__with_stmt == 1:
self.drop()
self.__with_stmt -= 1
self.__drop_privs_lock.release()
def drop(self):
"""
Drop process privileges. Setting unpriv_gid to etpConst['entropygid']
makes Entropy UGC/Data cache handling working.
"""
if self.__drop_privs:
with self.__drop_privs_lock:
const_drop_privileges(unpriv_gid = etpConst['entropygid'])
const.setup()
def regain(self):
"""
Regain previously dropped process privileges.
"""
if self.__drop_privs:
with self.__drop_privs_lock:
const_regain_privileges()
const.setup()
File diff suppressed because it is too large Load Diff
+19 -11
View File
@@ -25,10 +25,11 @@ except ImportError:
import _thread as thread
from sulfur.setup import const, SulfurConf
from sulfur.dialogs import LicenseDialog, okDialog, choiceDialog, inputDialog
from sulfur.core import get_entropy_webservice
import gobject
# Entropy Imports
from entropy.const import etpConst, etpUi
from entropy.const import etpConst, etpUi, const_debug_write
from entropy.output import print_generic, nocolor, decolorize
from entropy.client.interfaces import Client
from entropy.fetchers import UrlFetcher
@@ -37,6 +38,7 @@ from entropy.misc import ParallelTask
from entropy.client.mirrors import StatusInterface
from entropy.exceptions import RepositoryError
from entropy.db.exceptions import OperationalError, IntegrityError
from entropy.services.client import WebService
import entropy.dep
import entropy.tools
@@ -193,14 +195,22 @@ class QueueExecutor:
if not download_sources:
def spawn_ugc():
try:
if self._entropy.UGC != None:
for myrepo in mykeys:
mypkgkeys = sorted(mykeys[myrepo])
self._entropy.UGC.add_download_stats(myrepo,
mypkgkeys)
except:
pass
for myrepo, mykeys in mykeys.items():
try:
webserv = get_entropy_webservice(self._entropy,
myrepo)
except WebService.UnsupportedService:
continue
try:
avail = webserv.service_available()
except WebService.WebServiceException:
avail = False
if avail:
try:
webserv.add_downloads(sorted(mykeys))
except WebService.WebServiceException as err:
const_debug_write(__name__, repr(err))
continue
spawn_ugc()
def do_skip_hide():
@@ -315,8 +325,6 @@ class Equo(Client):
Client.init_singleton(self, *args, **kwargs)
self._progress_divider = 1
self.xcache = True # force xcache enabling
if etpUi['debug']:
self.UGC.quiet = False
self._mthread_rc = {
'ask_question': {},
'input_box': {},
+51 -23
View File
@@ -35,6 +35,7 @@ from entropy.output import print_generic
from entropy.i18n import _
from entropy.misc import ParallelTask
from entropy.client.mirrors import StatusInterface
from entropy.services.client import WebService
import entropy.dump
from sulfur.filters import Filter
@@ -563,7 +564,8 @@ class SulfurApplicationEventsMixin:
do_clear_filter_bar = False
if action != self.lastPkgPB:
do_clear_filter_bar = True
# disabled for now, users are complaining
do_clear_filter_bar = False
if action == "updates":
self.ui.updatesButtonbox.show()
@@ -813,46 +815,72 @@ class SulfurApplicationEventsMixin:
self.load_color_settings()
def on_ugcLoginButton_clicked(self, widget):
if self._entropy.UGC == None: return
if not self._ugc_status:
return
model, myiter = self.ugcRepositoriesView.get_selection().get_selected()
if (myiter == None) or (model == None): return
if (myiter == None) or (model == None):
return
obj = model.get_value( myiter, 0 )
if obj:
#logged_data = self._entropy.UGC.read_login(obj['repoid'])
self._entropy.UGC.login(obj['repoid'], force = True)
webserv = self._get_webservice(obj['repoid'])
if webserv is None:
return
def fake_callback(*args, **kwargs):
return True
# use input box to read login
input_params = [
('username', _('Username'), fake_callback, False),
('password', _('Password'), fake_callback, True)
]
login_data = self._entropy.input_box(
"%s %s %s" % (
_('Please login against'), obj['repoid'], _('repository'),),
input_params,
cancel_button = True
)
if not login_data:
okDialog(self.ui.main, _("Login aborted. Not logged in."))
return
username, password = login_data['username'], login_data['password']
with self._privileges:
webserv.add_credentials(username, password)
try:
webserv.validate_credentials()
except WebService.AuthenticationFailed:
okDialog(self.ui.main,
_("Authentication error. Not logged in."))
return
self.load_ugc_repositories()
def on_ugcClearLoginButton_clicked(self, widget):
if self._entropy.UGC == None: return
if not self._ugc_status:
return
model, myiter = self.ugcRepositoriesView.get_selection().get_selected()
if (myiter == None) or (model == None): return
if (myiter == None) or (model == None):
return
obj = model.get_value( myiter, 0 )
if obj:
if not self._entropy.UGC.is_repository_eapi3_aware(obj['repoid']):
webserv = self._get_webservice(obj['repoid'])
if webserv is None:
return
logged_data = self._entropy.UGC.read_login(obj['repoid'])
if logged_data: self._entropy.UGC.remove_login(obj['repoid'])
with self._privileges:
webserv.remove_credentials()
self.load_ugc_repositories()
def on_ugcClearCacheButton_clicked(self, widget):
if self._entropy.UGC == None: return
repo_excluded = self._settings['repositories']['excluded']
avail_repos = self._settings['repositories']['available']
for repoid in list(set(list(avail_repos.keys())+list(repo_excluded.keys()))):
self._entropy.UGC.UGCCache.clear_cache(repoid)
self.set_status_ticker("%s: %s ..." % (_("Cleaning UGC cache of"), repoid,))
self.set_status_ticker("%s" % (_("UGC cache cleared"),))
def on_ugcClearCredentialsButton_clicked(self, widget):
if self._entropy.UGC == None:
if not self._ugc_status:
return
repo_excluded = self._settings['repositories']['excluded']
avail_repos = self._settings['repositories']['available']
for repoid in list(set(list(avail_repos.keys())+list(repo_excluded.keys()))):
if not self._entropy.UGC.is_repository_eapi3_aware(repoid):
webserv = self._get_webservice(repoid)
if webserv is None:
continue
logged_data = self._entropy.UGC.read_login(repoid)
if logged_data: self._entropy.UGC.remove_login(repoid)
with self._privileges:
webserv.remove_credentials()
self.load_ugc_repositories()
self.set_status_ticker("%s" % (_("UGC credentials cleared"),))
+72 -13
View File
@@ -14,10 +14,15 @@ from entropy.const import *
from entropy.i18n import _
import entropy.dep
import entropy.tools
from entropy.services.client import WebService
from sulfur.entropyapi import Equo
from sulfur.setup import cleanMarkupString, SulfurConf
from sulfur.core import get_entropy_webservice
ENTROPY = Equo()
WEBSERV_MAP = {}
class DummyEntropyPackage:
@@ -130,6 +135,25 @@ class EntropyPackage:
self.matched_atom = matched_atom
self.installed_match = None
def _get_webservice(self):
repository_id = self.get_repository_clean()
webserv = WEBSERV_MAP.get(repository_id)
if webserv == -1:
return None # not available
if webserv is not None:
return webserv
try:
webserv = get_entropy_webservice(ENTROPY, repository_id)
except WebService.UnsupportedService:
WEBSERV_MAP[repository_id] = -1
return None
WEBSERV_MAP[repository_id] = webserv
return webserv
# for debugging purposes, sample method
"""
def __setattr__(self, name, value):
@@ -263,8 +287,7 @@ class EntropyPackage:
if atom is None: # wtf!
return 'N/A'
key = entropy.dep.dep_getkey(atom)
downloads = ENTROPY.UGC.UGCCache.get_package_downloads(repo_clean, key)
downloads = self.get_ugc_package_downloads()
ugc_string = '<small>[%s|<span foreground="%s">%s</span>]</small> ' % (
downloads, SulfurConf.color_title2, repo_clean,)
if ver_str:
@@ -577,14 +600,40 @@ class EntropyPackage:
return ENTROPY.Settings()['repositories']['branch']
return self.dbconn.retrieveBranch(self.matched_id)
def _get_vote_raw(self, pkg_key):
webserv = self._get_webservice()
if webserv is None:
return None
# try to get vote for the single package first, if it fails,
# we'll search the info inside the available vote data.
got_miss = False
try:
vote = webserv.get_votes([pkg_key], cache = True,
cached = True)[pkg_key]
except WebService.CacheMiss:
vote = None
got_miss = True
# found it?
if vote is not None:
return vote
# fallback to available cache
try:
return webserv.get_available_votes(cache = True,
cached = True).get(pkg_key)
except WebService.CacheMiss:
return None
def get_ugc_package_vote(self):
if self.pkgset:
return -1
atom = self.get_name()
if not atom:
return None
return ENTROPY.UGC.UGCCache.get_package_vote(
self.get_repository_clean(), entropy.dep.dep_getkey(atom))
pkg_key = entropy.dep.dep_getkey(atom)
return self._get_vote_raw(pkg_key)
def get_ugc_package_vote_int(self):
if self.pkgset:
@@ -592,9 +641,10 @@ class EntropyPackage:
atom = self.get_name()
if not atom:
return 0
vote = ENTROPY.UGC.UGCCache.get_package_vote(
self.get_repository_clean(), entropy.dep.dep_getkey(atom))
if not isinstance(vote, float):
pkg_key = entropy.dep.dep_getkey(atom)
vote = self._get_vote_raw(pkg_key)
if vote is None:
return 0
return int(vote)
@@ -604,9 +654,10 @@ class EntropyPackage:
atom = self.get_name()
if not atom:
return 0.0
vote = ENTROPY.UGC.UGCCache.get_package_vote(
self.get_repository_clean(), entropy.dep.dep_getkey(atom))
if not isinstance(vote, float):
pkg_key = entropy.dep.dep_getkey(atom)
vote = self._get_vote_raw(pkg_key)
if vote is None:
return 0.0
return vote
@@ -619,9 +670,17 @@ class EntropyPackage:
atom = self.get_name()
if not atom:
return 0
key = entropy.dep.dep_getkey(atom)
return ENTROPY.UGC.UGCCache.get_package_downloads(
self.get_repository_clean(), key)
webserv = self._get_webservice()
if webserv is None:
return 0
pkg_key = entropy.dep.dep_getkey(atom)
try:
downloads = webserv.get_available_downloads(cache = True,
cached = True).get(pkg_key, 0)
except WebService.CacheMiss:
return 0
return downloads
def get_attribute(self, attr):
cached = self.__cache.get(('get_attribute', attr,))
+39 -152
View File
@@ -124,9 +124,9 @@
<property name="can_focus">True</property>
<property name="border_width">2</property>
<property name="show_border">False</property>
<signal name="change_current_page" handler="on_notebook_change_current_page"/>
<signal name="switch_page" handler="on_notebook_switch_page"/>
<signal name="select_page" handler="on_notebook_select_page"/>
<signal name="change_current_page" handler="on_notebook_change_current_page"/>
<child>
<widget class="GtkVBox" id="packagesVbox">
<property name="visible">True</property>
@@ -2740,8 +2740,8 @@ Relaxed</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<signal name="clicked" handler="on_configProtectSkipNew_clicked"/>
<signal name="released" handler="on_Preferences_toggled"/>
<signal name="clicked" handler="on_configProtectSkipNew_clicked"/>
<child>
<widget class="GtkImage" id="image23">
<property name="visible">True</property>
@@ -2783,8 +2783,8 @@ Relaxed</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<signal name="clicked" handler="on_configProtectSkipDelete_clicked"/>
<signal name="released" handler="on_Preferences_toggled"/>
<signal name="clicked" handler="on_configProtectSkipDelete_clicked"/>
<child>
<widget class="GtkImage" id="image36">
<property name="visible">True</property>
@@ -2869,8 +2869,8 @@ Relaxed</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<signal name="clicked" handler="on_configProtectMaskNew_clicked"/>
<signal name="released" handler="on_Preferences_toggled"/>
<signal name="clicked" handler="on_configProtectMaskNew_clicked"/>
<child>
<widget class="GtkImage" id="image15">
<property name="visible">True</property>
@@ -2891,8 +2891,8 @@ Relaxed</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<signal name="clicked" handler="on_configProtectMaskEdit_clicked"/>
<signal name="released" handler="on_Preferences_toggled"/>
<signal name="clicked" handler="on_configProtectMaskEdit_clicked"/>
<child>
<widget class="GtkImage" id="image29">
<property name="visible">True</property>
@@ -2913,8 +2913,8 @@ Relaxed</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<signal name="clicked" handler="on_configProtectMaskDelete_clicked"/>
<signal name="released" handler="on_Preferences_toggled"/>
<signal name="clicked" handler="on_configProtectMaskDelete_clicked"/>
<child>
<widget class="GtkImage" id="image35">
<property name="visible">True</property>
@@ -2998,8 +2998,8 @@ Relaxed</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<signal name="clicked" handler="on_configProtectNew_clicked"/>
<signal name="released" handler="on_Preferences_toggled"/>
<signal name="clicked" handler="on_configProtectNew_clicked"/>
<child>
<widget class="GtkImage" id="image13">
<property name="visible">True</property>
@@ -3020,8 +3020,8 @@ Relaxed</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<signal name="clicked" handler="on_configProtectEdit_clicked"/>
<signal name="released" handler="on_Preferences_toggled"/>
<signal name="clicked" handler="on_configProtectEdit_clicked"/>
<child>
<widget class="GtkImage" id="image27">
<property name="visible">True</property>
@@ -3042,8 +3042,8 @@ Relaxed</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<signal name="clicked" handler="on_configProtectDelete_clicked"/>
<signal name="released" handler="on_Preferences_toggled"/>
<signal name="clicked" handler="on_configProtectDelete_clicked"/>
<child>
<widget class="GtkImage" id="image33">
<property name="visible">True</property>
@@ -3441,34 +3441,6 @@ Relaxed</property>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="ugcClearCacheButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip" translatable="yes">Clear all cache</property>
<signal name="clicked" handler="on_ugcClearCacheButton_clicked"/>
<child>
<widget class="GtkHBox" id="hbox46">
<property name="visible">True</property>
<child>
<widget class="GtkImage" id="image37">
<property name="visible">True</property>
<property name="stock">gtk-harddisk</property>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
</widget>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
@@ -6998,8 +6970,8 @@ gz</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_closeInfo_clicked"/>
<signal name="delete_event" handler="on_closeInfo_delete_event"/>
<signal name="clicked" handler="on_closeInfo_clicked"/>
<child>
<widget class="GtkImage" id="image14">
<property name="visible">True</property>
@@ -7988,90 +7960,6 @@ gz</property>
<widget class="GtkVBox" id="vbox53">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
<widget class="GtkHBox" id="hbox32">
<property name="visible">True</property>
<child>
<widget class="GtkImage" id="ugcIcon">
<property name="visible">True</property>
<property name="stock">gtk-dnd</property>
<property name="pixel_size">0</property>
<property name="icon-size">6</property>
</widget>
<packing>
<property name="padding">3</property>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkVBox" id="vbox60">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
<widget class="GtkLabel" id="ugcTitleLabel">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="yalign">1</property>
<property name="label" translatable="yes">User Generated Content</property>
<property name="use_markup">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="padding">3</property>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="ugcDescriptionLabel">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
<property name="label">User Generated Content description</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkAlignment" id="alignment10">
<property name="visible">True</property>
<property name="bottom_padding">20</property>
<property name="right_padding">6</property>
<child>
<widget class="GtkButton" id="loadUgcButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip" translatable="yes">Load User Generated Content</property>
<signal name="clicked" handler="on_loadUgcButton_clicked"/>
<child>
<widget class="GtkImage" id="refreshImage">
<property name="visible">True</property>
<property name="stock">gtk-refresh</property>
<property name="icon-size">6</property>
</widget>
</child>
</widget>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="padding">6</property>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkVBox" id="vbox55">
<property name="visible">True</property>
@@ -8195,7 +8083,6 @@ gz</property>
<child>
<widget class="GtkHButtonBox" id="hbuttonbox5">
<property name="visible">True</property>
<property name="spacing">6</property>
<property name="layout_style">end</property>
<child>
<widget class="GtkButton" id="ugcShowButton">
@@ -8238,6 +8125,29 @@ gz</property>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="loadUgcButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="has_tooltip">True</property>
<property name="tooltip" translatable="yes">Load User Generated Content</property>
<signal name="clicked" handler="on_loadUgcButton_clicked"/>
<child>
<widget class="GtkImage" id="refreshImage">
<property name="visible">True</property>
<property name="pixel_size">22</property>
<property name="icon_name">view-refresh</property>
<property name="icon-size">3</property>
</widget>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</widget>
<packing>
<property name="expand">False</property>
@@ -8247,7 +8157,7 @@ gz</property>
</child>
</widget>
<packing>
<property name="position">1</property>
<property name="position">0</property>
</packing>
</child>
</widget>
@@ -9282,6 +9192,7 @@ Please read them carefully and &lt;b&gt;make your choice&lt;/b&gt;.</property>
<widget class="GtkVBox" id="vbox59">
<property name="visible">True</property>
<property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
<property name="orientation">vertical</property>
<child>
<widget class="GtkHBox" id="hbox3">
<property name="visible">True</property>
@@ -9359,8 +9270,8 @@ Please read them carefully and &lt;b&gt;make your choice&lt;/b&gt;.</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="on_closeInfo_clicked"/>
<signal name="delete_event" handler="on_closeInfo_clicked"/>
<signal name="clicked" handler="on_closeInfo_clicked"/>
<child>
<widget class="GtkImage" id="image20">
<property name="visible">True</property>
@@ -9385,37 +9296,13 @@ Please read them carefully and &lt;b&gt;make your choice&lt;/b&gt;.</property>
<child>
<widget class="GtkVBox" id="vbox64">
<property name="visible">True</property>
<property name="orientation">vertical</property>
<child>
<widget class="GtkTable" id="ugcTable">
<property name="visible">True</property>
<property name="n_rows">6</property>
<property name="n_rows">5</property>
<property name="n_columns">2</property>
<property name="row_spacing">3</property>
<child>
<widget class="GtkLabel" id="sizeContent">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label">this is the size</property>
<property name="wrap">True</property>
</widget>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="sizeLabel">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Size</property>
</widget>
<packing>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="keywordsContent">
<property name="visible">True</property>
@@ -11393,8 +11280,8 @@ Please read them carefully and &lt;b&gt;make your choice&lt;/b&gt;.</property>
<child>
<widget class="GtkEventBox" id="repoManagerConsoleEvent">
<property name="visible">True</property>
<signal name="enter_notify_event" handler="on_repoManagerConsoleEvent_enter_notify_event"/>
<signal name="key_release_event" handler="on_repoManagerConsoleEvent_key_release_event"/>
<signal name="enter_notify_event" handler="on_repoManagerConsoleEvent_enter_notify_event"/>
<child>
<widget class="GtkHBox" id="repoManagerTermHBox">
<property name="visible">True</property>
+292 -125
View File
@@ -25,16 +25,16 @@ import tempfile
from entropy.exceptions import RepositoryError, InvalidPackageSet
from entropy.const import etpConst, etpSys, initconfig_entropy_constants, \
const_debug_write
const_debug_write, const_debug_enabled, const_get_stringtype
from entropy.misc import ParallelTask, TimeScheduled
from entropy.i18n import _, _LOCALE
from entropy.tools import print_traceback
from entropy.dep import dep_getkey
from entropy.const import const_get_stringtype
from sulfur.setup import const, cleanMarkupString, SulfurConf
from sulfur.core import UI, busy_cursor, normal_cursor, \
STATUS_BAR_CONTEXT_IDS, resize_image, fork_function
STATUS_BAR_CONTEXT_IDS, resize_image, fork_function, \
get_entropy_webservice, Privileges
from sulfur.widgets import CellRendererStars
from sulfur.package import DummyEntropyPackage, EntropyPackage
from sulfur.entropyapi import Equo
@@ -44,6 +44,8 @@ from sulfur.event import SulfurSignals
from entropy.db.exceptions import ProgrammingError, Error as DbError, \
OperationalError, DatabaseError
from entropy.services.client import WebService
from entropy.client.services.interfaces import ClientWebService
class EntropyPackageViewModelInjector:
@@ -494,6 +496,7 @@ class EntropyPackageView:
self.selected_objs = []
self.last_row = None
self.loaded_reinstallables = []
self._webserv_map = {}
self._pixbuf_map = {}
self.loaded_event = None
self.do_refresh_view = False
@@ -689,15 +692,14 @@ class EntropyPackageView:
self.queue_full_exception = Full
self.queue_empty_exception = Empty
self.__pkg_ugc_icon_local_path_cache = {}
self.__pkg_ugc_icon_cache = {}
self.__pkg_ugc_icon_call_cache = {}
# max 64 items at time, don't lower it too much, can cause
# request starvation with the current very low priority
self._ugc_load_queue = queue_class(64)
self._ugc_load_thread = TimeScheduled(3, self._ugc_queue_run)
# avoid DoS, lol
self._ugc_icon_load_queue = queue_class(64)
self._ugc_icon_thread = TimeScheduled(3, self._ugc_icon_queue_run)
if self._ugc_status:
# deferred loading, speedup UI init
gobject.timeout_add_seconds(15, self._ugc_load_thread.start)
gobject.timeout_add_seconds(15, self._ugc_icon_thread.start)
def __update_ugc_event(self, event):
self.view.queue_draw()
@@ -864,7 +866,7 @@ class EntropyPackageView:
self.loaded_event = event
del self.loaded_reinstallables[:]
if objs == None:
if objs is None:
objs = self.collect_selected_items(widget)
event_x = event.x
@@ -909,16 +911,16 @@ class EntropyPackageView:
return True
elif len(objs) == 1:
obj = objs[0]
distance = 0
for col in self.view.get_columns()[0:2]:
distance += col.get_width()
if (event_x > distance) and (event_x < (distance+self.stars_col_size)):
vote = int((event_x - distance)/self.stars_col_size*5)+1
# this is required to fix idiotic GTK behaviour
elif column.get_title() == self.pkgcolumn_text_rating:
def _do_vote(x, widget):
rel_distance = x
valid_votes = ClientWebService.VALID_VOTES
vote = int(float(rel_distance) / self.stars_col_size * \
len(valid_votes)) + 1
self.delayed_vote_submit(widget, vote)
return True
return False
gobject.idle_add(_do_vote, x, widget)
return True
return False
@@ -952,6 +954,7 @@ class EntropyPackageView:
def run_properties_menu(self, menu_item):
objs = self.collect_selected_items()
for obj in objs:
if obj.is_group or obj.is_pkgset_cat:
continue
@@ -1141,7 +1144,8 @@ class EntropyPackageView:
busy_cursor(self.main_window)
objs = self.selected_objs
oldmask = self.etpbase.unmaskingPackages.copy()
mydialog = MaskedPackagesDialog(self._entropy, self.etpbase, self.ui.main, objs)
mydialog = MaskedPackagesDialog(self._entropy, self.etpbase,
self.ui.main, objs)
result = mydialog.run()
if result != -5:
self.etpbase.unmaskingPackages = oldmask.copy()
@@ -1734,25 +1738,116 @@ class EntropyPackageView:
return store
def _get_webservice(self, repository_id):
webserv = self._webserv_map.get(repository_id)
if webserv == -1:
# not available
return None
if webserv is not None:
return webserv
with Privileges():
try:
webserv = get_entropy_webservice(self._entropy, repository_id)
except WebService.UnsupportedService as err:
webserv = None
if webserv is None:
self._webserv_map[repository_id] = -1
# not available
return
try:
available = webserv.service_available()
except WebService.WebServiceException:
available = False
if not available:
self._webserv_map[repository_id] = -1
# not available
return
self._webserv_map[repository_id] = webserv
return webserv
def _ugc_available(self, repository_id):
webserv = self._get_webservice(repository_id)
if webserv is None:
return False
return True
def _ugc_cache_icons(self, repository_id, package_names, cache):
webserv = self._get_webservice(repository_id)
if webserv is None:
return
for package_name in package_names:
with Privileges():
# sorry web service, we need data this way
try:
icon_docs = webserv.get_icons([package_name],
cache = cache)[package_name]
except WebService.WebServiceException as err:
return
# get document urls, store to local cache
for icon_doc in icon_docs:
cache_key = package_name, icon_doc.repository_id()
if cache_key in self.__pkg_ugc_icon_local_path_cache:
const_debug_write(__name__,
"_ugc_cache_icons: already in cache: %s" % (
cache_key,))
continue
with Privileges():
try:
local_path = webserv.get_document_url(icon_doc)
except ClientWebService.DocumentError as err:
const_debug_write(__name__,
"_ugc_cache_icons: document error: %s" % (
err,))
continue
try:
self.__pkg_ugc_icon_cache.pop(cache_key)
except KeyError:
pass
self.__pkg_ugc_icon_local_path_cache[cache_key] = local_path
const_debug_write(__name__,
"_ugc_cache_icons: pushed to cache: %s, %s" % (
cache_key, local_path,))
break
def __ugc_dnd_updates_clear_cache(self, *args):
const_debug_write(__name__, "EntropyPackageView, "
"__ugc_dnd_updates_clear_cache called")
while True:
try:
key, repoid = self._ugc_dnd_cache_taint.pop()
except KeyError:
break
self._entropy.UGC.get_docs(repoid, key)
def _do_parallel():
self.__pkg_ugc_icon_cache.clear()
self.__pkg_ugc_icon_call_cache.clear()
self._ugc_pixbuf_map.clear()
self._ugc_metadata_sync_exec_cache.clear()
self._emit_ugc_update()
fetch_map = {}
while True:
try:
key, repoid = self._ugc_dnd_cache_taint.pop()
except KeyError:
break
obj = fetch_map.setdefault(repoid, set())
obj.add(key)
def _ugc_drag_data_received(self, view, context, x, y, selection, drop_id, etime):
for repository_id, keys in fetch_map.items():
self._ugc_cache_icons(repository_id, keys, False)
self.__pkg_ugc_icon_cache.clear()
self._ugc_pixbuf_map.clear()
self._ugc_metadata_sync_exec_cache.clear()
self._emit_ugc_update()
self.__pkg_ugc_icon_local_path_cache.clear()
th = ParallelTask(_do_parallel)
th.start()
def _ugc_drag_data_received(self, view, context, x, y, selection, drop_id,
etime):
"""
Callback function for drag_data_received event used for UGC interaction
on Drag and Drop.
@@ -1857,7 +1952,7 @@ class EntropyPackageView:
obj.voted = 0.0
return
repository = obj.repoid
if not self._entropy.UGC.is_repository_eapi3_aware(repository):
if not self._ugc_available(repository):
obj.voted = 0.0
return
atom = obj.name
@@ -1868,8 +1963,80 @@ class EntropyPackageView:
t = ParallelTask(self.vote_submit_thread, repository, key, obj)
t.start()
def _ugc_login(self, webserv, repository):
def fake_callback(*args, **kwargs):
return True
# use input box to read login
input_params = [
('username', _('Username'), fake_callback, False),
('password', _('Password'), fake_callback, True)
]
login_data = self._entropy.input_box(
"%s %s %s" % (
_('Please login against'), repository, _('repository'),),
input_params,
cancel_button = True
)
if not login_data:
return False
username, password = login_data['username'], login_data['password']
with Privileges():
webserv.add_credentials(username, password)
try:
webserv.validate_credentials()
except WebService.AuthenticationFailed:
okDialog(self.ui.main,
_("Authentication error. Not logged in."))
return False
okDialog(self.ui.main,
_("Successfully logged in."))
return True
def vote_submit_thread(self, repository, key, obj):
status, err_msg = self._entropy.UGC.add_vote(repository, key, int(obj.voted))
status = True
err_msg = None
webserv = self._get_webservice(repository)
if webserv is None:
err_msg = _("Unsupported Service")
status = False
if status:
with Privileges():
try:
status = webserv.add_vote(key, int(obj.voted))
except WebService.AuthenticationRequired:
def _do_login():
logged_in = self._ugc_login(webserv, repository)
if logged_in:
# call myself again
gobject.idle_add(self.vote_submit_thread,
repository, key, obj)
else:
t = ParallelTask(self.refresh_vote_info, obj)
t.start()
return False
gobject.idle_add(_do_login)
return False
except WebService.WebServiceException as err:
err_msg = repr(err)
status = False
if status:
with Privileges():
# need to refill local cache
done = True
try:
webserv.get_votes([key], cache = False)
except WebService.WebServiceException as err:
# ouch! drop everything completely
webserv._drop_cached("get_votes")
done = False
if status:
color = SulfurConf.color_good
@@ -1878,19 +2045,16 @@ class EntropyPackageView:
else:
color = SulfurConf.color_error
txt1 = _("Error registering vote")
txt2 = err_msg
txt2 = err_msg or _("Already voted")
msg = "<span foreground='%s'><b>%s</b></span>: %s" % (
color, txt1, txt2,)
def do_refresh(msg):
self.ui.status.push(STATUS_BAR_CONTEXT_IDS['UGC'],
"%s: %s" % (txt1, txt2,))
self.ui.UGCMessageLabel.show()
self.ui.UGCMessageLabel.set_markup(msg)
return False
def remove_ugc_sts():
self.ui.status.pop(STATUS_BAR_CONTEXT_IDS['UGC'])
self.ui.UGCMessageLabel.set_markup("")
self.ui.UGCMessageLabel.hide()
return False
@@ -1899,6 +2063,7 @@ class EntropyPackageView:
gobject.timeout_add_seconds(20, remove_ugc_sts)
t = ParallelTask(self.refresh_vote_info, obj)
t.start()
return False
def refresh_vote_info(self, obj):
time.sleep(5)
@@ -2045,12 +2210,12 @@ class EntropyPackageView:
else:
cell.set_property('foreground', None)
def _ugc_queue_run(self):
def _ugc_icon_queue_run(self):
const_debug_write(__name__, "_ugc_queue_run called")
const_debug_write(__name__, "_ugc_icon_queue_run called")
pkgs = set()
queue = self._ugc_load_queue
queue = self._ugc_icon_load_queue
while queue.qsize():
try:
@@ -2062,40 +2227,24 @@ class EntropyPackageView:
if pkgs:
const_debug_write(__name__,
"_ugc_queue_run, spawning fetch of: %s" % (pkgs,))
self._spawn_ugc_metadata_fetch(pkgs)
"_ugc_icon_queue_run, spawning fetch of: %s" % (pkgs,))
self._spawn_ugc_icon_docs_fetch(pkgs)
def _spawn_ugc_metadata_fetch(self, pkgs):
def _spawn_ugc_icon_docs_fetch(self, pkgs):
def do_ugc_sync():
pkg_map = {}
for key, repoid in pkgs:
const_debug_write(__name__,
"_spawn_ugc_metadata_fetch, checking: %s, %s" % (
key, repoid,))
if self._entropy.UGC.UGCCache.is_alldocs_cached(key, repoid):
const_debug_write(__name__,
"_spawn_ugc_metadata_fetch, skipping !!: %s, %s" % (
key, repoid,))
continue
outcome = self._entropy.UGC.get_docs(repoid, key)
const_debug_write(__name__,
"_spawn_ugc_metadata_fetch, get_docs() called: %s" % (
outcome,))
self.__pkg_ugc_icon_call_cache.clear()
obj = pkg_map.setdefault(repoid, [])
obj.append(key)
def do_fork():
fork_function(do_ugc_sync, self._emit_ugc_update)
for repoid, keys in pkg_map.items():
self._ugc_cache_icons(repoid, keys, True)
self._emit_ugc_update()
gobject.idle_add(do_fork, priority = gobject.PRIORITY_LOW)
def _spawn_ugc_icon_fetch(self, icon_doc, repoid):
def do_icon_fetch():
self._entropy.UGC.UGCCache.store_document(icon_doc['iddoc'],
repoid, icon_doc['store_url'])
fork_function(do_icon_fetch, self._emit_ugc_update)
th = ParallelTask(do_ugc_sync)
th.start()
def _get_cached_pkg_ugc_icon(self, pkg):
@@ -2109,9 +2258,12 @@ class EntropyPackageView:
cache_key = (key, repoid,)
cached = self.__pkg_ugc_icon_cache.get(cache_key)
if cached == -1:
# unavailable
return None
if cached is not None:
const_debug_write(__name__, "_get_cached_pkg_ugc_icon %s in RAM" % (
cache_key,))
#const_debug_write(__name__, "_get_cached_pkg_ugc_icon %s in RAM" % (
# cache_key,))
return cached
# validate variables...
@@ -2120,38 +2272,41 @@ class EntropyPackageView:
if repoid is None:
return
icon_doc = self._entropy.UGC.UGCCache.get_icon_cache(key, repoid)
if icon_doc is None:
const_debug_write(__name__,
"_get_cached_pkg_ugc_icon %s NOT on disk" % (
cache_key,))
# not cached
return
const_debug_write(__name__,
"_get_cached_pkg_ugc_icon %s on disk?" % (
webserv = self._get_webservice(repoid)
if webserv is None:
const_debug_write(__name__, "_get_cached_pkg_ugc_icon %s unsup." % (
cache_key,))
return
store_path = self._entropy.UGC.UGCCache.get_stored_document(
icon_doc['iddoc'], repoid, icon_doc['store_url'])
store_path = self.__pkg_ugc_icon_local_path_cache.get(cache_key)
if store_path is None:
# not cached
called = self.__pkg_ugc_icon_call_cache.get(cache_key)
if not called:
self._spawn_ugc_icon_fetch(icon_doc, repoid)
self.__pkg_ugc_icon_call_cache[cache_key] = True
const_debug_write(__name__,
"_get_cached_pkg_ugc_icon %s not cached on disk" % (
cache_key,))
return
if not (os.access(store_path, os.R_OK) and os.path.isfile(store_path)):
# not cached
const_debug_write(__name__,
"_get_cached_pkg_ugc_icon %s path not available" % (
cache_key,))
return
with Privileges():
# sorry web service, we need data this way
try:
icon_docs = webserv.get_icons([key], cache = True,
cached = True)[key]
except WebService.CacheMiss as err:
#const_debug_write(__name__,
# "_get_cached_pkg_ugc_icon %s NOT cached yet" % (
# cache_key,))
self.__pkg_ugc_icon_cache[cache_key] = -1
return
# get document urls, store to local cache
for icon_doc in icon_docs:
local_path = icon_doc.local_document()
if local_path is None:
continue
store_path = local_path
break
if store_path is None:
#const_debug_write(__name__,
# "_get_cached_pkg_ugc_icon %s NOT cached yet (2)" % (
# cache_key,))
self.__pkg_ugc_icon_cache[cache_key] = -1
return
icon_path = store_path + ".sulfur_icon_small"
pixbuf = self._ugc_pixbuf_map.get(icon_path)
@@ -2165,7 +2320,8 @@ class EntropyPackageView:
if not (os.path.isfile(icon_path) and \
os.access(icon_path, os.R_OK)):
try:
resize_image(self._get_row_height(), store_path,
# keep some margin... -5
resize_image(self._get_row_height() - 5, store_path,
icon_path)
except (ValueError, OSError, IOError, gobject.GError):
# OSError = source file moved while copying
@@ -2191,52 +2347,63 @@ class EntropyPackageView:
def __new_ugc_pixbuf_stash_fetch(self, pkg):
# stash to queue for loading from WWW if required
const_debug_write(__name__,
"__new_ugc_pixbuf_stash_fetch called: %s" % (
pkg,))
#if const_debug_enabled():
# const_debug_write(__name__,
# "__new_ugc_pixbuf_stash_fetch called: %s" % (
# pkg,))
if not self._ugc_status:
const_debug_write(__name__,
"__new_ugc_pixbuf_stash_fetch UGC STATUS FALSE!")
if const_debug_enabled():
const_debug_write(__name__,
"__new_ugc_pixbuf_stash_fetch UGC STATUS FALSE!")
return
const_debug_write(__name__,
"__new_ugc_pixbuf_stash_fetch going on for: %s" % (
pkg,))
#if const_debug_enabled():
# const_debug_write(__name__,
# "__new_ugc_pixbuf_stash_fetch going on for: %s" % (
# pkg,))
try:
repoid = pkg.repoid
sync_item = (pkg.key, repoid)
except (ProgrammingError, OperationalError) as err:
const_debug_write(__name__,
"__new_ugc_pixbuf_stash_fetch, ouch: %s" % (
repr(err),))
if const_debug_enabled():
const_debug_write(__name__,
"__new_ugc_pixbuf_stash_fetch, ouch: %s" % (
repr(err),))
return
if sync_item in self._ugc_metadata_sync_exec_cache:
const_debug_write(__name__,
"__new_ugc_pixbuf_stash_fetch: already in cache: %s" % (
sync_item,))
#if const_debug_enabled():
# const_debug_write(__name__,
# "__new_ugc_pixbuf_stash_fetch: already in cache: %s" % (
# sync_item,))
return
self._ugc_metadata_sync_exec_cache.add(sync_item)
if not self._entropy.UGC.is_repository_eapi3_aware(repoid):
const_debug_write(__name__,
"__new_ugc_pixbuf_stash_fetch: repository not EAPI3 aware: %s" % (
repoid,))
if not self._ugc_available(repoid):
if const_debug_enabled():
const_debug_write(__name__,
"__new_ugc_pixbuf_stash_fetch: "
"repository not Web Services aware: %s" % (
repoid,))
return
const_debug_write(__name__,
"__new_ugc_pixbuf_stash_fetch: enqueue %s" % (sync_item,))
if const_debug_enabled():
const_debug_write(__name__,
"__new_ugc_pixbuf_stash_fetch: enqueue %s" % (sync_item,))
try:
self._ugc_load_queue.put_nowait(sync_item)
const_debug_write(__name__,
"__new_ugc_pixbuf_stash_fetch: enqueued!! %s" % (sync_item,))
self._ugc_icon_load_queue.put_nowait(sync_item)
if const_debug_enabled():
const_debug_write(__name__,
"__new_ugc_pixbuf_stash_fetch: enqueued!! %s" % (
sync_item,))
except self.queue_full_exception as err:
# argh! queue full!
const_debug_write(__name__,
"__new_ugc_pixbuf_stash_fetch: ARGH QUEUE FULL %s" % (
sync_item,))
if const_debug_enabled():
const_debug_write(__name__,
"__new_ugc_pixbuf_stash_fetch: ARGH QUEUE FULL %s" % (
sync_item,))
pass
def new_ugc_pixbuf(self, column, cell, model, myiter):