web-dev-qa-db-fra.com

Impossible de charger des fichiers à l'aide de cornichons et de plusieurs modules

J'essaie de créer un système utilisateur, qui utilise un paramètre et un module Gui, et lorsque le module GUI demande que le fichier se charge à l'aide de cornichon, je continue à recevoir une erreur d'attribut. cela provient du module des paramètres:

import pickle
import hashlib

class User(object):
    def __init__(self, fname, lname, dob, gender):
        self.firstname = fname
        self.lastname = lname
        self._dob = dob
        self.gender = gender
        self.type = 'General'
        self._username = ''
        self._hashkey = ''

    def Report(self):
        print("Full Name: {0} {1}\nDate of Birth: {2}\nGender: {3}\nAccess Level: {4}".format(self.firstname,self.lastname, self._dob, self.gender, self.type))
        print(self._username)

    def Genusername(self):
        self._username = str(str(self._dob)[:2] + self.firstname[:2] + self.lastname[:2])
        saveUsers(users)

    def Genhashkey(self, password):
        encoded = password.encode('utf-8','strict')
        return hashlib.sha256(encoded).hexdigest()

    def Verifypassword(self, password):
        if self._hashkey == self.Genhashkey(password):
            return True
        else:
            return False

class SAdmin(User):
    def __init__(self, fname, lname, dob, gender):
        super().__init__(fname, lname, dob, gender)
        self.type = 'Stock Admin'

class Manager(User):
    def __init__(self, fname, lname, dob, gender):
        super().__init__(fname, lname, dob, gender)
        self.type = 'Manager'

def saveUsers(users):
    with open('user_data.pkl', 'wb') as file:
        pickle.dump(users, file, -1) # PICKLE HIGHEST LEVEL PROTOCOL

def loadUsers(users):
    try:        
        with open('user_data.pkl', 'rb') as file:
            temp = pickle.load(file)
            for item in temp:
                users.append(item)
    except IOError:
        saveUsers([])

def userReport(users):
    for user in users:
        print(user.firstname, user.lastname)

def addUser(users):
    fname = input('What is your First Name?\n > ')
    lname = input('What is your Last Name?\n > ')
    dob = int(input('Please enter your date of birth in the following format, example 12211996\n> '))
    gender = input("What is your gender? 'M' or 'F'\n >")
    level = input("Enter the access level given to this user 'G', 'A', 'M'\n > ")
    password = input("Enter a password:\n > ")
    if level == 'G':
        usertype = User
    if level == 'A':
        usertype = SAdmin
    if level == 'M':
        usertype = Manager
    users.append(usertype(fname, lname, dob, gender))
    user = users[len(users)-1]
    user.Genusername()
    user._hashkey = user.Genhashkey(password)
    saveUsers(users)

def deleteUser(users):
    userReport(users)
    delete = input('Please type in the First Name of the user do you wish to delete:\n > ')
    for user in users:
        if user.firstname == delete:
            users.remove(user)
    saveUsers(users)

def changePass(users):
    userReport(users)
    change = input('Please type in the First Name of the user you wish to change the password for :\n > ')
    for user in users:
        if user.firstname == change:
            oldpass = input('Please type in your old password:\n > ')
            newpass = input('Please type in your new password:\n > ')
            if user.Verifypassword(oldpass):
                user._hashkey = user.Genhashkey(newpass)
                saveUsers(users)
            else:
                print('Your old password does not match!')

def verifyUser(username, password):
    for user in users:
        if user._username == username and user.Verifypassword(password):
            return True
        else:
            return False  

if __name__ == '__main__':
    users = []
    loadUsers(users)

et voici le module GUI:

from PyQt4 import QtGui, QtCore
import Settings

class loginWindow(QtGui.QDialog):    
    def __init__(self):
        super().__init__()        
        self.initUI()

    def initUI(self):
        self.lbl1 = QtGui.QLabel('Username')
        self.lbl2 = QtGui.QLabel('Password')
        self.username = QtGui.QLineEdit()
        self.password = QtGui.QLineEdit()

        self.okButton = QtGui.QPushButton("OK")
        self.okButton.clicked.connect(self.tryLogin)
        self.cancelButton = QtGui.QPushButton("Cancel")

        grid = QtGui.QGridLayout()
        grid.setSpacing(10)

        grid.addWidget(self.lbl1, 1, 0)
        grid.addWidget(self.username, 1, 1)
        grid.addWidget(self.lbl2, 2, 0)
        grid.addWidget(self.password, 2, 1)
        grid.addWidget(self.okButton, 3, 1)
        grid.addWidget(self.cancelButton, 3, 0)

        self.setLayout(grid)

        self.setGeometry(300, 300, 2950, 150)
        self.setWindowTitle('Login')
        self.show()

    def tryLogin(self):
        print(self.username.text(), self.password.text())
        if Settings.verifyUser(self.username.text(),self.password.text()):
            print('it Woks')
        else:
            QtGui.QMessageBox.warning(
                self, 'Error', 'Incorrect Username or Password')

