How to serialize a Python class using jsonpickle? - python

I am running the following code on VS Code to serialize a Python class:
import json
import jsonpickle
import sys
class P_C(object):
def __init__(self, name = "default name", items = None):
self.name = name
self.items = items
def get_name (self):
return f"Name is: {self.name}"
def get_items (self):
return f"Items are: {self.items}"
def get_name_items_dict (self):
return dict (name = self.name, items = self.items )
p_c = P_C()
with open ("P_C.json", "w") as json_file:
json_file.write (jsonpickle.encode(p_c))
When I run this code on VS Code, I get the following error:
AttributeError: module 'jsonpickle' has no attribute 'encode'
However, when I run the exact same code on a Jupyter notebook, I get the desired output, which is inside P_C.json:
{"items": null, "name": "default name", "py/object": "__main__.P_C"}
I already checked that I am using Python 3 on VS Code
Thanks!

Related

Confused by accessing class methods

I'm not new to OOP but I'm quite a noobie when it comes to python, I'm working on a framework that should help in the development of my work but I'm getting confused by how to access methods.
I'm on Python 3.10
class Dialogue(EngineComponent):
def __init__(self, name: str, directory=""):
self.__scenes = []
self.__directory = directory
super().__init__(name, "dialogue", ".dialogue.json")
def scene(self, tag: str):
scene = Dialogue.__Scene(tag)
self.__scenes.append(scene)
return scene
def export(self):
dialogues = {
"format_version": "1.17.0",
"minecraft:npc_dialogue": {
"scenes": []
}
}
for scene in self.__scenes:
dialogues["minecraft:npc_dialogue"]["scenes"].append(
scene.__export())
self.content(dialogues)
return super().export(self.__directory)
class __Scene():
def __init__(self, tag: str):
self.__buttons = []
self.__tag = tag
def properties(self, npc_name: str, text: str):
self.__npc_name, self.__npc_display_name = RawText(npc_name)
self.__text, self.__display_text = RawText(text)
return self
def button(self, button_name: str, commands: str or object):
button = self.__Button(button_name, commands)
self.__buttons.append(button)
return button
def __export(self):
buttons = []
for button in self.__buttons:
buttons.append(button.__export())
Language(f"npc_name.{self.__npc_name}={self.__npc_display_name}\n")
Language(f"npc_text.{self.__tag}={self.__display_text}\n")
return {
"scene_tag": self.__tag,
"npc_name": {"rawtext": [{"translate": f"npc_name.{self.__npc_name}"}]},
"text": {"rawtext": [{"translate": f"npc_text.{self.__tag}", "with": ["\n"]}]},
"buttons": buttons
}
class __Button():
def __init__(self, button_name: str, commands: str or Function):
self.__button_name = button_name
if isinstance(commands, Function):
self.__commands = f"function {commands.get_path()}"
else:
self.__commands = commands
def __export(self):
Language(f"{self.__button_name}={self.__button_name}\n")
return {
"name": self.__button_name,
"commands": [
self.__commands
]
}
I have written the class above to help with the generation of files, and actual usage of that class should be
d0 = Dialogue("my_dialogue")
scene0 = d0.scene("hello").properties("Jake","Hello traveler!\\nLooking for some tips?").button("Get Tips","/say tips")
d0.export()
Everything worked fine when I write all the export methods as export()
But I want to prevent people from using the inner classes export() methods for the __Scene and __Button so I'm following the PEP8 or so I read on other posts that I should just switch export() to __export(). It can still be used but someone familiar with python knows better not to
But when I do that I get this error AttributeError: '__Scene' object has no attribute '_Dialogue__export'
for scene in self.__scenes:
dialogues["minecraft:npc_dialogue"]["scenes"].append(
scene.__export())
And I can't figure out why?? I've just changed the name of the method and the way how I call it, why is it not working as intended?
I'm also using chained method calls as you can see in properties() and button() but also for a lot of other classes where I have so many methods. I'm wondering if there's a way to limit the reusability of each method but still allow chained calls for unused ones?

