ValueError: Value out of range mit progressbar-Modul

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

Hallo Leute,

enn ich den nachfolgenden, ausführbaren Quelltext ausführe, dann ende ich zum Schluss bei der Fehlermeldung ValueError: Value out of range (Am Ende dieses Beitrags siehst du den vollen Traceback). Die Datei wird wie gewöhnlich und erfolgreich zum FTP-Server hochgeladen. Jedoch bricht die Progressbar bei 100 Prozent ab Sie kommt nur bis 99 % und dann folgt die Meldung. Hat jemand eine Ahnung, woran es liegen könnte?

Code: Alles auswählen

from ftplib import FTP_TLS
from os import path
from sys import exc_info
import socket
from progressbar import ProgressBar, Bar, Percentage
from traceback import format_exc

class FTP_Client(object):

    def __init__(self,
                 ftp_url=None,
                 ftp_port=None,
                 ftp_user_name=None,
                 ftp_user_pwd=None,
                 ftp_operation=None,
                 file_name=None):
        
        self.uploaded_byte= 0
        self.totalSize = 0
        self.lastShownPercent = 0
        self.pbar = None

        self.ftp_url = ftp_url
        self.ftp_port = ftp_port
        self.ftp_operation = ftp_operation

        self.ftp_user_name = ftp_user_name
        self.ftp_user_pwd = ftp_user_pwd
                 
        self.file_name= file_name

        self.totalSize = self.get_total_file_size(file_path=self.file_name)

        '''
            # Open the file for reading in binary mode
        '''
        self.opened_file = open(self.file_name, 'rb')

        self.open_ftp_connect(url_ftp=self.ftp_url,
                              port_ftp=self.ftp_port)

    def get_total_file_size(self, file_path=None):

        return float(path.getsize(file_name))

    def open_ftp_connect(self,
                         url_ftp=None,
                         port_ftp=None):

        
        # Open FTP connection, and set the session as the variable client
        client = FTP_TLS(timeout=100)
        client.connect(url_ftp, port_ftp)

        # enable TLS
        client.auth()
        client.prot_p() # switch to secure data connection
                        # securing data connection explicitly


        self.login_ftp(ftp_connect_obj=client,
                           ftp_user_name=self.ftp_user_name,
                           ftp_user_password=self.ftp_user_pwd)

    


    def login_ftp(self,
                  ftp_connect_obj=None,
                  ftp_user_name=None,
                  ftp_user_password=None):
                        
        ftp_connect_obj.login(ftp_user_name,ftp_user_password)
        
        print "TEXT", ftp_connect_obj.getwelcome()
        print "PWD", ftp_connect_obj.sendcmd('PWD')

        self.change_directiory(ftp_connect_obj=ftp_connect_obj,
                               directory='/python')

    def change_directiory(self,
                          ftp_connect_obj=None,
                          directory=None):

        ftp_connect_obj.cwd(directory) # change working directory to the root

        try:

            ftp_operation_func_dict = {'d': self.download_file,
                                       'u': self.upload_file}

            ftp_operation_func_dict[self.ftp_operation](ftp_connect_obj=ftp_connect_obj)

            print "File transfered"

        except KeyError:
            print "The function doesn't exist! Try again!"
            
        except Exception as a_err:
            print "Connection lost. Error in File transfering: ", a_err

            desired_trace = format_exc(exc_info())
            print "desired_trace", desired_trace

    def download_file(self,
                    ftp_connect_obj=None):
        pass
        

    def upload_file(self,
                    ftp_connect_obj=None):

        '''
            NOTICE:
            =======
            Here we upload the given file a chunk at a time
            Each chunk is sent to handle_progress
            We append the chunk to the file.
            STOR is an FTP command
        '''

        file_size = self.totalSize

        result = file_size / (1024*5)
        
        chunk_size = int(result)
        
        uploaded_bytes = 0

        destination = 'tester.rar'

        file_handle = 0

        if self.pbar is None:
            self.pbar = ProgressBar(maxval=self.totalSize)

        self.pbar.start()

        ## Open a file to upload
        with open(self.file_name, 'rb') as fd:

            file_handle = fd
        
            ftp_connect_obj.storbinary('STOR %s' % destination, file_handle, blocksize=chunk_size, callback=lambda conn_obj=ftp_connect_obj:
                                       self.handle_update_file_transfer_progress(chunk=chunk_size))



        self.pbar.finish()
        self.pbar = None
        
        self.close_quite_ftp_connect(ftp_connect_obj=ftp_connect_obj)

    def handle_update_file_transfer_progress(self, chunk=None):
        '''
            NOTICE:
            ======
            This method is defined to show the percentage uploaded
        '''
        self.uploaded_byte += chunk

        self.pbar.update(self.uploaded_byte)

    def close_quite_ftp_connect(self, ftp_connect_obj=None):
        # Close the connection and the file
        print ftp_connect_obj.quit()
        print ftp_connect_obj.close()

