~nataliabidart/software-center/lost-in-translation

« back to all changes in this revision

Viewing changes to softwarecenter/utils.py

  • Committer: Kiwinote
  • Date: 2012-03-15 22:36:31 UTC
  • mfrom: (2867 trunk)
  • mto: This revision was merged to the branch mainline in revision 2881.
  • Revision ID: kiwinote@gmail.com-20120315223631-lvea6t5sydpkkqni
mergeĀ fromĀ trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
32
32
# py3 compat
33
33
try:
34
34
    from urllib.parse import urlsplit
35
 
    urlsplit # pyflakes
 
35
    urlsplit  # pyflakes
36
36
except ImportError:
37
37
    from urlparse import urlsplit
38
38
 
45
45
 
46
46
# define additional entities for the unescape method, needed
47
47
# because only '&', '<', and '>' are included by default
48
 
ESCAPE_ENTITIES = {"'":"'",
49
 
                   '"':'"'}
50
 
                   
 
48
ESCAPE_ENTITIES = {"'": "'",
 
49
                   '"': '"'}
 
50
 
51
51
LOG = logging.getLogger(__name__)
52
52
 
 
53
 
53
54
class UnimplementedError(Exception):
54
55
    pass
55
56
 
 
57
 
56
58
class ExecutionTime(object):
57
59
    """
58
60
    Helper that can be used in with statements to have a simple
63
65
    def __init__(self, info="", with_traceback=False):
64
66
        self.info = info
65
67
        self.with_traceback = with_traceback
 
68
 
66
69
    def __enter__(self):
67
70
        self.now = time.time()
 
71
 
68
72
    def __exit__(self, type, value, stack):
69
73
        logger = logging.getLogger("softwarecenter.performance")
70
74
        logger.debug("%s: %s" % (self.info, time.time() - self.now))
71
75
        if self.with_traceback:
72
76
            log_traceback("populate model from query: '%s' (threaded: %s)")
73
77
 
 
78
 
74
79
def utf8(s):
75
80
    """
76
81
    Takes a string or unicode object and returns a utf-8 encoded
90
95
    """
91
96
    logger = logging.getLogger("softwarecenter.traceback")
92
97
    logger.debug("%s: %s" % (info, "".join(traceback.format_stack())))
93
 
    
 
98
 
94
99
 
95
100
def wait_for_apt_cache_ready(f):
96
101
    """ decorator that ensures that self.cache is ready using a
98
103
    """
99
104
    def wrapper(*args, **kwargs):
100
105
        self = args[0]
101
 
        # check if the cache is ready and 
 
106
        # check if the cache is ready and
102
107
        window = None
103
108
        if hasattr(self, "app_view"):
104
 
            window =  self.app_view.get_window()
 
109
            window = self.app_view.get_window()
105
110
        if not self.cache.ready:
106
111
            if window:
107
112
                window.set_cursor(self.busy_cursor)
114
119
        return False
115
120
    return wrapper
116
121
 
 
122
 
117
123
def normalize_package_description(desc):
118
124
    """ this takes a package description and normalizes it
119
125
        so that all uneeded \n are stripped away and all
148
154
        # check if in a enumeration
149
155
        if part[:2] in BULLETS:
150
156
            in_blist = True
151
 
            norm_description += "\n" + indent*' ' + "* " + part[2:]
 
157
            norm_description += "\n" + indent * ' ' + "* " + part[2:]
152
158
        elif in_blist and indent > 0:
153
159
            norm_description += " " + part
154
160
        elif part.endswith('.') or part.endswith(':'):
163
169
            norm_description += part
164
170
    return norm_description.strip()
165
171
 
 
172
 
166
173
def get_title_from_html(html):
167
174
    """ takes a html string and returns the document title,
168
175
        if the document has no title it uses the first h1
169
176
        (but only if that has no further html tags)
170
 
        
 
177
 
171
178
        returns "" if it can't find anything or can't parse the html
172
179
    """