python-sphinx extension, how to get name of current object?

How to get the name of parent object in Python code for which is current documentation build for? I mean how to get name of class "ExampleCls0" in MyDirective.run()?
class ExampleCls0():
"""
.. mydirect::
"""
Lets suppose that we have Spring directive called mydirect.
And it is correctly registered in Sphinx and documentation is build for python code.
class MyDirective(Directive):
required_arguments = 0
optional_arguments = 0
has_content = True
option_spec = {}
def run(self):
env = self.state.document.settings.env
def setup(app):
app.add_directive('mydirect', MyDirective)
For build I am using:
from sphinx.cmdline import main as sphinx_main
from sphinx.ext.apidoc import main as apidoc_main
apidoc_main(["--module-first", "--force", "--full",
"--output-dir", "doc/", "."])
sphinx_main(["-b", "html", "-E",
"-c", pwd,
"doc/",
"doc_build/",
])
I do not know if name of the parent object can be accessed somewhere in Directive.run method, but I found out that it is possible to read the name later.
class SchematicLink(nodes.TextElement):
#staticmethod
def depart_html(self, node):
self.depart_admonition(node)
#staticmethod
def visit_html(self, node):
parentClsNode = node.parent.parent
assert parentClsNode.attributes['objtype'] == 'class'
assert parentClsNode.attributes['domain'] == 'py'
sign = node.parent.parent.children[0]
assert isinstance(sign, desc_signature)
absoluteName = sign.attributes['ids'][0]
print(absoluteName) # file0.ExampleCls0
self.visit_admonition(node)
class MyDirective(Directive):
required_arguments = 0
optional_arguments = 0
def run(self):
schema_node = SchematicLink()
self.state.nested_parse(self.content,
self.content_offset,
schema_node)
return [schema_node]
def setup(app):
app.add_node(SchematicLink,
html=(SchematicLink.visit_html,
SchematicLink.depart_html))
app.add_directive('mydirect', MyDirective)
And this is probably good example how NOT to do it. Code reads id from label of class doc.

how to create an object which takes as a parameter another object using python