if __name__ == "__main__":

    ftp_url = raw_input('Enter URL of FTP without // at the begin: ')
    ftp_port = raw_input('Enter FTP-Port: ')
    ftp_user_name = raw_input('Enter Username: ')
    ftp_user_pwd = raw_input('Enter Password: ')
    choice = raw_input("(U)pload or (D)ownload?: ")
    file_name = raw_input('Enter file path with file name: ')

    try:

        uploadTracker = FTP_Client(ftp_url=ftp_url,
                                         ftp_port=ftp_port,
                                         ftp_user_name=ftp_user_name,
                                         ftp_user_pwd=ftp_user_pwd,
                                         ftp_operation=choice.lower(),
                                         file_name=file_name)

    except socket.gaierror:
        # When your computer can't resolve domain name, you're gave to him.
        print "Can't open the connection"

    except WindowsError:
        print "ERROR: There isn't a file to open!"

Connection lost. Error in File transfering: Value out of range
desired_trace Traceback (most recent call last):
File "C:\Users\Sophus\Desktop\py_scripts\ftp_secure.py", line 92, in change_directiory
ftp_operation_func_dict[self.ftp_operation](ftp_connect_obj=ftp_connect_obj)
File "C:\Users\Sophus\Desktop\py_scripts\ftp_secure.py", line 157, in upload_file
ftp_connect_obj.storbinary('STOR %s' % destination, file_handle, blocksize=chunk_size, callback=lambda conn_obj=ftp_connect_obj:
File "C:\Python27\lib\ftplib.py", line 749, in storbinary
if callback: callback(buf)
File "C:\Users\Sophus\Desktop\py_scripts\ftp_secure.py", line 158, in <lambda>
self.handle_update_file_transfer_progress(chunk=chunk_size))
File "C:\Users\Sophus\Desktop\py_scripts\ftp_secure.py", line 175, in handle_update_file_transfer_progress
self.pbar.update(self.uploaded_byte)
File "build\bdist.win32\egg\progressbar\__init__.py", line 271, in update
raise ValueError('Value out of range')
ValueError: Value out of range
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: wenn Du einen Traceback zeigst, sollte der auch zum gezeigen Code passen. Sonst ist es schwierig, den Fehler nachzuvollziehen. Warum öffnest Du eine Datei, ohne was damit zu machen? Warum ist die Dateigröße ein float? Das das ganze eine lange Kette von Funktionsaufrufen ist, macht die Sache nicht übersichtlicher. Zum Problem: Du addierst chunk_size unabhängig von der tatsächlichen Blockgröße aufeinander, was beim letzten Block natürlich zu einer zu großen Zahl führt.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@Sirius3: Das mit der chunk_size-Größe ist für mich in der Tat ein Problem. In kann Zeile 144-145

Code: Alles auswählen

            ftp_connect_obj.storbinary('STOR %s' % destination, file_handle, blocksize=chunk_size, callback=lambda conn_obj=ftp_connect_obj:
                                       self.handle_update_file_transfer_progress(chunk=chunk_size))
unmöglich in eine Schleife stecken?

Mein Pseudo-Quelltext sähe dann in Etwas so aus:

Code: Alles auswählen

[...]
def read_in_chunks(file_object, chunk_size=1024):
    """Lazy function (generator) to read a file piece by piece.
    Default chunk size: 1k."""
    while True:
        data = file_object.read(chunk_size)
        if not data:
            break
        yield data

def upload_file(self, ftp_connect_obj=None):
     with open(self.file_name, 'rb') as fd:
           for chunk in read_in_chunks(fd):
                 fd.read(chunk)
                   uploaded_bytes = fd.tell()

                    ftp_connect_obj.storbinary('STOR %s' % destination, file_handle, blocksize=chunk_size, callback=lambda:
                                       self.handle_update_file_transfer_progress(chunk=uploaded_bytes ))

 def handle_update_file_transfer_progress(self, chunk=None):
    self.uploaded_byte += chunk
 
      self.pbar.update(self.uploaded_byte)
[...]
Aber ich denke, mit diesem Denkansatz liege ich auch gänzlich falsch?
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ich würde die Logik der ProgressBar ja als eigene Klasse auslagern, um den Überblick zu behalten:

Code: Alles auswählen

import os
import time

from progressbar import ProgressBar

