[Yum-devel] [PATCH] No implicit use of TextMultiFileMeter

James Antill james at fedoraproject.org
Mon Aug 20 16:08:03 UTC 2012


On Mon, 2012-08-20 at 16:46 +0200, Zdeněk Pavlas wrote:
> ---
>  urlgrabber/grabber.py |   82 ++++++++++++++++++++++++++++++++-----------------
>  1 files changed, 54 insertions(+), 28 deletions(-)
> 
> diff --git a/urlgrabber/grabber.py b/urlgrabber/grabber.py
> index b3071ce..b16b9a9 100644
> --- a/urlgrabber/grabber.py
> +++ b/urlgrabber/grabber.py
> @@ -54,6 +54,21 @@ GENERAL ARGUMENTS (kwargs)
>        po.update(read) # read == bytes read so far
>        po.end()
>  
> +  multi_obj = None

 Can we call this multi_progress_obj instead?

> +    a class instance that supports the following methods:
> +      mo.start(total_files, total_size)
> +      mo.newMeter() => meter
> +      mo.removeMeter(meter)
> +      mo.end()
> +
> +   The 'meter' object is similiar to progress_obj, but multiple
> +   instances may be created and updated at the same time.
> +
> +   When downloading multiple files in parallel and multi_obj is None
> +   progress_obj is used in compatibility mode: finished files are
> +   shown but there's no in-progress display.
> +
>    text = None
>    
>      specifies alternative text to be passed to the progress meter
> @@ -909,6 +924,7 @@ class URLGrabberOptions:
>          provided here.
>          """
>          self.progress_obj = None
> +        self.multi_obj = None
>          self.throttle = 1.0
>          self.bandwidth = 0
>          self.retry = None
> @@ -2028,7 +2044,7 @@ class _ExternalDownloader:
>              v = getattr(opts, k)
>              if v is None: continue
>              arg.append('%s=%s' % (k, _dumps(v)))
> -        if opts.progress_obj:
> +        if opts.progress_obj and opts.multi_obj:
>              arg.append('progress_obj=True')
>          arg = ' '.join(arg)
>          if DEBUG: DEBUG.info('attempt %i/%s: %s', opts.tries, opts.retry, opts.url)
> @@ -2048,7 +2064,7 @@ class _ExternalDownloader:
>              line = line.split(' ', 5)
>              _id, size = map(int, line[:2])
>              if len(line) == 2:
> -                self.running[_id].progress_obj.update(size)
> +                self.running[_id].progress.update(size)

 See below.

>                  continue
>              # job done
>              opts = self.running.pop(_id)
> @@ -2117,20 +2133,31 @@ class _ExternalDownloaderPool:
>  
>  _async_queue = []
>  
> -def parallel_wait(meter = 'text'):
> +def parallel_wait():

 Any caller that does parallel_wait('text') or
parallel_wait(meter='text') gets a traceback after this change.


> +class CompatMeter:
> +    """ Compat wrapper on top of a single-file progress meter 'obj'.
> +    """
> +    def __init__(self, obj):
> +        self.obj = obj
> +    def start(self, text):
> +        self.text = text
> +        self.started = time.time()
> +    def end(self, amount):
> +        self.obj.start(text=self.text, now=self.started)
> +        self.obj.update(amount)
> +        self.obj.end(amount)
> +    def update(self, amount): pass
> + 

 This should be in the progress module, and are you sure you want it to
be public with that name? Maybe Single2MultiWrapperMeter()?
 Or maybe don't use a wrapper, and have functions somewhere?

>      '''Process queued requests in parallel.
>      '''
>  
> -    if meter:
> -        count = total = 0
> -        for opts in _async_queue:
> -            if opts.progress_obj:
> -                count += 1
> -                total += opts.size
> -        if meter == 'text':
> -            from progress import TextMultiFileMeter
> -            meter = TextMultiFileMeter()
> -        meter.start(count, total)
> +    meters = {}
> +    for opts in _async_queue:
> +        if opts.progress_obj and opts.multi_obj:
> +            count, total = meters.get(opts.multi_obj) or (0, 0)
> +            meters[opts.multi_obj] = count + 1, total + opts.size
> +    for meter in meters:
> +        meter.start(*meters[meter])
>  
>      dl = _ExternalDownloaderPool()
>      host_con = {} # current host connection counts
> @@ -2139,11 +2166,10 @@ def parallel_wait(meter = 'text'):
>          key, limit = opts.async
>          host_con[key] = host_con.get(key, 0) + 1
>          opts.tries = tries
> -        if meter and opts.progress_obj:
> -            opts.progress_obj = meter.newMeter()
> -            opts.progress_obj.start(text=opts.text, basename=os.path.basename(opts.filename))
> -        else:
> -            opts.progress_obj = None
> +        if opts.progress_obj:
> +            m = opts.multi_obj
> +            opts.progress = m and m.newMeter() or CompatMeter(opts.progress_obj)

 Again with the magic lines of doing N things? If statements do work.
 Also if we want to wrap this are you sure you want self.progress to be
public?
 On the other side it doesn't seem that bad to have a function/if at the
2-3 places self.progress is being called.

>          if DEBUG: DEBUG.info('attempt %i/%s: %s', opts.tries, opts.retry, opts.url)
>          dl.start(opts)
>  
> @@ -2151,15 +2177,14 @@ def parallel_wait(meter = 'text'):
>          for opts, size, ug_err in dl.perform():
>              key, limit = opts.async
>              host_con[key] -= 1
> -            m = opts.progress_obj
> -            if m:
> -                if ug_err:
> -                    m.failure(ug_err.args[1])
> -                else:
> -                    # file size might have changed
> -                    meter.re.total += size - opts.size
> -                    m.end(size)
> -                meter.removeMeter(m)
> +            if opts.progress_obj:
> +                if not ug_err:
> +                    if opts.multi_obj:
> +                        # file size might have changed
> +                        opts.multi_obj.re.total += size - opts.size
> +                    opts.progress.end(size)
> +                if opts.multi_obj:
> +                    opts.multi_obj.removeMeter(opts.progress)
>  
>              if ug_err is None:
>                  if opts.checkfunc:



More information about the Yum-devel mailing list