Reputation: 1003
I have a ReportEntry
class
class ReportEntry(object):
def __init__(self):
# Many attributes defined here
... # Lot many setattr/getattr here
def validate(self):
# Lot of validation code in here
return self
Multiple other classes maintain has-a
relation with ReportEntry
class
class A(object):
def test1(self):
t1 = ReportEntry()
# Assign the attribute values to t1
return t1.validate()
def test2(self):
t2 = ReportEntry()
# Assign the attribute values to t2
return t2.validate()
And there are multiple such classes as A.
I need to enforce each ReportEntry
class instance to call validate()
on return
or maybe just before return
.
Basically, no instance of ReportEntry
should escape validation since the final report generation will fail if something is missing.
How may I achieve that ?
Upvotes: 4
Views: 2316
Reputation: 1003
One approach that I could think of is to define __enter__
and __exit__
methods where validate
is called upon __exit__
in ReportEntry
class ReportEntry(object):
def __enter__(self):
return self
def __init__(self):
# Many attributes defined here
... # Lot many setattr/getattr here
def validate(self):
# Lot of validation code in here
return self
def __exit__(self, a,b,c):
self.validate()
return True
# And then use it as
with ReportEntry() as report:
...
But again, this will be enforced only when used with ReportEntry() as report:
Upvotes: 1
Reputation: 73460
You can write a class decorator:
import inspect
def validate_entries(cls):
def validator(fnc): # this is a function decorator ...
def wrapper(*args, **kwargs):
rval = fnc(*args, **kwargs)
if isinstance(rval, ReportEntry):
# print('validating')
return rval.validate()
return rval
return wrapper
for name, f in inspect.getmembers(cls, predicate=inspect.isfunction):
setattr(cls, name, validator(f)) # .. that we apply to all functions
return cls
Now you can define all A
-like classes:
@validate_entries
class A(object):
# ...
This will validate any ReportEntry
that is returned by any of A
's methods.
Upvotes: 3
Reputation: 1077
There are two ways I can think about to go about this. I cannot say more without knowing more implementation details:
Decorate your methods: Where every return instance is run through the decorator function. You may want to put this as a stand-alone function or part of a class depending on your specific use case.
def validate(func):
return func().validate()
class A(object):
@validate
def test1(self):
t1 = ReportEntry()
# Assign the attribute values to t1
return t1
@validate
def test2(self):
t2 = ReportEntry()
# Assign the attribute values to t2
return t2
Updating the __setattr__ and decorate your class:
def always_validate(cls):
# save the old set attribute method
old_setattr = getattr(cls, '__setattr__', None)
def __setattr__(self, name, value):
# set the attribute
validate(name, value)
old_setattr(self, name, value)
cls.__setattr__ = __setattr__
return cls
and then you could decorate your ReportEntry:
@alway_validate
class ReportEntry(object):
...
Upvotes: 2