[reportlab-users] Canvas does not work with SpooledTemporaryFile

162 views
Skip to first unread message

Robert Schroll

unread,
Mar 14, 2021, 8:55:19 PM3/14/21
to reportl...@lists2.reportlab.com
Hi all,

I understand this is the correct place to report bugs in ReportLab?  If not please point me to the right location.

I've found that Canvas's save function doesn't work if the canvas was created with a tempfile.SpooledTemporaryFile.  Specifically, it raises
> AttributeError: 'NoneType' object has no attribute 'decode'
I've attached a test case at the end of this email.  It fails on several recent versions of ReportLab, including 3.5.65, which seems to be the latest available through PyPI.  Note that the example works just fine with a standard TemporaryFile.

The problem seems to be around line 212 of pdfdoc.py.  It gets the name property of the file object, defaulting to empty string if it does not exist.  If the name is an integer (as it is for a TemporaryFile), a special string is formatted with that integer.  Otherwise, it passes the name property to `makeFileName`, which is expecting a string argument.  The implicit assumption is that the name property of a file object is a string, an int, or nonexistent.

However, a SpooledTemporaryFile has a name property of `None` (at least until it rolls over to disk), violating this assumption.  I see two relatively easy fixes:
1. Default to empty string for any falsey name attribute:
    filename = getattr(f, 'name', None) or ''
This would catch this problem, but it would still be open to any file object that defines a name property of another type.

2. Explicitly cast the name to a string:
    filename = makeFileName(str(filename))
This would avoid all such problems, at the risk of producing some strange-looking filenames.  But given that the existing code already produces such strange-looking filenames for ints, I suspect this is okay.

Hope this makes sense.  Let me know if you have any questions,
Robert

Example code:

import tempfile
from reportlab.pdfgen import canvas

f = tempfile.SpooledTemporaryFile()
c = canvas.Canvas(f)
c.save()  # Raises AttributeError

Robin Becker

unread,
Mar 15, 2021, 5:12:36 AM3/15/21
to reportlab-users, Robert Schroll
Hi Robert, thanks for the report. Your analysis seems perfectly correct. The spoole temp file will get a name when it
rolls over. However, unless you keep a handle to it you have no guarantee it exists on disk. I assume you want to write
it into a temporary location to read it later (perhaps to send it via another channel).

I will opt for a version of your solution 2 ie ensure we have a string on the branche where we have a callable write
attribute. So that code will now look like

if hasattr(getattr(filename, "write",None),'__call__'):
myfile = 0
f = filename
filename = getattr(f,'name',None)
if isinstance(filename,int):
filename = '<os fd:%d>'% filename
elif not isStr(filename): #try to fix bug reported by Robert Schroll <rschroll at gmail.com>
filename = '<%s@0X%8.8X>' % (f.__class__.__name__,id(f))
filename = makeFileName(filename)

it gives us a bit of extra information should we ever see the filename. However, there's a flaw in that as soon as the
file gets rolled over (if it ever does) then it will presumably get a name and it won't be the same.

It does fix the immediate bug though.

thanks again

On 15/03/2021 00:54, Robert Schroll wrote:
> Hi all,
>
.........
.........
> 2. Explicitly cast the name to a string:
> filename = makeFileName(str(filename))
> This would avoid all such problems, at the risk of producing some
> strange-looking filenames. But given that the existing code already
> produces such strange-looking filenames for ints, I suspect this is okay.
>
> Hope this makes sense. Let me know if you have any questions,
> Robert
>
> Example code:
>
> import tempfile
> from reportlab.pdfgen import canvas
>
> f = tempfile.SpooledTemporaryFile()
> c = canvas.Canvas(f)
> c.save() # Raises AttributeError


--
Robin Becker
_______________________________________________
reportlab-users mailing list
reportl...@lists2.reportlab.com
https://pairlist2.pair.net/mailman/listinfo/reportlab-users

Robert Schroll

unread,
Mar 19, 2021, 1:26:27 AM3/19/21
to Robin Becker, reportlab-users
On Mon, Mar 15, 2021 at 2:11 AM Robin Becker <ro...@reportlab.com> wrote:
it gives us a bit of extra information should we ever see the filename. However, there's a flaw in that as soon as the
file gets rolled over (if it ever does) then it will presumably get a name and it won't be the same.

That looks reasonable to me.  In my case, I was looking to use a SpooledTemporaryFile to convey the output of ReportLab to pdfrw.  If the file is small, it's better to keep it all in memory, but if it's large, it'd be better to let it roll over to disk.   The SpooledTemporaryFile should let that happen automatically.  I don't actually care where it's stored, so the filename changing doesn't bother me at all.

Thanks for the quick fix,
Robert

Reply all
Reply to author
Forward
0 new messages