Reputation: 437
I am trying to get a private class decorator, or at least a decorator that is associated with the class working. I have been trying to use an inline class, but I am currently getting the following problem when I run it:
click_ImportToDB() missing 1 required positional argument: 'self'
If you could take a look at the following, which I have cut down quite a bit and let me know what I am doing wrong, that would be great:
class frmApplication(tk.Frame):
# THIS IS THE PRIVATE INLINE CLASS DECORATOR THAT I AM TRYING TO BUILD.
class _form_validation():
def __init__(self, func):
self.func = func
def __call__(self):
# Wrap the function up in error trapping and form validation
try:
# Ensure the strDBPath and config file have the same value for the database
# Ensure the database actually exists
# Now call the function
self.func()
except Exception as Ex:
strReport=Ex.args[0]
messagebox.showerror("Error",strReport)
#BACK TO THE frmApplication CLASS
def __init__(self,FormParent=tk.NONE):
.....
def __populateDatabaseFrame(self):
....
def __populateButtonFrame(self):
#First, add a frame
self.__objButtonFrame=ttk.Frame(self.master,border=2,relief=tk.SUNKEN)
self.__objButtonFrame.grid(row=1,column=0,columnspan=1,sticky=tk.N+tk.S+tk.E+tk.W,padx=10,pady=10)
F=self.__objButtonFrame
#Configure the columns of the frame
F.grid_columnconfigure(0,weight=1)
#Now add an upload to database button into the frame
# THERE IS A CALL TO click_ImportToDB HERE !!!!!
F.__btnImportToDB=ttk.Button(F,text="Upload to Database",command=self.click_ImportToDB)
F.__btnImportToDB.grid(row=0,column=0,sticky=tk.E+tk.W,padx=10,pady=10)
#Now add a button to show the data
.....
def __createDatabase(self):
...
def __selectDatabase(self):
...
#Public Functions.
def click_SelectDB(self):
...
def click_NewDB(self):
....
@_form_validation
def click_ImportToDB(self):
frmTop=tk.Toplevel(self.master)
frmTop.transient(frmTop.master)
frmTop.focus_set()
frmTop.grab_set()
objFrm=vID.frmImportToDB(frmTop)
frmTop.wait_window()
del objFrm
Many thanks
Mark
Upvotes: 0
Views: 43
Reputation: 437
Thanks for your help. The first suggestion didn't really work. I have edited the code to try to help explain what the problem is:
class Test():
def __init__(self):
self.name = 'test class'
class _decorator(object):
def __init__(self, arg):
self.name = '_decorator class'
self._arg = arg
def __call__(self):
print("\n---- BEFORE FUNCTION EXECUTION ----\n")
print('self points to: "' + self.name + '"')
print('However I really need it to point to the test class to perform validation etc.')
self._arg(self)
print("\n---- AFTER FUNCTION EXECUTION ----\n")
@_decorator
def test_method(self):
print('self points to: "' + self.name + '"')
print("**** Executing test_method ****")
if __name__ == '__main__':
test = Test()
test.test_method()
The output looks like this:
---- BEFORE FUNCTION EXECUTION ----
self points to: "_decorator class"
However I really need it to point to the test class to perform validation etc.
self points to: "_decorator class"
**** Executing test_method ****
---- AFTER FUNCTION EXECUTION ----
I did try adjusting the second option as follow's:
from functools import wraps
class Test():
def __init__(self):
self.name = 'Test Class'
def _decorator(f):
@wraps(f)
def wrapped(inst, *args, **kwargs):
print('inst points to: '+inst.name)
print("EXECUTING BEFORE FUNC")
return_val = f(inst, *args, **kwargs)
print("EXECUTING AFTER FUNC")
return return_val
return wrapped
@_decorator
def test_method(self):
print("EXECUTING FUNC")
return "RETURN FROM TEST_METHOD"
if __name__ == '__main__':
test = Test()
print(test.test_method())
The output looks like this:
inst points to: Test Class
EXECUTING BEFORE FUNC
EXECUTING FUNC
EXECUTING AFTER FUNC
RETURN FROM TEST_METHOD
This looks much more like what I was after. The only downside is that the linter now reports the following problem, however it doesn't stop it from working:
Method '_decorator' should have 'self' as first argument.Python(no-self-argument)
Upvotes: 0
Reputation: 536
i'm not sure you're passing the argument from the decorator to the func.
class Test():
class _decorator(object):
def __init__(self, arg):
self._arg = arg
print(self._arg)
def __call__(self):
print("DOING SOMETHING HERE BEFORE THE FUNC EXEC")
retval = self._arg(self)
print("DOING SOMETHING HERE AFTER THE FUNC EXEC")
return retval
@_decorator
def test_method(self):
print("EXECUTING FUNC")
return "RETURN FROM TEST_METHOD"
if __name__ == '__main__':
test = Test()
print(test.test_method())
I tried to create this code to replicate your doubt.
try, passing self
to your function.
If that does not work. You cant try to use noraml decorators instead of class decoratos, here is the same example.
from functools import wraps
class Test():
def _decorator(f):
@wraps(f)
def wrapped(inst, *args, **kwargs):
print("EXECUTING BEFORE FUNC")
return_val = f(inst, *args, **kwargs)
print("EXECUTING AFTER FUNC")
return return_val
return wrapped
@_decorator
def test_method(self):
print("EXECUTING FUNC")
return "RETURN FROM TEST_METHOD"
if __name__ == '__main__':
test = Test()
print(test.test_method())
hope it helps.
Upvotes: 1