Reputation:
I have below one class vuln.py
from reportlab.graphics.shapes import Drawing, String, Rect
class vuln(Drawing):
def __init__(self, width=300, height=150, report_type=None, *args, **kw):
Drawing.__init__(self, width, height, *args, **kw)
self.report_type = report_type
def print_report(self):
print self.report_type
and calling program rep.py
import vuln
obj = vuln.vuln(report_type="abc")
obj.print_report()
After executing this it gives error,
Traceback (most recent call last):
File "rep.py", line 3, in <module>
obj = vuln.vuln(report_type="abc")
File "/data/support/vuln.py", line 5, in __init__
self.report_type = report_type
File "/usr/lib64/python2.6/site-packages/reportlab/graphics/shapes.py", line 359, in __setattr__
validateSetattr(self,attr,value) #from reportlab.lib.attrmap
File "/usr/lib64/python2.6/site-packages/reportlab/lib/attrmap.py", line 118, in validateSetattr
raise AttributeError, "Illegal attribute '%s' in class %s" % (name, obj.__class__.__name__)
AttributeError: Illegal attribute 'report_type' in class vuln
Please help to know what the error is all about.
Upvotes: 4
Views: 619
Reputation: 7887
Long story short, their developers are causing you this pain and doing things they shouldn't be doing in python. Luckily it is open source. The API you are using dynamically checks the attributes of classes that inherit from Shape
, or as they say in /reportlab/graphics/shapes.py, line 359:
if shapeChecking:
"""This adds the ability to check every attribute assignment as it is made.
It slows down shapes but is a big help when developing. It does not
get defined if rl_config.shapeChecking = 0"""
def __setattr__(self, attr, value):
"""By default we verify. This could be off
in some parallel base classes."""
validateSetattr(self,attr,value) #from reportlab.lib.attrmap
Keep digging through the code and on line 99 of the attrmap source code you can see what is causing the issue:
def validateSetattr(obj,name,value):
'''validate setattr(obj,name,value)'''
if rl_config.shapeChecking:
map = obj._attrMap
if map and name[0]!= '_':
#we always allow the inherited values; they cannot
#be checked until draw time.
if isinstance(value, DerivedValue):
#let it through
pass
else:
try:
validate = map[name].validate
if not validate(value):
raise AttributeError("Illegal assignment of '%s' to '%s' in class %s" % (value, name, obj.__class__.__name__))
except KeyError:
raise AttributeError("Illegal attribute '%s' in class %s" % (name, obj.__class__.__name__))
obj.__dict__[name] = value
Note that they don't check attributes that start with _
or __
which pythonists use to indicate "private" variables; as such, you can fix your code as follows:
from reportlab.graphics.shapes import Drawing, String, Rect
class vuln(Drawing):
def __init__(self, width=300, height=150, report_type=None, *args, **kw):
Drawing.__init__(self, width, height, *args, **kw)
self._report_type = report_type
def print_report(self):
print self._report_type
Then everything should work as normal.
Upvotes: 3
Reputation: 24691
Your stack trace provides a bit of a clue, and searching for attrmap
in this documentation (see page 35) provides more information. The most useful bit of info actually came from calling help()
on the Drawing
class:
...
| Methods inherited from reportlab.graphics.shapes.Shape:
|
| __setattr__(self, attr, value)
| By default we verify. This could be off
| in some parallel base classes.
...
This seems to tie back into the documentation.
The underlying problem here is that, for whatever reason, the reportlab
objects feel the need to verify that paramaters are "expected". Looking at the source code via the inspect
module yields this:
>>> print(inspect.getsource(Drawing.__setattr__))
def __setattr__(self, attr, value):
"""By default we verify. This could be off
in some parallel base classes."""
validateSetattr(self,attr,value) #from reportlab.lib.attrmap
>>> print(inspect.getsource(reportlab.lib.attrmap.validateSetattr))
def validateSetattr(obj,name,value):
'''validate setattr(obj,name,value)'''
if rl_config.shapeChecking:
map = obj._attrMap
if map and name[0]!= '_':
#we always allow the inherited values; they cannot
#be checked until draw time.
if isinstance(value, DerivedValue):
#let it through
pass
else:
try:
validate = map[name].validate
if not validate(value):
raise AttributeError("Illegal assignment of '%s' to '%s' in class %s" % (value, name, obj.__class__.__name__))
except KeyError:
raise AttributeError("Illegal attribute '%s' in class %s" % (name, obj.__class__.__name__))
obj.__dict__[name] = value
From this we can conclude that there are solutions. First, you can override __setattr__()
to not call validateSetattr()
- just add the following method to your class:
def __setattr__(self, name, value):
# use the `object` class's __setattr__ instead of the superclass's
# thus avoiding calling validateSetattr()
object.__setattr__(self, name, value)
Or alternatively you can do what @ErrorSyntacticalRemorse said above, and add a _
to the beginning of your variable names. Or you can try to monkey with the self._attrMap
variable, but I'm actually not sure how to do that in a way that will work with the function.
Upvotes: 0