class StreamReader(object):
    def __init__(self, stream, total=None):
        if total is None:
            total = os.path.getsize(stream.name)
        self.stream = stream
        self.pbar = ProgressBar(maxval=total)
        self.chars_read = 0

    def read(self, size=None):
        result = self.stream.read(size)
        self.chars_read += len(result)
        self.pbar.update(self.chars_read)
        return result

    def close(self):
        self.stream.close()
        self.pbar.finish()


class PseudoStream(object):
    def __init__(self, source=os.urandom, default_size=1024):
        self.source = source
        self.default_size = default_size

    def read(self, size=None):
        if size is None:
            size = self.default_size
        # Simulate a long reading operation
        time.sleep(1)
        return self.source(size)

    def close(self):
        pass


def main():
    reader = StreamReader(PseudoStream(), total=10*1024)
    for _ in range(10):
        # Just to test the progress bar
        result = reader.read()
    reader.close()


if __name__ == '__main__':
    main()
Funktioniert bei mir wie erwartet und stürzt am Ende auch nicht ab.
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Und noch ein Tipp: Namen sollten eine passende Beschreibung geben. Sie sollten aber auch möglichst kurz und prägnant sein. Deine handle_update_file_transfer_progress()-Methode erfüllt dies nicht unbedingt... ;)
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@Sophus: ja, damit liegst Du sowas von daneben. Nicht einmal die Grundfunktionalität wird damit erreicht. Du solltest einfach mal nachlesen, was die Parameter bedeuten, die Du da für Deinen FTP-Upload benutzt. Du solltest auch dringend lernen, wie man Klassen schreibt. Das was Du da benutzt ist eine wilde Mischung aus Methoden, die keine sind, Attribute die mal auch nur als lokale Variablen benutzt werden und Parameter, die eigentlich Attribute sein sollten. Auch solltest Du nochmal nachschauen, wie Du chunk_size berechnest. Warum wird file_handle auf 0 gesetzt? Warum fd in file_handle umbenannt?

Das ganze also ohne diesen Klasse-Quatsch:

Code: Alles auswählen

def upload_file(ftp_connect_obj, filename, destination='tester.rar'):
    file_size = os.path.getsize(filename)
    pbar = ProgressBar(max_value=file_size)
    pbar.start()
    
    def callback(block):
        position = min(pbar.value + len(block), pbar.max_value)
        pbar.update(position)
 
    with open(filename, 'rb') as file_handle:
        ftp_connect_obj.storbinary('STOR %s' % destination, file_handle, callback=callback)
    pbar.finish()
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@snafu: Vielen Dank für deinen Tip. Ich gebe gerne zu, dass der Name einer Methode (hier: handle_update_file_transfer_progress) ziemlich lang ausgefallen ist. Mich hat es auch ein wenig "zurückgeschreckt". Aber deine angeführte Bedingung, der Name solle möglichst kurz, prägnant und passend beschrieben werden, halte ich eher für eine sogenannte Richtlinie. Verstehe mich bitte nicht falsch. Ich versuche mich (im eigenen Projekt, nicht hier im Forum) daran zu halten. Aber wir kennen das alle, dass der Name nicht immer alles erfüllen kann. Also macht man auch gerne Abstriche. Ich für meinen Teil habe also bei der Kürze und Prägnanz Abstriche gemacht, Hauptsache, der Name dieser Methode sagt schon einmal was aus, und zwar, dass der Transfer-Prozess einer Datei immer aktualisiert werden soll.