173
180
    import xml.etree.ElementTree
183
190
    all_h1 = root.findall(".//h1")
184
191
    if all_h1:
185
192
        h1 = all_h1[0]
186
 
        # we don't support any sub html in the h1 when 
 
193
        # we don't support any sub html in the h1 when
187
194
        if len(h1) == 0:
188
195
            return h1.text
189
196
    return ""
190
197
 
 
198
 
191
199
def htmlize_package_description(desc):
192
200
    html = ""
193
201
    inside_li = False
194
202
    for part in normalize_package_description(desc).split("\n"):
195
203
        stripped_part = part.strip()
196
 
        if not stripped_part: continue
 
204
        if not stripped_part:
 
205
            continue
197
206
        if stripped_part.startswith("* "):
198
207
            if not inside_li:
199
208
                html += "<ul>"
208
217
        html += "</ul>"
209
218
    return html
210
219
 
 
220
 
211
221
def get_parent_xid(widget):
212
222
    while widget.get_parent():
213
223
        widget = widget.get_parent()
230
240
    else:
231
241
        return proxy
232
242
 
 
243
 
233
244
def get_http_proxy_string_from_gsettings():
234
245
    """Helper that gets the http proxy from gsettings
235
246
 
250
261
                authentication = "%s:%s@" % (user, password)
251
262
            host = settings.get_string("host")
252
263
            port = settings.get_int("port")
253
 
            http_proxy = "http://%s%s:%s/" %  (authentication, host, port)
 
264
            http_proxy = "http://%s%s:%s/" % (authentication, host, port)
254
265
            if host:
255
266
                return http_proxy
256
267
    except Exception:
257
268
        logging.exception("failed to get proxy from gconf")
258
269
 
 
270
 
259
271
def encode_for_xml(unicode_data, encoding="ascii"):
260
272
    """ encode a given string for xml """
261
273
    return unicode_data.encode(encoding, 'xmlcharrefreplace')
262
274
 
 
275
 
263
276
def decode_xml_char_reference(s):
264
 
    """ takes a string like 
265
 
        'Search&#x2026;' 
 
277
    """ takes a string like
 
278
        'Search&#x2026;'
266
279
        and converts it to
267
280
        'Search...'
268
281
    """
269
282
    p = re.compile("\&\#x(\d\d\d\d);")
270
283
    return p.sub(r"\u\1", s).decode("unicode-escape")
271
 
    
 
284
 
 
285
 
272
286
def unescape(text):
273
287
    """
274
288
    unescapes the given text
275
289
    """
276
290
    return xml.sax.saxutils.unescape(text, ESCAPE_ENTITIES)
277
291
 
 
292
 
278
293
def uri_to_filename(uri):
279
294
    try:
280
295
        import apt_pkg
284
299
        uri = re.sub(p1, "", uri)
285
300
        return uri.replace("/", "_")
286
301
 
 
302
 
287
303
def human_readable_name_from_ppa_uri(ppa_uri):
288
304
    """ takes a PPA uri and returns a human readable name for it """
289
305
    name = urlsplit(ppa_uri).path
291
307
        return name[0:-len("/ubuntu")]
292
308
    return name
293
309
 
 
310
 
294
311
def sources_filename_from_ppa_entry(entry):
295
 
    """ 
 
312
    """
296
313
    takes a PPA SourceEntry and returns a filename suitable for sources.list.d
297
314
    """
298
315
    import apt_pkg
299
316
    name = "%s.list" % apt_pkg.URItoFileName(entry.uri)
300
317
    return name
301
 
    
 
318
 
 
319
 
302
320
def obfuscate_private_ppa_details(text):
303
321
    """
304
322
    hides any private PPA details that may be found in the given text
314
332
                result = result.replace(url_parts.password, "hidden")
315
333
    return result
316
334
 
 
335
 
