Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to load files using pickle and multiple modules

I'm trying to create a user system, which uses a setting and Gui module, and when the GUI module requests for the file to load up using pickle, I keep getting an attribute error. this is from the settings module:

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) 

and this is the GUI module:

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_()) 

each user is a class and are put into a list and then the list is saved using pickle when I load up just the settings file and verify the login everything works fine but when I open up the GUI module and try to verify it doesn't let me, the error I'm getting:

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)> 
like image 680
Inthu Avatar asked Jan 01 '15 16:01

Inthu


People also ask

How do I use pickle to load a file?

Python Pickle load You have to use pickle. load() function to do that. The primary argument of pickle load function is the file object that you get by opening the file in read-binary (rb) mode. Simple!

Can you pickle multiple objects Python?

pickle will read them in the same order you dumped them in. Prints out ['One', 'Two', 'Three'] ['1', '2', '3'] . To pickle an arbitrary number of objects, or to just make them easier to work with, you can put them in a tuple, and then you only have to pickle the one object.

What module do you import if you want to pickle objects?

The shelve module provides a simple interface to pickle and unpickle objects on DBM-style database files.

Why is python pickle unsafe?

Dangers of Python pickling Since there are no effective ways to verify the pickle stream being unpickled, it is possible to provide malicious shell code as input, causing remote code execution. The most common attack scenario leading to this would be to trust raw pickle data received over the network.


2 Answers

The issue is that you're pickling objects defined in Settings by actually running the 'Settings' module, then you're trying to unpickle the objects from the GUI module.

Remember that pickle doesn't actually store information about how a class/object is constructed, and needs access to the class when unpickling. See wiki on using Pickle for more details.

In the pkl data, you see that the object being referenced is __main__.Manager, as the 'Settings' module was main when you created the pickle file (i.e. you ran the 'Settings' module as the main script to invoke the addUser function).

Then, you try unpickling in 'Gui' - so that module has the name __main__, and you're importing Setting within that module. So of course the Manager class will actually be Settings.Manager. But the pkl file doesn't know this, and looks for the Manager class within __main__, and throws an AttributeError because it doesn't exist (Settings.Manager does, but __main__.Manager doesn't).

Here's a minimal code set to demonstrate.

The 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() 

You run the above to generate the pickle data. The main_module.py module:

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

You run the above to attempt to open the pickle file, and this throws roughly the same error that you were seeing. (Slightly different, but I'm guessing that's because I'm on Python 2.7)

The solution is either:

  1. You make the class available within the namespace of the top-level module (i.e. GUI or main_module) through an explicit import, or
  2. You create the pickle file from the same top-level module as the one that you will open it in (i.e. call Settings.addUser from GUI, or class_def.main from main_module). This means that the pkl file will save the objects as Settings.Manager or class_def.Foo, which can then be found in the GUI`main_module` namespace.

Option 1 example:

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) 

Option 2 example:

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) 
like image 136
zehnpaard Avatar answered Oct 12 '22 07:10

zehnpaard


Please first read the answer mentioned by zehnpaard to know the reason for the attribute error. Other than the solution he already provided, in python3 you can use the pickle.Unpickler class and override the find_class method as mentioned below:

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() 
like image 23
Pankaj Saini Avatar answered Oct 12 '22 06:10

Pankaj Saini