@Sirius3: Ich weiß die Kritik und Anregungen der Regulars, und die deiner, sehr zu schätzen. Allerdings habe ich immer das Gefühl, das du deine Kritik gerne dramatisch aufziehst. Da macht man eine Sache falsch oder nicht ganz korrekt, schon klingt es bei dir - zumindest lese ich deine Texte so - als solle ich gleich von vorne anfangen oder gar die Finger davon lassen. Damit ich nicht unhöflich erscheine, möchte ich direkt an zwei Beispielen anknüpfen. Zunächst ist mir aufgefallen, dass du mir gerne gewisse Dinge unterstellst - zum Beispiel die Unwissenheit, wenn du sagst
Sirius3 hat geschrieben: Du solltest einfach mal nachlesen, was die Parameter bedeuten, die Du da für Deinen FTP-Upload benutzt.
Wie du ja siehst, habe ich beim FTP-Upload in der storbinary()-Funktion mit Schlüsselargumenten (hier: blocksize und callback) gearbeitet. Ohne diese Schlüsselargumente hättest du vielleicht behaupten können, ich hätte blind und rein willkürlich gehandelt und die Argumente in Form von Positionsargumente hingestreut. So nach dem Motto, ich habe hier einen Sack voller Argumente, und die streue ich wild und unüberlegt herum - wird schon irgendwie passen. Aber dass ich Schlüsselargumente benutze, zeugt doch davon, dass ich im Ansatz weiß, wofür ich hier was verwende. Oder etwa nicht? Und als nächstes trommelst du mit deiner Kritik-Keule auf mich ein, und lässt mich dann zurück. Um es vorsichtig auszudrücken: vieles wirkt auf mich (ich formuliere mit Absicht subjektiv) sehr aufbauschend. Dieser Eindruck wird in mir dadurch verstärkt, indem du zwar mit der Kritik-Keule um dich schleuderst, aber nie beispielhaft und punktuell zeigst, woran ich konkret diesmal gescheitert bin. Das vermisse ich jetzt und gerade zum Beispiel.
Sirius3 hat geschrieben: Du solltest auch dringend lernen, wie man Klassen schreibt.
Ok, dann fangen wir mal an. Ich höre zu.
Sirius3 hat geschrieben: [...]wilde Mischung aus Methoden, die keine sind[...]
Damit es nicht zu wild wird, frage ich dich ganz ruhig, ob du mir mindestens an einem beispiel zeigen kannst, was genau du damit meinst? Wenn es mehr als 158468435154 Fehler sind, reicht mir natürlich ein Beispiel.
Sirius3 hat geschrieben: [...]Attribute die mal auch nur als lokale Variablen benutzt werden[...]
Konkretes Beispiel?
Sirius3 hat geschrieben: und Parameter, die eigentlich Attribute sein sollten.[...]
Auch hier fehlt mir ein konkretes Beispiel.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Konkretes Beispiel fuer eine Methode, die keine ist:

Code: Alles auswählen

def get_total_file_size(self, file_path=None):
        return float(path.getsize(file_name))
        
Denn sie bezieht sich nicht auf "self", koennte genauso gut also auch frei stehend sein. Fehlehaft ist sie obendrein, weil file_name nicht existiert, und du dich auf file_path gar nicht beziehst.

Konkretes Beispiel fuer ein Attribut, das nur eine lokale Variable sein sollte: self.pbar. Wird zu Beginn von upload_file angelegt, und zum Ende auf None gesetzt. Koennte auch lokale Variable sein, weil dein Lambda das eh captured, und du das dann mit partial oder dem lambda (hatten wir ja die Tage...) an handle_update_file_transfer_progress uebergeben kannst. Statt es auf der Instanz deines Objektes anzulegen.

Aehnlich komisch:

Code: Alles auswählen

self.open_ftp_connect(url_ftp=self.ftp_url,
                              port_ftp=self.ftp_port)
Entweder brauchst du self.ftp_url auch spaeter mal, dann sollte open_ftp_connect das auch so benutzen und nicht als Parameter, oder du brauchst es nicht - dann speichere es nicht auf der Instanz, sondern uebergib es nur an open_ftp_connect.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@__deets__: Vielen Dank. Deine Erläuterung war sehr anschaulich und lehrreich. Allerdings möchte ich mich für die open_ftp_connect() rechtfertigen - zumindest versuchen. Wie du ja siehst, wurde diese besagte Methode unter der __init__-Methode platziert. Dadurch möchte ich provozieren, dass open_ftp_connect() im Zuge der Erzeugung der Klasse als erstes aufgerufen wird. Auch sehen wir, dass ich der Klasse ein paar Informationen übergebe, und diese dann als Klassenattribute speichere, damit man auch im späteren Verlauf damit arbeiten kann. Wenn ich also open_ftp_connect() gleich direkt in __init__ aufrufe, dann sehe ich mich unweigerlich dazu gezwungen, mich an Klassenattribute zu vergreifen, und diese dann als Parameter zu übergeben. Ich wollte damit vermeiden, dass die Methoden im Objekt nicht wild herumfuchteln, indem beliebig auf Attribute zugegriffen wird. Ich wollte schon, dass die Methoden damit arbeiten, dass man denen auch übergibt - weitestgehend natürlich.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das du die da platzierst erzwingt oder suggeriert nichts. Und ich lese in deiner Erklärung nichts, dass die Wahl, Attribute die du schon hast, noch mal als Parameter zu übergeben, rechtfertigt.schlimmer noch, du mischst ja dein Vorgehen. Du rufst nacheinander Methoden auf, die sowohl Parameter nehmen die sie auch über self nachschlagen könnten, als auch ihrerseits genau das mit ANDEREN Paramatern an die nächste ebne tun. Das ist schon einfach konfus.
Benutzeravatar
Sophus
User
Beiträge: 1109
Registriert: Freitag 25. April 2014, 12:46
Wohnort: Osnabrück

@__deets__: Es war ja auch nur eine versuchte Rechtfertigung :) Das nächste mal greift die Methode eben auf die Attribute zu :)
Antworten