The add_page()
function errors whenever I try to get any user input in __init__()
or a classmethod
. It works fine when I don't have any methods to get user input, so I think it's somehow interfering.
AttributeError: 'PDF' object has no attribute 'state'. Did you mean: 'rotate'?
Error
Name: ttt
Traceback (most recent call last):
File "/workspaces/106404228/shirtificate/shirtificate.py", line 63, in <module>
pdf = PDF.get_name()
File "/workspaces/106404228/shirtificate/shirtificate.py", line 44, in get_name
return cls(name)
File "/workspaces/106404228/shirtificate/shirtificate.py", line 32, in __init__
self.add_page(self, format='a4')
File "/home/ubuntu/.local/lib/python3.10/site-packages/fpdf/fpdf.py", line 813, in add_page
if self.state == DocumentState.CLOSED:
AttributeError: 'PDF' object has no attribute 'state'. Did you mean: 'rotate'?
Code
from fpdf import FPDF
class PDF(FPDF):
def __init__(self, name):
if not name:
raise ValueError("no name")
self.name = name
self.add_page(self, format='a4')
# header
def header(self):
self.image("shirtificate.png")
self.ln(20)
@classmethod
def get_name(cls):
name = input("Name: ")
return cls(name)
@property
def name(self):
return self._name
@name.setter
def name(self, name):
if not name:
raise ValueError("no name")
self._name = name
pdf = PDF.get_name()
pdf.set_font("helvetica", "B", 16)
pdf.output("shirtificate.pdf")
CodePudding user response:
It's not because of the user input, but it's because you are calling .add_page
when the FPDF
object is not yet properly instantiated. You can replace the user input with a hardcoded name
and you would still get the same error. The classmethod is just making the problem more visible.
You can see the sequence of what's happening from the Traceback:
File "/workspaces/106404228/shirtificate/shirtificate.py", line 44, in get_name
return cls(name)
File "/workspaces/106404228/shirtificate/shirtificate.py", line 32, in __init__
self.add_page(self, format='a4')
The return cls(name)
code will instantiate the FDPF
object, which then calls your __init__
, which then calls .add_page
. But .add_page
is an instance method of the FDPF
class. It's expected to be called after the object has been instantiated. In your code, since the __init__
of the parent FPDF
class wasn't called, then your object would be missing a .state
and all the other attributes of a FPDF
object, which leads to the error:
'PDF' object has no attribute 'state'
You can check the __init__
method of the FPDF
class to see what it does. If you are creating a custom subclass, you typically call the parent's __init__
as part of your subclass __init__
. (see Why aren't superclass __init__
methods automatically invoked?).
The fix is to reorganize your code to something like this:
from fpdf import FPDF
class PDF(FPDF):
def __init__(self, name):
if not name:
raise ValueError("no name")
# Call the parent FPDF init
super().__init__()
# Add your custom code after
self.name = name
...
@classmethod
def get_name(cls):
name = input("Name: ")
# Create the object
obj = cls(name)
# Call FPDF object instance methods
# after it is created to customize
obj.add_page(format="A4")
return obj
...
pdf = PDF.get_name()
pdf.set_font("helvetica", "B", 16)
pdf.output("shirtificate.pdf")
Here, the main changes are:
- Calling
super().__init__()
to call the parent's__init__
- Calling
.add_page
after instantiating an object
Another recommended change would be renaming that classmethod since it does more than just "getting the name". It should be called something like create_from_name
or create_with_prompted_name
.