class Window(QtGui.QMainWindow):
    def __init__(self):
        super().__init__()        


if __name__ == '__main__':

    app = QtGui.QApplication(sys.argv)
    users = []
    Settings.loadUsers(users)
    if loginWindow().exec_() == QtGui.QDialog.Accepted:
        window = Window()
        window.show()
        sys.exit(app.exec_())

chaque utilisateur est une classe et est mis dans une liste, puis la liste est enregistrée à l'aide de cornichon lorsque je charge juste le fichier de paramètres et vérifie la connexion, tout fonctionne correctement, mais lorsque j'ouvre le module GUI et essaie de vérifier qu'il ne fonctionne pas laissez-moi, l'erreur que je reçois:

Traceback (most recent call last):
  File "C:\Users`Program\LoginGUI.py", line 53, in <module>
    Settings.loadUsers(users)
  File "C:\Users\Program\Settings.py", line 51, in loadUsers
    temp = pickle.load(file)
AttributeError: Can't get attribute 'Manager' on <module '__main__' (built-in)>
30
Inthuson

Le problème est que vous décapez les objets définis dans les paramètres en exécutant en fait le module 'Paramètres' , alors vous essayez de décoller les objets du GUI module.

Rappelez-vous que le cornichon ne stocke pas réellement les informations sur la façon dont une classe/un objet est construit, et a besoin d'accéder à la classe lors du débrochage. Voir wiki sur l'utilisation de Pickle pour plus de détails.

Dans les données pkl, vous voyez que l'objet référencé est __main__.Manager, car le module "Paramètres" était principal lorsque vous avez créé le fichier pickle (c'est-à-dire que vous avez exécuté le module "Paramètres" comme script principal à invoquer) la fonction addUser).

Ensuite, vous essayez de décompresser dans 'Gui' - pour que le module ait le nom __main__, et vous importez Setting dans ce module. Alors bien sûr, la classe Manager sera en fait Settings.Manager. Mais le fichier pkl ne le sait pas et recherche la classe Manager dans __main__, et lance une AttributeError car elle n'existe pas (Settings.Manager oui, mais __main__.Manager non).

Voici un ensemble de codes minimal à démontrer.

Le class_def.py module:

import pickle

class Foo(object):
    def __init__(self, name):
        self.name = name

def main():
    foo = Foo('a')
    with open('test_data.pkl', 'wb') as f:
        pickle.dump([foo], f, -1)

if __name__=='__main__':
    main()

Vous exécutez ce qui précède pour générer les données de cornichon. Le main_module.py module:

import pickle

import class_def

if __name__=='__main__':
    with open('test_data.pkl', 'rb') as f:
        users = pickle.load(f)

Vous exécutez ce qui précède pour tenter d'ouvrir le fichier de cornichons, ce qui génère à peu près la même erreur que vous voyiez. (Légèrement différent, mais je suppose que c'est parce que je suis sur Python 2.7)

La solution est soit:

  1. Vous rendez la classe disponible dans l'espace de noms du module de niveau supérieur (c'est-à-dire GUI ou main_module) via une importation explicite, ou
  2. Vous créez le fichier pickle à partir du même module de niveau supérieur que celui dans lequel vous l'ouvrirez (c'est-à-dire appeler Settings.addUser depuis l'interface graphique ou class_def.main de main_module). Cela signifie que le fichier pkl enregistrera les objets sous Settings.Manager ou class_def.Foo, qui peut ensuite être trouvé dans l'espace de noms GUI`main_module`.

Exemple de l'option 1:

import pickle

import class_def
from class_def import Foo # Import Foo into main_module's namespace explicitly

if __name__=='__main__':
    with open('test_data.pkl', 'rb') as f:
        users = pickle.load(f)

Exemple de l'option 2:

import pickle

import class_def

if __name__=='__main__':
    class_def.main() # Objects are being pickled with main_module as the top-level
    with open('test_data.pkl', 'rb') as f:
        users = pickle.load(f)
52
zehnpaard

Veuillez d'abord lire la réponse mentionnée par zehnpaard pour connaître la raison de l'erreur d'attribut. Autre que la solution qu'il a déjà fournie, en python3 vous pouvez utiliser le pickle.Unpickler classe et remplacer le find_class méthode telle que mentionnée ci-dessous:

import pickle

class CustomUnpickler(pickle.Unpickler):

    def find_class(self, module, name):
        if name == 'Manager':
            from settings import Manager
            return Manager
        return super().find_class(module, name)

pickle_data = CustomUnpickler(open('file_path.pkl', 'rb')).load()
17
Pankaj Saini

Si vous avez une classe définie en dehors du module, dont l'objet est dans des données de pickle, vous devez importer la classe

from outside_module import DefinedClass1, DefinedClass2, DefinedClass3 

with open('pickle_file.pkl', 'rb') as f:
    pickle_data = pickle.load(f)
0
Vinoj John Hosan