317
336
def release_filename_in_lists_from_deb_line(debline):
318
337
    """
319
338
    takes a debline and returns the filename of the Release file
323
342
    entry = aptsources.sourceslist.SourceEntry(debline)
324
343
    name = "%s_dists_%s_Release" % (uri_to_filename(entry.uri), entry.dist)
325
344
    return name
326
 
    
 
345
 
 
346
 
327
347
def is_unity_running():
328
348
    """
329
349
    return True if Unity is currently running
335
355
    except:
336
356
        LOG.exception("could not check for Unity dbus service")
337
357
    return unity_running
338
 
    
339
 
def get_icon_from_theme(icons, iconname=None, iconsize=Icons.APP_ICON_SIZE, missingicon=Icons.MISSING_APP):
 
358
 
 
359
 
 
360
def get_icon_from_theme(icons, iconname=None, iconsize=Icons.APP_ICON_SIZE,
 
361
                        missingicon=Icons.MISSING_APP):
340
362
    """
341
363
    return the icon in the theme that corresponds to the given iconname
342
 
    """    
 
364
    """
343
365
    if not iconname:
344
366
        iconname = missingicon
345
367
    try:
346
368
        icon = icons.load_icon(iconname, iconsize, 0)
347
369
    except Exception as e:
348
 
        LOG.warning(utf8("could not load icon '%s', displaying missing icon instead: %s "
349
 
                        ) % (utf8(iconname), utf8(e.message)))
 
370
        LOG.warning(utf8("could not load icon '%s', displaying missing "\
 
371
                         "icon instead: %s ") % (
 
372
                utf8(iconname), utf8(e.message)))
350
373
        icon = icons.load_icon(missingicon, iconsize, 0)
351
374
    return icon
352
 
    
353
 
def get_file_path_from_iconname(icons, iconname=None, iconsize=Icons.APP_ICON_SIZE):
 
375
 
 
376
 
 
377
def get_file_path_from_iconname(icons, iconname=None,
 
378
                                iconsize=Icons.APP_ICON_SIZE):
354
379
    """
355
380
    return the file path of the icon in the theme that corresponds to the
356
381
    given iconname, or None if it cannot be determined
366
391
        icon_file_path = icon_info.get_filename()
367
392
        icon_info.free()
368
393
        return icon_file_path
369
 
        
370
 
def convert_desktop_file_to_installed_location(app_install_data_file_path, pkgname):
 
394
 
 
395
 
 
396
def convert_desktop_file_to_installed_location(app_install_data_file_path,
 
397
                                               pkgname):
371
398
    """ returns the installed desktop file path that corresponds to the
372
399
        given app-install-data file path, and will also check directly for
373
400
        the desktop file that corresponds to a given pkgname.
374
401
    """
375
402
    if app_install_data_file_path and pkgname:
376
403
        # "normal" case
377
 
        installed_desktop_file_path = app_install_data_file_path.replace("app-install/desktop/"
378
 
                                                                         + pkgname + ":",
379
 
                                                                         "applications/")
 
404
        installed_desktop_file_path = app_install_data_file_path.replace(
 
405
            "app-install/desktop/" + pkgname + ":", "applications/")
380
406
        if os.path.exists(installed_desktop_file_path):
381
 
            return installed_desktop_file_path  
 
407
            return installed_desktop_file_path
382
408
        # next, try case where a subdirectory is encoded in the app-install
383
409
        # desktop filename, e.g. kde4_soundkonverter.desktop
384
 
        installed_desktop_file_path = installed_desktop_file_path.replace(APP_INSTALL_PATH_DELIMITER, "/")
 
410
        installed_desktop_file_path = installed_desktop_file_path.replace(
 
411
            APP_INSTALL_PATH_DELIMITER, "/")
385
412
        if os.path.exists(installed_desktop_file_path):
386
413
            return installed_desktop_file_path
387
 
    # lastly, just try checking directly for the desktop file based on the pkgname itself
 
414
    # lastly, just try checking directly for the desktop file based on the
 