I want to create my own logging class: which writes some data to a text file.
For this I have made a class mylog.py
I want to be able to create an instance object of the mylog.py class an pass the instance object as a paremeter to the other classes I have written.
However when I try to access the mylog object using the self notation and without using the self notation I am having issues.
The issue is that when I refer to the mylog object in the startup class and use self.log = logger this doesn't work to use the methods of the mylog class like self.log.write() nor does setting logobj to a variable without self and passing that in.
My mylog.py class
import datetime
import os
class logtextfile(object):
def __init__(self, name):
self.name = name
def __str__(self):
return "{} ".format(self.__class__.__name__)
def write(self,**kwargs):
"""Writes a log message to a user specified file which indicates the action takes and if it was successful"""
self.file = kwargs.get('file',"log.txt")
self.loglevel = kwargs.get('loglevel',"critical")
self.logmessage = kwargs.get('logmessage',"error")
self.success = kwargs.get('success',False)
self.class_name = kwargs.get('class',str("{}".format(self.__class__.__name__)))
self.output = ", ".join([str(datetime.datetime.now().replace(second=0,microsecond=0)),self.class_name,str(self.logmessage),str(self.success),str("\n")])
for key, value in kwargs.items():
setattr(self,key,value)
f = open(str(self.file),"a")
f.write(self.output)
f.close()
def now(self, filename, openas, data):
"""Creates a log file with todays date and time"""
fmt='%Y-%m-%d-%H-%M-%S_{fname}'
fn = datetime.datetime.now().strftime(fmt).format(fname=filename)
f = open(str(fn),openas)
f.write(data + "\n")
f.close()
My startup class
import pandas as pd
import numpy as np
from pandas_datareader import data as web
import datetime
import requests
import lxml
from IPython.display import clear_output
import time
import timeit
from bs4 import BeautifulSoup
import re
import os
import sqlite3
from sqlalchemy import create_engine # database connection
from zenlog import log
class company(object):
def __init__(self, name, logobj):
self.name = name
logger = logobj
def __str__(self):
return "{} ".format(self.__class__.__name__)
def listed(self):
try:
#all companies on asx downloaded from asx website csv
self.function_name = str("{}".format(self.__class__.__name__))
df = pd.read_csv('http://asx.com.au/asx/research/ASXListedCompanies.csv', skiprows=1)
df.columns = ["company","asx_code","industry"]
df["yahoo_code"] = df["asx_code"]+".AX"
message = "succesfully downloaded ASXListedCompanies.csv"
logger.write(file="asx_module_log.txt",logmessage=message,success=True)
return df
except:
message = "ASXListedCompanies.csv could not be retrieved, the website is unavailable"
try:
logger.write(file="asx_module_log.txt",logmessage=message)
except:
log.critical(message)
def valid(self):
try:
df = self.listed()
return df[(df["industry"]!= "Not Applic") & (df["industry"]!="Class Pend")]
except:
message = "Could not retrieve listed companies object with pandas dataframe"
try:
logfile.write(file="asx_module_log.txt",logmessage=message)
except:
log.critical(message)
def invalid(self):
try:
df = self.listed()
return df[(df["industry"]=="Not Applic") | (df["industry"]=="Class Pend")]
except:
message = "Could not retrieve listed companies object with pandas dataframe"
try:
logfile.write(file="asx_module_log.txt",logmessage=message)
except:
log.critical(message)
my code to create an instance of mylog and pass it to the startup class so that it can log to the textfile.
import mylog
import startup
logger = mylog.logtextfile(name="mylogfile")
c = startup.company(name="mycompany",logobj=logger)
df = c.invalid()
df.head()
I can't test your company class: I don't have most of those 3rd-party modules. However, it's generally a bad idea to have "naked" except clauses. Use named exceptions, otherwise you may be catching things that you don't expect.
Anyway, here's a short demo of using your logger in an instance of another class.
import datetime
class LogTextfile(object):
def __init__(self, name):
self.name = name
def __str__(self):
return "{} ".format(self.__class__.__name__)
def write(self, **kwargs):
""" Writes a log message to a user specified file which
indicates the action takes and if it was successful
"""
self.file = kwargs.get('file', "log.txt")
self.loglevel = kwargs.get('loglevel', "critical")
self.logmessage = kwargs.get('logmessage', "error")
self.success = kwargs.get('success', False)
self.class_name = kwargs.get('class', str("{}".format(self.__class__.__name__)))
self.output = ", ".join([str(datetime.datetime.now().replace(second=0, microsecond=0)),
self.class_name, str(self.logmessage), str(self.success), str("\n")])
for key, value in kwargs.items():
setattr(self, key, value)
f = open(str(self.file), "a")
f.write(self.output)
f.close()
def now(self, filename, openas, data):
"""Creates a log file with todays date and time"""
fmt = '%Y-%m-%d-%H-%M-%S_{fname}'
fn = datetime.datetime.now().strftime(fmt).format(fname=filename)
f = open(str(fn), openas)
f.write(data + "\n")
f.close()
class Test(object):
def __init__(self, logger):
self.logger = logger
def logtest(self, message):
self.logger.write(logmessage=message)
logger = LogTextfile(name="mylogfile")
logger.write(logmessage='This is a test')
t = Test(logger)
t.logtest('Message from Test')
contents of "log.txt"
2017-05-04 22:40:00, LogTextfile, This is a test, False,
2017-05-04 22:40:00, LogTextfile, Message from Test, False,
Simply:
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
class A(object):
def __init__(self, x=None):
self.x = x
class B(object):
def __init__(self, a):
if (isinstance(a, A)):
self.x = a.x
else:
self.x = 0
if __name__ == "__main__":
a = A(10)
b = B(a)

TypeError: 'tuple' object is not callable when trying to call method

