Re: [reportlab-users] [reportlab-support] ReportLab Pylance Bug

13 views
Skip to first unread message

Robin Becker

unread,
Sep 5, 2022, 5:15:19 AMSep 5
to Natalie Jackson, For users of Reportlab open source software
Hi Natalie,

thanks for the report, but as you note the conclusion drawn by Pylance is clearly wrong; perhaps there's something wrong
with the instance of reportlab that you are running so that Pylance concludes there's always an exception thrown.


1) reportlab.graphics.renderbase.Renderer class implements undefined methods eg drawString

2) The function reportlab.graphics.renderPDF.draw is

def draw(drawing, canvas, x, y, showBoundary=rl_config._unset_):
"""As it says"""
R = _PDFRenderer()
R.draw(renderScaledDrawing(drawing), canvas, x, y, showBoundary=showBoundary)

so it is using class reportlab.graphics.renderPDF._PDFRenderer.

3) class _PDFRenderer(Renderer):
.......
inherits from reportlab.graphics.renderbase.Renderer, but as is expected this class does override all the needed methods
including eg
def drawString(self, stringObj):
textRenderMode = getattr(stringObj,'textRenderMode',0)
needFill = textRenderMode in (0,2,4,6)
needStroke = textRenderMode in (1,2,5,6)
.......

so as you find yourself there's no forced exception in renderbase.Renderer

It seems clear that the analysis of bschnurr is wrong. No decorator is needed on _PDFRenderer, standard python
inheritance is sufficient.

If you wish to continue the discussion by all means do so, but please do so at the reportlab-users list which you can
easily join at https://pairlist2.pair.net/mailman/listinfo/reportlab-users.
--
Robin Becker

On 01/09/2022 19:02, Natalie Jackson wrote:
> Hi,
>
> When I call the method reportlab.graphics.renderPDF.draw(), Pylance in VS
> Code interprets the subsequent code as unreachable, though the subsequent
> code runs.
>
> I posted this issue to Pylance issues on github, and a Microsoft member
> responded with some advice, including that I file an issue with the library
> maintainer, hence this message to you now.
>
> Copied from https://github.com/microsoft/pylance-release/issues/3270 :
> natajacks <https://github.com/natajacks> commented 2 hours ago
> <https://github.com/microsoft/pylance-release/issues/3270#issue-1359059212>
> Environment data
>
> - Language Server version: 2022.8.50
> - OS and version: Windows 10 Enterprise, Version 21H
> - Python version (& distribution if applicable, e.g. Anaconda): 3.8.6
> - reportlab version: 3.6.5 (pip install reportlab)
>
> Code Snippet
>
> from reportlab.graphics import renderPDF
> ...
> renderPDF.draw(
> my_reportlab_graphics_object,
> my_canvas,
> 70, # x pixels from left on pdf page (reportlab Canvas)
> 30 # y pixels from bottom on pdf page (reportlab Canvas)
> )
>
> code_after_renderPDFdraw_marked_unreachable_but_it_runs()
> [image: @github-actions] <https://github.com/apps/github-actions>
> github-actions <https://github.com/apps/github-actions> bot added the
> triage-needed
> <https://github.com/microsoft/pylance-release/labels/triage-needed> label 2
> hours ago
> <https://github.com/microsoft/pylance-release/issues/3270#event-7306542796>
> [image: @bschnurr] <https://github.com/bschnurr>
> Member
> bschnurr <https://github.com/bschnurr> commented 1 hour ago
> <https://github.com/microsoft/pylance-release/issues/3270#issuecomment-1234531092>
> •
> edited
>
> thanks for the issue.
>
> seems there are two class
> parent Renderer
> child _PDFRenderer
>
> child's instance calls the parents draw, which calls applyStateChanges and
> the parent's version always throws an exception.
>
> maybe _PDFRenderer is missing an @override decorator
> [image: @bschnurr] <https://github.com/bschnurr>
> Member
> bschnurr <https://github.com/bschnurr> commented 1 hour ago
> <https://github.com/microsoft/pylance-release/issues/3270#issuecomment-1234540153>
> •
> edited
>
> ah..
>
> in renderbase.py
>
> add @abstractmethod to
>
> @abstractmethoddef drawNode(self, node):
> @abstractmethoddef applyStateChanges(self, delta, newState):
>
> and any funciton that calls self.undefined("drawString")
> [image: @bschnurr] <https://github.com/bschnurr>
> Member
> bschnurr <https://github.com/bschnurr> commented 1 hour ago
> <https://github.com/microsoft/pylance-release/issues/3270#issuecomment-1234540949>
>
> please file an issue with the library maintainer
>
>
>
>
>
>
> Let me know if I can provide more information, thanks,
>
>
> - Natalie
>
>
> _______________________________________________
> reportlab-support mailing list
> reportla...@lists.reportlab.com
> http://lists.reportlab.com/cgi-bin/mailman/listinfo/reportlab-support
> **NB** attachments under 750kB please

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

Robin Becker

unread,
Sep 5, 2022, 7:34:44 AMSep 5
to For users of Reportlab open source software

Robin Becker

unread,
Sep 9, 2022, 4:40:13 AMSep 9
to Natalie Jackson, For users of Reportlab open source software
On 09/09/2022 01:31, Natalie Jackson wrote:
> bschnurr <https://github.com/bschnurr> commented 1 minute ago
> <https://github.com/microsoft/pylance-release/issues/3270#issuecomment-1241364028>
>
> I can't remember what kind of exception they throw, but if they throw
> 'UnimplementedError' in the base applyStateChanges instead then we should
> also not treat it as no return.
>
>
,,,,,,,

I'm afraid I will not be adding any decorators to methods to assist any specific analysis tool.

The tool's analysis is just wrong. The _PDFRenderer.draw method is Render.draw is true, but the methods called by that
method are those of _PDFRenderer. I made a small example see at end. Does pylance really think that the final print is
unreachable>? If it does then presumable it doesn't understand that the self in methods may refer to a derived class.

Presumably pylance is unable to figure out that the child class implements some or all of the methods in the base which
it thinks will cause an exception.

The observed truth is that the _PDFrenderer draw method does not always raise an exception.

############################################################
if __name__=='__main__':
class B:
def draw(self):
print(f'B.draw {self.draw}')
self.applyStateChanges()

def undefined(self, operation):
print(f'B.undefined {self.undefined}')
raise ValueError(f'{undefined} in {self.__class__}')

def applyStateChanges(self):
print(f'B.applyStateChanges {self.applyStateChanges}')
self.undefined('applyStateChanges')

class D(B):
def applyStateChanges(self):
print(f'D.applyStateChanges {self.applyStateChanges}')

def draw():
d = D()
d.draw()

draw()
print('draw was called and did not raise an exception')
############################################################
--
Robin Becker
Reply all
Reply to author
Forward
0 new messages