415
    # pkgname itself
388
416
    if pkgname:
389
 
        installed_desktop_file_path =  "/usr/share/applications/%s.desktop" % pkgname
 
417
        installed_desktop_file_path = "/usr/share/applications/%s.desktop" %\
 
418
            pkgname
390
419
        if os.path.exists(installed_desktop_file_path):
391
420
            return installed_desktop_file_path
392
 
    LOG.warn("Could not determine the installed desktop file path for app-install desktop file: '%s'" % app_install_data_file_path)
 
421
    LOG.warn("Could not determine the installed desktop file path for "
 
422
             "app-install desktop file: '%s'" % app_install_data_file_path)
393
423
    return ""
394
424
 
 
425
 
395
426
def clear_token_from_ubuntu_sso(appname):
396
 
    """ send a dbus signal to the com.ubuntu.sso service to clear 
 
427
    """ send a dbus signal to the com.ubuntu.sso service to clear
397
428
        the credentials for the given appname, e.g. _("Ubuntu Software Center")
398
429
    """
399
430
    from ubuntu_sso import (
409
440
                           dbus_interface=DBUS_CREDENTIALS_IFACE)
410
441
    proxy.clear_credentials(appname, {})
411
442
 
 
443
 
412
444
def get_nice_date_string(cur_t):
413
445
    """ return a "nice" human readable date, like "2 minutes ago"  """
414
446
    import datetime
418
450
    secs = dt.seconds
419
451
 
420
452
    if days < 1:
421
 
 
422
453
        if secs < 120:   # less than 2 minute ago
423
454
            s = _('a few minutes ago')   # dont be fussy
424
455
 
425
456
        elif secs < 3600:   # less than an hour ago
426
457
            s = gettext.ngettext("%(min)i minute ago",
427
458
                                 "%(min)i minutes ago",
428
 
                                 (secs/60)) % { 'min' : (secs/60) }
 
459
                                 (secs / 60)) % {'min': (secs / 60)}
429
460
 
430
461
        else:   # less than a day ago
431
462
            s = gettext.ngettext("%(hours)i hour ago",
432
463
                                 "%(hours)i hours ago",
433
 
                                 (secs/3600)) % { 'hours' : (secs/3600) }
434
 
 
435
 
    elif days <= 5: # less than a week ago
 
464
                                 (secs / 3600)) % {'hours': (secs / 3600)}
 
465
    elif days <= 5:  # less than a week ago
436
466
        s = gettext.ngettext("%(days)i day ago",
437
467
                             "%(days)i days ago",
438
 
                             days) % { 'days' : days }
439
 
 
 
468
                             days) % {'days': days}
440
469
    else:   # any timedelta greater than 5 days old
441
470
        # YYYY-MM-DD
442
471
        s = cur_t.isoformat().split('T')[0]
443
 
 
444
472
    return s
445
473
 
 
474
 
446
475
def _get_from_desktop_file(desktop_file, key):
447
476
    import ConfigParser
448
477
    config = ConfigParser.ConfigParser()
452
481
    except ConfigParser.NoOptionError:
453
482
        return None
454
483
 
 
484
 
455
485
def get_exec_line_from_desktop(desktop_file):
456
486
    return _get_from_desktop_file(desktop_file, "Exec")
457
487
 
 
488
 
458
489
def is_no_display_desktop_file(desktop_file):
459
 
    nd =  _get_from_desktop_file(desktop_file, "NoDisplay")
 
490
    nd = _get_from_desktop_file(desktop_file, "NoDisplay")
460
491
    # desktop spec says the booleans are always either "true" or "false
461
492
    if nd == "true":
462
493
        return True
463
494
    return False
464
495
 
 
496
 
465
497
def get_nice_size(n_bytes):
466
 
    nice_size = lambda s:[(s%1024**i and "%.1f"%(s/1024.0**i) or \
467
 
        str(s/1024**i))+x.strip() for i,x in enumerate(' KMGTPEZY') \
468
 
        if s<1024**(i+1) or i==8][0]
 
498
    nice_size = lambda s: [(s % 1024 ** i and "%.1f" % (s / 1024.0 ** i) or \
 
499
        str(s / 1024 ** i)) + x.strip() for i, x in enumerate(' KMGTPEZY') \
 
500
        if s < 1024 ** (i + 1) or i == 8][0]
469
501
    return nice_size(n_bytes)
470
 
            
 
502
 
 
503
 
471
504
def save_person_to_config(username):
472
505
    """ save the specified username value for Ubuntu SSO to the config file