Here is what I have going on so far:
# -*- coding: cp1252 -*-
import time
class Item():
def __init__(self, name, description, base_value):
self.name = name
self.description = description
self.ingredients = ingredients
self.base_value = value
def __str__(self):
return format(self.name, self.description, self.ingredients, self.base_value)
class Metal(Item):
def __init__(self, name, description, ingredients, base_value):
self.smelt_time = smelt_time
self.smelted = smelted
def __str__(self):
return format(self.name, self.description, self.ingredients, self.base_value, self.smelt_time, self.smelted)
class Bronze_Ingot(Metal):
def __init__(self):
self.name = "Bronze Ingot",
self.description = "A refined ingot of bronze."
#self.ingredients = Tin_Ore(1)+Copper_Ore(1) <--- I will get these lines working later...
self.base_value = 33
self.smelt_time = 15
self.smelted = ()
class Fuel(Item):
def __init__(self, name, description, ingredients, base_value):
self.fuel_time = fuel_time
def __str__(self):
return format(self.name, self.description, self.ingredients, self.base_value, self.fuel_time)
class Cloth(Fuel):
def __init__(self):
self.name = "Cloth",
self.description = "A piece of cotton cloth."
#self.ingredients = 2 Cotton <--- I will get these lines working later...
self.base_value = 2
self.fuel_time = 5
But I am having great trouble with this function...
def smelted(Fuel, Metal):
if (Fuel.fuel_time - Metal.smelt_time) > 0:
time.sleep(1)
print "Smelting", Metal.name, "..."
time.sleep(Metal.smelt_time)
print "Time to forge!"
The problem is more or less making it work. My friend and I have tried EVERYTHING that we can think of when running this function, but to no avail. Here is our most recent attempt:
from Smelting_Progress import *
x = Cloth()
y = Bronze_Ingot()
y.smelted(x,y)
After trying to run this, I received this error:
Traceback (most recent call last):
File "C:\Users\WCS-HSSTUDENT\Desktop\Files\Project SAOffline\Coding\New Aincrad World\Items\NAI_Smelted.pyw", line 6, in <module>
Metal.smelted(Fuel, Metal)
TypeError: 'tuple' object is not callable
You have an instance attribute smelted; you set it in Metal.__init__():
self.smelted = smelted
Your Bronze_Ingot subclass sets it to an empty tuple:
self.smelted = ()
You cannot have both a method and and a tuple use the same name. Rename one or the other.
If you meant to use your smelted() code as a function, then define it at the top level (same indentation as your classes), and call it as a function, not a method:
smelted(x, y)
(note, no y. in front).

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)>
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:
You make the class available within the namespace of the top-level module (i.e. GUI or main_module) through an explicit import, or
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)
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()
If you're still getting this error even after importing the appropriate classes in the loading module (zehnpaard's solution #1), then the find_class function of pickle.Unpickler can be overwritten and explicitly directed to look in the current module's namespace.
import pickle
from settings import Manager
class CustomUnpickler(pickle.Unpickler):
def find_class(self, module, name):
try:
return super().find_class(__name__, name)
except AttributeError:
return super().find_class(module, name)
pickle_data = CustomUnpickler(open('file_path.pkl', 'rb')).load()
## No exception trying to get 'Manager'
Note: This method loses the relative-import path information stored in module. So, be careful of namespace collisions in your pickled classes.
If you have a class defined outside the module, whose object is in pickle data,
you have to import the class
from outside_module import DefinedClass1, DefinedClass2, DefinedClass3
with open('pickle_file.pkl', 'rb') as f:
pickle_data = pickle.load(f)
if you use dill dump/load model will work
import dill
from sklearn.preprocessing import FunctionTransformer
sp_clf = FunctionTransformer(lambda X:X.astype('float').fillna(0).applymap(abs))
with open('temp.joblib','wb') as io:
dill.dump(sp_clf,io)
with open('temp.joblib','rb') as io:
dd=dill.load(io)

Categories

Resources