473
506
    """
481
514
        config.set("reviews", "username", username)
482
515
        config.write()
483
516
        # refresh usefulness cache in the background once we know
484
 
        # the person 
 
517
        # the person
485
518
        from backend.reviews import UsefulnessCache
486
519
        UsefulnessCache(True)
487
520
    return
488
 
            
 
521
 
 
522
 
489
523
def get_person_from_config():
490
524
    """ get the username value for Ubuntu SSO from the config file
491
525
    """
494
528
        return cfg.get("reviews", "username")
495
529
    return None
496
530
 
 
531
 
497
532
def pnormaldist(qn):
498
 
    '''Inverse normal distribution, based on the Ruby statistics2.pnormaldist'''
 
533
    """
 
534
    Inverse normal distribution, based on the Ruby statistics2.pnormaldist
 
535
    """
499
536
    b = [1.570796288, 0.03706987906, -0.8364353589e-3,
500
537
         -0.2250947176e-3, 0.6841218299e-5, 0.5824238515e-5,
501
538
         -0.104527497e-5, 0.8360937017e-7, -0.3231081277e-8,
502
539
         0.3657763036e-10, 0.6936233982e-12]
503
 
        
 
540
 
504
541
    if qn < 0 or qn > 1:
505
542
        raise ValueError("qn must be between 0.0 and 1.0")
506
543
    if qn == 0.5:
507
544
        return 0.0
508
 
    
 
545
 
509
546
    w1 = qn
510
547
    if qn > 0.5:
511
548
        w1 = 1.0 - w1
512
549
    w3 = -math.log(4.0 * w1 * (1.0 - w1))
513
550
    w1 = b[0]
514
 
    for i in range (1,11):
 
551
    for i in range(1, 11):
515
552
        w1 = w1 + (b[i] * math.pow(w3, i))
516
 
        
 
553
 
517
554
    if qn > 0.5:
518
 
        return math.sqrt(w1*w3)
 
555
        return math.sqrt(w1 * w3)
519
556
    else:
520
 
        return -math.sqrt(w1*w3)
 
557
        return -math.sqrt(w1 * w3)
 
558
 
521
559
 
522
560
def wilson_score(pos, n, power=0.2):
523
561
    if n == 0:
524
562
        return 0
525
 
    z = pnormaldist(1-power/2)
 
563
    z = pnormaldist(1 - power / 2)
526
564
    phat = 1.0 * pos / n
527
 
    return (phat + z*z/(2*n) - z * math.sqrt((phat*(1-phat)+z*z/(4*n))/n))/(1+z*z/n)
 
565
    return (phat + z * z / (2 * n) - z * math.sqrt(
 
566
            (phat * (1 - phat) + z * z / (4 * n)) / n)) / (1 + z * z / n)
 
567
 
528
568
 
529
569
def calc_dr(ratings, power=0.1):
530
570
    '''Calculate the dampened rating for an app given its collective ratings'''
531
571
    if not len(ratings) == 5:
532
572
        raise AttributeError('ratings argument must be a list of 5 integers')
533
 
   
 
573
 
534
574
    tot_ratings = 0
535
 
    for i in range (0,5):
 
575
    for i in range(0, 5):
536
576
        tot_ratings = ratings[i] + tot_ratings
537
 
      
 
577
 
538
578
    sum_scores = 0.0
539
 
    for i in range (0,5):
 
579
    for i in range(0, 5):
540
580
        ws = wilson_score(ratings[i], tot_ratings, power)
541
 
        sum_scores = sum_scores + float((i+1)-3) * ws
542
 
   
 
581
        sum_scores = sum_scores + float((i + 1) - 3) * ws
543
582
    return sum_scores + 3
544
583
 
 
584
 
545
585
# we need this because some iconnames have already split off the extension
546
586
# (the desktop file standard suggests this) but still have a "." in the name.
547
587
# From other sources we get icons with a full extension so a simple splitext()
548
588
# is not good enough
549
589
def split_icon_ext(iconname):
550
 
    """ return the basename of a icon if it matches a known icon 
 
590
    """ return the basename of a icon if it matches a known icon
551
591
        extenstion like tiff, gif, jpg, svg, png, xpm, ico
552
592
    """
553
 
    SUPPORTED_EXTENSIONS = [".tiff", ".tif", ".gif", ".jpg", ".jpeg", ".svg", 
 
593
    SUPPORTED_EXTENSIONS = [".tiff", ".tif", ".gif", ".jpg", ".jpeg", ".svg",
554
594
                            ".png", ".xpm", ".ico"]
555
595
    basename, ext = os.path.splitext(iconname)
556
596
    if ext.lower() in SUPPORTED_EXTENSIONS:
563
603
    # we are running in a local checkout, make life as easy as possible
564
604
    # for this
565
605
    if os.path.exists("./data/ui/gtk3/SoftwareCenter.ui"):
566
 
        logging.getLogger("softwarecenter").info("Using data (UI, xapian) from current dir")
 
606
        logging.getLogger("softwarecenter").info(
 
607
            "Using data (UI, xapian) from current dir")
567
608
        # set pythonpath for the various helpers
568
 
        if os.environ.get("PYTHONPATH",""):
569
 
            os.environ["PYTHONPATH"]=os.path.abspath(".") + ":" + os.environ.get("PYTHONPATH","")
 
609
        if os.environ.get("PYTHONPATH", ""):
 
610
            os.environ["PYTHONPATH"] = os.path.abspath(".") + ":" +\
 
611
                os.environ.get("PYTHONPATH", "")
570
612
        else:
571
 
            os.environ["PYTHONPATH"]=os.path.abspath(".")
 
613
            os.environ["PYTHONPATH"] = os.path.abspath(".")
572
614
        datadir = "./data"
573
615
        xapian_base_path = datadir
574
616
        # set new global datadir
575
617
        softwarecenter.paths.datadir = datadir
576
618
        # also alter the app-install path
577
 
        path =  "%s/desktop/software-center.menu" % softwarecenter.paths.APP_INSTALL_PATH
 
619
        path = "%s/desktop/software-center.menu" % \
 
620
            softwarecenter.paths.APP_INSTALL_PATH
578
621
        if not os.path.exists(path):
579
622
            softwarecenter.paths.APP_INSTALL_PATH = './build/share/app-install'
580
 
            logging.warn("using local APP_INSTALL_PATH: %s" % softwarecenter.paths.APP_INSTALL_PATH)
 
623
            logging.warn("using local APP_INSTALL_PATH: %s" %\
 
624
                             softwarecenter.paths.APP_INSTALL_PATH)
581
625
    else:
582
626
        datadir = softwarecenter.paths.datadir
583
627
        xapian_base_path = softwarecenter.paths.XAPIAN_BASE_PATH
584
628
    return (datadir, xapian_base_path)
585
 
    
 
629
 
 
630
 
586
631
def get_uuid():
587
632
    import uuid
588
633
    return str(uuid.uuid4())
593
638
    LOG = logging.getLogger("softwarecenter.simplefiledownloader")
594
639
 
595
640
    __gsignals__ = {
596
 
        "file-url-reachable"      : (GObject.SIGNAL_RUN_LAST,
597
 
                                     GObject.TYPE_NONE,
598
 
                                     (bool,),),
599
 
 
600
 
        "file-download-complete"  : (GObject.SIGNAL_RUN_LAST,
601
 
                                     GObject.TYPE_NONE,
602
 
                                     (str,),),
603
 
 
604
 
        "error"                   : (GObject.SIGNAL_RUN_LAST,
605
 
                                     GObject.TYPE_NONE,
606
 
                                     (GObject.TYPE_PYOBJECT,
607
 
                                      GObject.TYPE_PYOBJECT,),),
 
641
        "file-url-reachable": (GObject.SIGNAL_RUN_LAST,
 
642
                               GObject.TYPE_NONE,
 
643
                               (bool,),),
 
644
 
 
645
        "file-download-complete": (GObject.SIGNAL_RUN_LAST,
 
646
                                   GObject.TYPE_NONE,
 
647
                                   (str,),),
 
648
 
 
649
        "error": (GObject.SIGNAL_RUN_LAST,
 
650
                  GObject.TYPE_NONE,
 
651
                  (GObject.TYPE_PYOBJECT,
 
652
                   GObject.TYPE_PYOBJECT,),),
608
653
        }
609
654
 
610
655
    def __init__(self):
614
659
 
615
660
    def download_file(self, url, dest_file_path=None, use_cache=False,
616
661
                      simple_quoting_for_webkit=False):
617
 
        """ Download a url and emit the file-download-complete 
 
662
        """ Download a url and emit the file-download-complete
618
663
            once the file is there. Note that calling this twice
619
664
            will cancel the previous pending operation.
620
665
            If dest_file_path is given, download to that specific
669
714
 
670
715
        f = Gio.File.new_for_uri(url)
671
716
        # first check if the url is reachable
672
 
        f.query_info_async(Gio.FILE_ATTRIBUTE_STANDARD_SIZE, 0, 0, 
 
717
        f.query_info_async(Gio.FILE_ATTRIBUTE_STANDARD_SIZE, 0, 0,
673
718
                           self._cancellable,
674
719
                           self._check_url_reachable_and_then_download_cb,
675
720
                           None)
676
 
                           
677
 
    def _check_url_reachable_and_then_download_cb(self, f, result, user_data=None):
 
721
 
 
722
    def _check_url_reachable_and_then_download_cb(self, f, result,
 
723
                                                  user_data=None):
678
724
        self.LOG.debug("_check_url_reachable_and_then_download_cb: %s" % f)
679
725
        try:
680
726
            info = f.query_info_finish(result)
681
727
            etag = info.get_etag()
682
728
            self.emit('file-url-reachable', True)
683
729
            self.LOG.debug("file reachable %s %s %s" % (self.url,
684
 
                                                        info, 
 
730
                                                        info,
685
731
                                                        etag))
686
732
            # url is reachable, now download the file
687
733
            f.load_contents_async(
694
740
 
695
741
    def _file_download_complete_cb(self, f, result, path=None):
696
742
        self.LOG.debug("file download completed %s" % self.dest_file_path)
697
 
        # The result from the download is actually a tuple with three 
 
743
        # The result from the download is actually a tuple with three
698
744
        # elements (content, size, etag?)
699
745
        # The first element is the actual content so let's grab that
700
746
        try:
740
786
from softwarecenter.db.pkginfo import get_pkg_info
741
787
# do not call here get_pkg_info, since package switch may not have been set
742
788
# instead use an anonymous function delay
743
 
upstream_version_compare = lambda v1, v2: get_pkg_info().upstream_version_compare(v1, v2)
 
789
upstream_version_compare = lambda v1, v2: \
 
790
    get_pkg_info().upstream_version_compare(v1, v2)
744
791
upstream_version = lambda v: get_pkg_info().upstream_version(v)
745
792
version_compare = lambda v1, v2: get_pkg_info().version_compare(v1, v2)
746
793