Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crash when linting LeoQt #6438

Closed
edreamleo opened this issue Apr 22, 2022 · 18 comments · Fixed by pylint-dev/astroid#1534
Closed

Crash when linting LeoQt #6438

edreamleo opened this issue Apr 22, 2022 · 18 comments · Fixed by pylint-dev/astroid#1534
Labels
Astroid Related to astroid Crash 💥 A bug that makes pylint crash Needs astroid update Needs an astroid update (probably a release too) before being mergable
Milestone

Comments

@edreamleo
Copy link
Contributor

edreamleo commented Apr 22, 2022

Bug description

pylint crashes as shown below.

Note: Perhaps due to the long bug report I had trouble at first filling in the various sections. I think I have the desired data now, but I did have to create a new section for the program under test.

C:\leo.repo\leo-editor>python -m pylint-leo -f leo\plugins\freewin.py

C:\leo.repo\leo-editor>echo off
pylint: 1 file
************* Module C:\Users\Edward Ream\.leo\pylint-leo-rc.txt
C:\Users\Edward Ream\.leo\pylint-leo-rc.txt:1:0: E0012: Bad option value for --disable. Don't recognize message import. (bad-option-value)
************* Module C:\Users\Edward Ream\.leo\pylint-leo-rc.txt
C:\Users\Edward Ream\.leo\pylint-leo-rc.txt:1:0: E0015: Unrecognized option found: cache-size, files-output, disable-report, inlinvar-rgx, bad-functions, acquired-members (unrecognized-option)
************* Module leo.plugins.freewin
leo\plugins\freewin.py:778:8: E1120: No value for argument 'slot' in method call (no-value-for-parameter)
Exception on node <FunctionDef.keyPressEvent l.909 at 0x12474830b80> in file 'C:\leo.repo\leo-editor\leo\plugins\freewin.py'
Traceback (most recent call last):
  File "C:\Users\Edward Ream\Python\Python310\lib\site-packages\pylint-2.14.0.dev0-py3.10.egg\pylint\utils\ast_walker.py", line 82, in walk
    callback(astroid)
  File "C:\Users\Edward Ream\Python\Python310\lib\site-packages\pylint-2.14.0.dev0-py3.10.egg\pylint\checkers\classes\class_checker.py", line 1180, in visit_functiondef
    for obj in ancestor.lookup(node.name)[1]:
  File "C:\Users\Edward Ream\Python\Python310\lib\site-packages\astroid\nodes\node_classes.py", line 385, in lookup
    return self.scope().scope_lookup(self, name)
  File "C:\Users\Edward Ream\Python\Python310\lib\site-packages\astroid\nodes\scoped_nodes\scoped_nodes.py", line 2377, in scope_lookup
    return frame._scope_lookup(node, name, offset)
  File "C:\Users\Edward Ream\Python\Python310\lib\site-packages\astroid\nodes\scoped_nodes\mixin.py", line 54, in _scope_lookup
    stmts = _filter_stmts(node, self.locals[name], self, offset)
  File "C:\Users\Edward Ream\Python\Python310\lib\site-packages\astroid\filter_statements.py", line 115, in _filter_stmts
    assert hasattr(node, "assign_type"), (
AssertionError: (<EmptyNode.keyPressEvent l.0 at 0x124765be950>, <ClassDef.QMainWindow l.0 at 0x124765723e0>, {'__module__': [<Const.str l.0 at 0x12476572410>], '__qualname__': [<Const.str l.0 at 0x12476572440>], 'AllowNestedDocks': [<EmptyNode.AllowNestedDocks l.0 at 0x124765724d0>], 'AllowTabbedDocks': [<EmptyNode.AllowTabbedDocks l.0 at 0x12476572500>], 'AnimatedDocks': [<EmptyNode.AnimatedDocks l.0 at 0x12476572530>], 'DockOption': [<ClassDef.DockOption l.0 at 0x12476572590>], 'DockOptions': [<ClassDef.DockOptions l.0 at 0x12476573a30>], 'DrawChildren': [<EmptyNode.DrawChildren l.0 at 0x12476572560>], 'DrawWindowBackground': [<EmptyNode.DrawWindowBackground l.0 at 0x1247658a980>], 'ForceTabbedDocks': [<EmptyNode.ForceTabbedDocks l.0 at 0x1247658abf0>], 'GroupedDragging': [<EmptyNode.GroupedDragging l.0 at 0x1247658ac20>], 'IgnoreMask': [<EmptyNode.IgnoreMask l.0 at 0x1247658ac50>], 'PaintDeviceMetric': [<EmptyNode.PaintDeviceMetric l.0 at 0x1247658ac80>], 'PdmDepth': [<EmptyNode.PdmDepth l.0 at 0x1247658acb0>], 'PdmDevicePixelRatio': [<EmptyNode.PdmDevicePixelRatio l.0 at 0x1247658ace0>], 'PdmDevicePixelRatioScaled': [<EmptyNode.PdmDevicePixelRatioScaled l.0 at 0x1247658ad10>], 'PdmDpiX': [<EmptyNode.PdmDpiX l.0 at 0x1247658ad40>], 'PdmDpiY': [<EmptyNode.PdmDpiY l.0 at 0x1247658ad70>], 'PdmHeight': [<EmptyNode.PdmHeight l.0 at 0x1247658ada0>], 'PdmHeightMM': [<EmptyNode.PdmHeightMM l.0 at 0x1247658add0>], 'PdmNumColors': [<EmptyNode.PdmNumColors l.0 at 0x1247658ae00>], 'PdmPhysicalDpiX': [<EmptyNode.PdmPhysicalDpiX l.0 at 0x1247658ae30>], 'PdmPhysicalDpiY': [<EmptyNode.PdmPhysicalDpiY l.0 at 0x1247658ae60>], 'PdmWidth': [<EmptyNode.PdmWidth l.0 at 0x1247658ae90>], 'PdmWidthMM': [<EmptyNode.PdmWidthMM l.0 at 0x1247658aec0>], 'RenderFlag': [<ClassDef.RenderFlag l.0 at 0x12474ac5840>], 'RenderFlags': [<ClassDef.RenderFlags l.0 at 0x12474ac6ce0>], 'VerticalTabs': [<EmptyNode.VerticalTabs l.0 at 0x1247658aef0>], '__class__': [<ImportFrom.__class__ l.0 at 0x1247658af20>], '__delattr__': [<FunctionDef.__delattr__ l.None at 0x1247658af80>], '__dict__': [<EmptyNode.__dict__ l.0 at 0x1247658af50>], '__dir__': [<FunctionDef.__dir__ l.None at 0x1247658b160>], '__eq__': [<FunctionDef.__eq__ l.None at 0x1247658b2e0>], '__format__': [<FunctionDef.__format__ l.None at 0x1247658b460>], '__ge__': [<FunctionDef.__ge__ l.None at 0x1247658b5e0>], '__getattr__': [<EmptyNode.__getattr__ l.0 at 0x1247658b130>], '__getattribute__': [<FunctionDef.__getattribute__ l.None at 0x1247658b790>], '__gt__': [<FunctionDef.__gt__ l.None at 0x1247658b910>], '__hash__': [<FunctionDef.__hash__ l.None at 0x1247658ba90>], '__init__': [<FunctionDef.__init__ l.None at 0x1247658bc10>], '__init_subclass__': [<EmptyNode.__init_subclass__ l.0 at 0x1247658b760>], '__le__': [<FunctionDef.__le__ l.None at 0x1247658bdc0>], '__lt__': [<FunctionDef.__lt__ l.None at 0x1247658bf40>], '__ne__': [<FunctionDef.__ne__ l.None at 0x124765bc100>], '__new__': [<ImportFrom.__new__ l.0 at 0x1247658bd90>], '__reduce__': [<FunctionDef.__reduce__ l.None at 0x124765bc2b0>], '__reduce_ex__': [<FunctionDef.__reduce_ex__ l.None at 0x124765bc430>], '__repr__': [<FunctionDef.__repr__ l.None at 0x124765bc5b0>], '__setattr__': [<FunctionDef.__setattr__ l.None at 0x124765bc730>], '__sizeof__': [<FunctionDef.__sizeof__ l.None at 0x124765bc8b0>], '__str__': [<FunctionDef.__str__ l.None at 0x124765bca30>], '__subclasshook__': [<ImportFrom.__subclasshook__ l.0 at 0x124765bc280>], '__weakref__': [<ClassDef.__weakref__ l.0 at 0x124765bcbe0>], 'acceptDrops': [<EmptyNode.acceptDrops l.0 at 0x124765bcbb0>], 'accessibleDescription': [<EmptyNode.accessibleDescription l.0 at 0x124765bcca0>], 'accessibleName': [<EmptyNode.accessibleName l.0 at 0x124765bccd0>], 'actionEvent': [<EmptyNode.actionEvent l.0 at 0x124765bcd00>], 'actions': [<EmptyNode.actions l.0 at 0x124765bcd30>], 'activateWindow': [<EmptyNode.activateWindow l.0 at 0x124765bcd60>], 'addAction': [<EmptyNode.addAction l.0 at 0x124765bcd90>], 'addActions': [<EmptyNode.addActions l.0 at 0x124765bcdc0>], 'addDockWidget': [<EmptyNode.addDockWidget l.0 at 0x124765bcdf0>], 'addToolBar': [<EmptyNode.addToolBar l.0 at 0x124765bce20>], 'addToolBarBreak': [<EmptyNode.addToolBarBreak l.0 at 0x124765bce50>], 'adjustSize': [<EmptyNode.adjustSize l.0 at 0x124765bce80>], 'autoFillBackground': [<EmptyNode.autoFillBackground l.0 at 0x124765bceb0>], 'backgroundRole': [<EmptyNode.backgroundRole l.0 at 0x124765bcee0>], 'baseSize': [<EmptyNode.baseSize l.0 at 0x124765bcf10>], 'blockSignals': [<EmptyNode.blockSignals l.0 at 0x124765bcf40>], 'centralWidget': [<EmptyNode.centralWidget l.0 at 0x124765bcf70>], 'changeEvent': [<EmptyNode.changeEvent l.0 at 0x124765bcfa0>], 'childAt': [<EmptyNode.childAt l.0 at 0x124765bcfd0>], 'childEvent': [<EmptyNode.childEvent l.0 at 0x124765bd000>], 'children': [<EmptyNode.children l.0 at 0x124765bd030>], 'childrenRect': [<EmptyNode.childrenRect l.0 at 0x124765bd060>], 'childrenRegion': [<EmptyNode.childrenRegion l.0 at 0x124765bd090>], 'clearFocus': [<EmptyNode.clearFocus l.0 at 0x124765bd0c0>], 'clearMask': [<EmptyNode.clearMask l.0 at 0x124765bd0f0>], 'close': [<EmptyNode.close l.0 at 0x124765bd120>], 'closeEvent': [<EmptyNode.closeEvent l.0 at 0x124765bd150>], 'colorCount': [<EmptyNode.colorCount l.0 at 0x124765bd180>], 'connectNotify': [<EmptyNode.connectNotify l.0 at 0x124765bd1b0>], 'contentsMargins': [<EmptyNode.contentsMargins l.0 at 0x124765bd1e0>], 'contentsRect': [<EmptyNode.contentsRect l.0 at 0x124765bd210>], 'contextMenuEvent': [<EmptyNode.contextMenuEvent l.0 at 0x124765bd240>], 'contextMenuPolicy': [<EmptyNode.contextMenuPolicy l.0 at 0x124765bd270>], 'corner': [<EmptyNode.corner l.0 at 0x124765bd2a0>], 'create': [<EmptyNode.create l.0 at 0x124765bd2d0>], 'createPopupMenu': [<EmptyNode.createPopupMenu l.0 at 0x124765bd300>], 'createWindowContainer': [<EmptyNode.createWindowContainer l.0 at 0x124765bd330>], 'cursor': [<EmptyNode.cursor l.0 at 0x124765bd360>], 'customContextMenuRequested': [<FunctionDef.customContextMenuRequested l.None at 0x124765bd3c0>], 'customEvent': [<EmptyNode.customEvent l.0 at 0x124765bd390>], 'deleteLater': [<EmptyNode.deleteLater l.0 at 0x124765bd540>], 'depth': [<EmptyNode.depth l.0 at 0x124765bd570>], 'destroy': [<EmptyNode.destroy l.0 at 0x124765bd5a0>], 'destroyed': [<FunctionDef.destroyed l.None at 0x124765bd600>], 'devType': [<EmptyNode.devType l.0 at 0x124765bd5d0>], 'devicePixelRatio': [<EmptyNode.devicePixelRatio l.0 at 0x124765bd780>], 'devicePixelRatioF': [<EmptyNode.devicePixelRatioF l.0 at 0x124765bd7b0>], 'devicePixelRatioFScale': [<EmptyNode.devicePixelRatioFScale l.0 at 0x124765bd7e0>], 'disconnect': [<EmptyNode.disconnect l.0 at 0x124765bd810>], 'disconnectNotify': [<EmptyNode.disconnectNotify l.0 at 0x124765bd840>], 'dockOptions': [<EmptyNode.dockOptions l.0 at 0x124765bd870>], 'dockWidgetArea': [<EmptyNode.dockWidgetArea l.0 at 0x124765bd8a0>], 'documentMode': [<EmptyNode.documentMode l.0 at 0x124765bd8d0>], 'dragEnterEvent': [<EmptyNode.dragEnterEvent l.0 at 0x124765bd900>], 'dragLeaveEvent': [<EmptyNode.dragLeaveEvent l.0 at 0x124765bd930>], 'dragMoveEvent': [<EmptyNode.dragMoveEvent l.0 at 0x124765bd960>], 'dropEvent': [<EmptyNode.dropEvent l.0 at 0x124765bd990>], 'dumpObjectInfo': [<EmptyNode.dumpObjectInfo l.0 at 0x124765bd9c0>], 'dumpObjectTree': [<EmptyNode.dumpObjectTree l.0 at 0x124765bd9f0>], 'dynamicPropertyNames': [<EmptyNode.dynamicPropertyNames l.0 at 0x124765bda20>], 'effectiveWinId': [<EmptyNode.effectiveWinId l.0 at 0x124765bda50>], 'ensurePolished': [<EmptyNode.ensurePolished l.0 at 0x124765bda80>], 'enterEvent': [<EmptyNode.enterEvent l.0 at 0x124765bdab0>], 'event': [<EmptyNode.event l.0 at 0x124765bdae0>], 'eventFilter': [<EmptyNode.eventFilter l.0 at 0x124765bdb10>], 'find': [<EmptyNode.find l.0 at 0x124765bdb40>], 'findChild': [<EmptyNode.findChild l.0 at 0x124765bdb70>], 'findChildren': [<EmptyNode.findChildren l.0 at 0x124765bdba0>], 'focusInEvent': [<EmptyNode.focusInEvent l.0 at 0x124765bdbd0>], 'focusNextChild': [<EmptyNode.focusNextChild l.0 at 0x124765bdc00>], 'focusNextPrevChild': [<EmptyNode.focusNextPrevChild l.0 at 0x124765bdc30>], 'focusOutEvent': [<EmptyNode.focusOutEvent l.0 at 0x124765bdc60>], 'focusPolicy': [<EmptyNode.focusPolicy l.0 at 0x124765bdc90>], 'focusPreviousChild': [<EmptyNode.focusPreviousChild l.0 at 0x124765bdcc0>], 'focusProxy': [<EmptyNode.focusProxy l.0 at 0x124765bdcf0>], 'focusWidget': [<EmptyNode.focusWidget l.0 at 0x124765bdd20>], 'font': [<EmptyNode.font l.0 at 0x124765bdd50>], 'fontInfo': [<EmptyNode.fontInfo l.0 at 0x124765bdd80>], 'fontMetrics': [<EmptyNode.fontMetrics l.0 at 0x124765bddb0>], 'foregroundRole': [<EmptyNode.foregroundRole l.0 at 0x124765bdde0>], 'frameGeometry': [<EmptyNode.frameGeometry l.0 at 0x124765bde10>], 'frameSize': [<EmptyNode.frameSize l.0 at 0x124765bde40>], 'geometry': [<EmptyNode.geometry l.0 at 0x124765bde70>], 'getContentsMargins': [<EmptyNode.getContentsMargins l.0 at 0x124765bdea0>], 'grab': [<EmptyNode.grab l.0 at 0x124765bded0>], 'grabGesture': [<EmptyNode.grabGesture l.0 at 0x124765bdf00>], 'grabKeyboard': [<EmptyNode.grabKeyboard l.0 at 0x124765bdf30>], 'grabMouse': [<EmptyNode.grabMouse l.0 at 0x124765bdf60>], 'grabShortcut': [<EmptyNode.grabShortcut l.0 at 0x124765bdf90>], 'graphicsEffect': [<EmptyNode.graphicsEffect l.0 at 0x124765bdfc0>], 'graphicsProxyWidget': [<EmptyNode.graphicsProxyWidget l.0 at 0x124765bdff0>], 'hasFocus': [<EmptyNode.hasFocus l.0 at 0x124765be020>], 'hasHeightForWidth': [<EmptyNode.hasHeightForWidth l.0 at 0x124765be050>], 'hasMouseTracking': [<EmptyNode.hasMouseTracking l.0 at 0x124765be080>], 'hasTabletTracking': [<EmptyNode.hasTabletTracking l.0 at 0x124765be0b0>], 'height': [<EmptyNode.height l.0 at 0x124765be0e0>], 'heightForWidth': [<EmptyNode.heightForWidth l.0 at 0x124765be110>], 'heightMM': [<EmptyNode.heightMM l.0 at 0x124765be140>], 'hide': [<EmptyNode.hide l.0 at 0x124765be170>], 'hideEvent': [<EmptyNode.hideEvent l.0 at 0x124765be1a0>], 'iconSize': [<EmptyNode.iconSize l.0 at 0x124765be1d0>], 'iconSizeChanged': [<FunctionDef.iconSizeChanged l.None at 0x124765be230>], 'inherits': [<EmptyNode.inherits l.0 at 0x124765be200>], 'initPainter': [<EmptyNode.initPainter l.0 at 0x124765be3b0>], 'inputMethodEvent': [<EmptyNode.inputMethodEvent l.0 at 0x124765be3e0>], 'inputMethodHints': [<EmptyNode.inputMethodHints l.0 at 0x124765be410>], 'inputMethodQuery': [<EmptyNode.inputMethodQuery l.0 at 0x124765be440>], 'insertAction': [<EmptyNode.insertAction l.0 at 0x124765be470>], 'insertActions': [<EmptyNode.insertActions l.0 at 0x124765be4a0>], 'insertToolBar': [<EmptyNode.insertToolBar l.0 at 0x124765be4d0>], 'insertToolBarBreak': [<EmptyNode.insertToolBarBreak l.0 at 0x124765be500>], 'installEventFilter': [<EmptyNode.installEventFilter l.0 at 0x124765be530>], 'isActiveWindow': [<EmptyNode.isActiveWindow l.0 at 0x124765be560>], 'isAncestorOf': [<EmptyNode.isAncestorOf l.0 at 0x124765be590>], 'isAnimated': [<EmptyNode.isAnimated l.0 at 0x124765be5c0>], 'isDockNestingEnabled': [<EmptyNode.isDockNestingEnabled l.0 at 0x124765be5f0>], 'isEnabled': [<EmptyNode.isEnabled l.0 at 0x124765be620>], 'isEnabledTo': [<EmptyNode.isEnabledTo l.0 at 0x124765be650>], 'isFullScreen': [<EmptyNode.isFullScreen l.0 at 0x124765be680>], 'isHidden': [<EmptyNode.isHidden l.0 at 0x124765be6b0>], 'isLeftToRight': [<EmptyNode.isLeftToRight l.0 at 0x124765be6e0>], 'isMaximized': [<EmptyNode.isMaximized l.0 at 0x124765be710>], 'isMinimized': [<EmptyNode.isMinimized l.0 at 0x124765be740>], 'isModal': [<EmptyNode.isModal l.0 at 0x124765be770>], 'isRightToLeft': [<EmptyNode.isRightToLeft l.0 at 0x124765be7a0>], 'isSeparator': [<EmptyNode.isSeparator l.0 at 0x124765be7d0>], 'isSignalConnected': [<EmptyNode.isSignalConnected l.0 at 0x124765be800>], 'isVisible': [<EmptyNode.isVisible l.0 at 0x124765be830>], 'isVisibleTo': [<EmptyNode.isVisibleTo l.0 at 0x124765be860>], 'isWidgetType': [<EmptyNode.isWidgetType l.0 at 0x124765be890>], 'isWindow': [<EmptyNode.isWindow l.0 at 0x124765be8c0>], 'isWindowModified': [<EmptyNode.isWindowModified l.0 at 0x124765be8f0>], 'isWindowType': [<EmptyNode.isWindowType l.0 at 0x124765be920>], 'keyPressEvent': [<EmptyNode.keyPressEvent l.0 at 0x124765be950>], 'keyReleaseEvent': [<EmptyNode.keyReleaseEvent l.0 at 0x124765be980>], 'keyboardGrabber': [<EmptyNode.keyboardGrabber l.0 at 0x124765be9b0>], 'killTimer': [<EmptyNode.killTimer l.0 at 0x124765be9e0>], 'layout': [<EmptyNode.layout l.0 at 0x124765bea10>], 'layoutDirection': [<EmptyNode.layoutDirection l.0 at 0x124765bea40>], 'leaveEvent': [<EmptyNode.leaveEvent l.0 at 0x124765bea70>], 'locale': [<EmptyNode.locale l.0 at 0x124765beaa0>], 'logicalDpiX': [<EmptyNode.logicalDpiX l.0 at 0x124765bead0>], 'logicalDpiY': [<EmptyNode.logicalDpiY l.0 at 0x124765beb00>], 'lower': [<EmptyNode.lower l.0 at 0x124765beb30>], 'mapFrom': [<EmptyNode.mapFrom l.0 at 0x124765beb60>], 'mapFromGlobal': [<EmptyNode.mapFromGlobal l.0 at 0x124765beb90>], 'mapFromParent': [<EmptyNode.mapFromParent l.0 at 0x124765bebc0>], 'mapTo': [<EmptyNode.mapTo l.0 at 0x124765bebf0>], 'mapToGlobal': [<EmptyNode.mapToGlobal l.0 at 0x124765bec20>], 'mapToParent': [<EmptyNode.mapToParent l.0 at 0x124765bec50>], 'mask': [<EmptyNode.mask l.0 at 0x124765bec80>], 'maximumHeight': [<EmptyNode.maximumHeight l.0 at 0x124765becb0>], 'maximumSize': [<EmptyNode.maximumSize l.0 at 0x124765bece0>], 'maximumWidth': [<EmptyNode.maximumWidth l.0 at 0x124765bed10>], 'menuBar': [<EmptyNode.menuBar l.0 at 0x124765bed40>], 'menuWidget': [<EmptyNode.menuWidget l.0 at 0x124765bed70>], 'metaObject': [<EmptyNode.metaObject l.0 at 0x124765beda0>], 'metric': [<EmptyNode.metric l.0 at 0x124765bedd0>], 'minimumHeight': [<EmptyNode.minimumHeight l.0 at 0x124765bee00>], 'minimumSize': [<EmptyNode.minimumSize l.0 at 0x124765bee30>], 'minimumSizeHint': [<EmptyNode.minimumSizeHint l.0 at 0x124765bee60>], 'minimumWidth': [<EmptyNode.minimumWidth l.0 at 0x124765bee90>], 'mouseDoubleClickEvent': [<EmptyNode.mouseDoubleClickEvent l.0 at 0x124765beec0>], 'mouseGrabber': [<EmptyNode.mouseGrabber l.0 at 0x124765beef0>], 'mouseMoveEvent': [<EmptyNode.mouseMoveEvent l.0 at 0x124765bef20>], 'mousePressEvent': [<EmptyNode.mousePressEvent l.0 at 0x124765bef50>], 'mouseReleaseEvent': [<EmptyNode.mouseReleaseEvent l.0 at 0x124765bef80>], 'move': [<EmptyNode.move l.0 at 0x124765befb0>], 'moveEvent': [<EmptyNode.moveEvent l.0 at 0x124765befe0>], 'moveToThread': [<EmptyNode.moveToThread l.0 at 0x124765bf010>], 'nativeEvent': [<EmptyNode.nativeEvent l.0 at 0x124765bf040>], 'nativeParentWidget': [<EmptyNode.nativeParentWidget l.0 at 0x124765bf070>], 'nextInFocusChain': [<EmptyNode.nextInFocusChain l.0 at 0x124765bf0a0>], 'normalGeometry': [<EmptyNode.normalGeometry l.0 at 0x124765bf0d0>], 'objectName': [<EmptyNode.objectName l.0 at 0x124765bf100>], 'objectNameChanged': [<FunctionDef.objectNameChanged l.None at 0x124765bf160>], 'overrideWindowFlags': [<EmptyNode.overrideWindowFlags l.0 at 0x124765bf130>], 'overrideWindowState': [<EmptyNode.overrideWindowState l.0 at 0x124765bf2e0>], 'paintEngine': [<EmptyNode.paintEngine l.0 at 0x124765bf310>], 'paintEvent': [<EmptyNode.paintEvent l.0 at 0x124765bf340>], 'paintingActive': [<EmptyNode.paintingActive l.0 at 0x124765bf370>], 'palette': [<EmptyNode.palette l.0 at 0x124765bf3a0>], 'parent': [<EmptyNode.parent l.0 at 0x124765bf3d0>], 'parentWidget': [<EmptyNode.parentWidget l.0 at 0x124765bf400>], 'physicalDpiX': [<EmptyNode.physicalDpiX l.0 at 0x124765bf430>], 'physicalDpiY': [<EmptyNode.physicalDpiY l.0 at 0x124765bf460>], 'pos': [<EmptyNode.pos l.0 at 0x124765bf490>], 'previousInFocusChain': [<EmptyNode.previousInFocusChain l.0 at 0x124765bf4c0>], 'property': [<EmptyNode.property l.0 at 0x124765bf4f0>], 'pyqtConfigure': [<EmptyNode.pyqtConfigure l.0 at 0x124765bf520>], 'raise_': [<EmptyNode.raise_ l.0 at 0x124765bf550>], 'receivers': [<EmptyNode.receivers l.0 at 0x124765bf580>], 'rect': [<EmptyNode.rect l.0 at 0x124765bf5b0>], 'releaseKeyboard': [<EmptyNode.releaseKeyboard l.0 at 0x124765bf5e0>], 'releaseMouse': [<EmptyNode.releaseMouse l.0 at 0x124765bf610>], 'releaseShortcut': [<EmptyNode.releaseShortcut l.0 at 0x124765bf640>], 'removeAction': [<EmptyNode.removeAction l.0 at 0x124765bf670>], 'removeDockWidget': [<EmptyNode.removeDockWidget l.0 at 0x124765bf6a0>], 'removeEventFilter': [<EmptyNode.removeEventFilter l.0 at 0x124765bf6d0>], 'removeToolBar': [<EmptyNode.removeToolBar l.0 at 0x124765bf700>], 'removeToolBarBreak': [<EmptyNode.removeToolBarBreak l.0 at 0x124765bf730>], 'render': [<EmptyNode.render l.0 at 0x124765bf760>], 'repaint': [<EmptyNode.repaint l.0 at 0x124765bf790>], 'resize': [<EmptyNode.resize l.0 at 0x124765bf7c0>], 'resizeDocks': [<EmptyNode.resizeDocks l.0 at 0x124765bf7f0>], 'resizeEvent': [<EmptyNode.resizeEvent l.0 at 0x124765bf820>], 'restoreDockWidget': [<EmptyNode.restoreDockWidget l.0 at 0x124765bf850>], 'restoreGeometry': [<EmptyNode.restoreGeometry l.0 at 0x124765bf880>], 'restoreState': [<EmptyNode.restoreState l.0 at 0x124765bf8b0>], 'saveGeometry': [<EmptyNode.saveGeometry l.0 at 0x124765bf8e0>], 'saveState': [<EmptyNode.saveState l.0 at 0x124765bf910>], 'screen': [<EmptyNode.screen l.0 at 0x124765bf940>], 'scroll': [<EmptyNode.scroll l.0 at 0x124765bf970>], 'sender': [<EmptyNode.sender l.0 at 0x124765bf9a0>], 'senderSignalIndex': [<EmptyNode.senderSignalIndex l.0 at 0x124765bf9d0>], 'setAcceptDrops': [<EmptyNode.setAcceptDrops l.0 at 0x124765bfa00>], 'setAccessibleDescription': [<EmptyNode.setAccessibleDescription l.0 at 0x124765bfa30>], 'setAccessibleName': [<EmptyNode.setAccessibleName l.0 at 0x124765bfa60>], 'setAnimated': [<EmptyNode.setAnimated l.0 at 0x124765bfa90>], 'setAttribute': [<EmptyNode.setAttribute l.0 at 0x124765bfac0>], 'setAutoFillBackground': [<EmptyNode.setAutoFillBackground l.0 at 0x124765bfaf0>], 'setBackgroundRole': [<EmptyNode.setBackgroundRole l.0 at 0x124765bfb20>], 'setBaseSize': [<EmptyNode.setBaseSize l.0 at 0x124765bfb50>], 'setCentralWidget': [<EmptyNode.setCentralWidget l.0 at 0x124765bfb80>], 'setContentsMargins': [<EmptyNode.setContentsMargins l.0 at 0x124765bfbb0>], 'setContextMenuPolicy': [<EmptyNode.setContextMenuPolicy l.0 at 0x124765bfbe0>], 'setCorner': [<EmptyNode.setCorner l.0 at 0x124765bfc10>], 'setCursor': [<EmptyNode.setCursor l.0 at 0x124765bfc40>], 'setDisabled': [<EmptyNode.setDisabled l.0 at 0x124765bfc70>], 'setDockNestingEnabled': [<EmptyNode.setDockNestingEnabled l.0 at 0x124765bfca0>], 'setDockOptions': [<EmptyNode.setDockOptions l.0 at 0x124765bfcd0>], 'setDocumentMode': [<EmptyNode.setDocumentMode l.0 at 0x124765bfd00>], 'setEnabled': [<EmptyNode.setEnabled l.0 at 0x124765bfd30>], 'setFixedHeight': [<EmptyNode.setFixedHeight l.0 at 0x124765bfd60>], 'setFixedSize': [<EmptyNode.setFixedSize l.0 at 0x124765bfd90>], 'setFixedWidth': [<EmptyNode.setFixedWidth l.0 at 0x124765bfdc0>], 'setFocus': [<EmptyNode.setFocus l.0 at 0x124765bfdf0>], 'setFocusPolicy': [<EmptyNode.setFocusPolicy l.0 at 0x124765bfe20>], 'setFocusProxy': [<EmptyNode.setFocusProxy l.0 at 0x124765bfe50>], 'setFont': [<EmptyNode.setFont l.0 at 0x124765bfe80>], 'setForegroundRole': [<EmptyNode.setForegroundRole l.0 at 0x124765bfeb0>], 'setGeometry': [<EmptyNode.setGeometry l.0 at 0x124765bfee0>], 'setGraphicsEffect': [<EmptyNode.setGraphicsEffect l.0 at 0x124765bff10>], 'setHidden': [<EmptyNode.setHidden l.0 at 0x124765bff40>], 'setIconSize': [<EmptyNode.setIconSize l.0 at 0x124765bff70>], 'setInputMethodHints': [<EmptyNode.setInputMethodHints l.0 at 0x124765bffa0>], 'setLayout': [<EmptyNode.setLayout l.0 at 0x124765bffd0>], 'setLayoutDirection': [<EmptyNode.setLayoutDirection l.0 at 0x124765dc040>], 'setLocale': [<EmptyNode.setLocale l.0 at 0x124765dc070>], 'setMask': [<EmptyNode.setMask l.0 at 0x124765dc0a0>], 'setMaximumHeight': [<EmptyNode.setMaximumHeight l.0 at 0x124765dc0d0>], 'setMaximumSize': [<EmptyNode.setMaximumSize l.0 at 0x124765dc100>], 'setMaximumWidth': [<EmptyNode.setMaximumWidth l.0 at 0x124765dc130>], 'setMenuBar': [<EmptyNode.setMenuBar l.0 at 0x124765dc160>], 'setMenuWidget': [<EmptyNode.setMenuWidget l.0 at 0x124765dc190>], 'setMinimumHeight': [<EmptyNode.setMinimumHeight l.0 at 0x124765dc1c0>], 'setMinimumSize': [<EmptyNode.setMinimumSize l.0 at 0x124765dc1f0>], 'setMinimumWidth': [<EmptyNode.setMinimumWidth l.0 at 0x124765dc220>], 'setMouseTracking': [<EmptyNode.setMouseTracking l.0 at 0x124765dc250>], 'setObjectName': [<EmptyNode.setObjectName l.0 at 0x124765dc280>], 'setPalette': [<EmptyNode.setPalette l.0 at 0x124765dc2b0>], 'setParent': [<EmptyNode.setParent l.0 at 0x124765dc2e0>], 'setProperty': [<EmptyNode.setProperty l.0 at 0x124765dc310>], 'setShortcutAutoRepeat': [<EmptyNode.setShortcutAutoRepeat l.0 at 0x124765dc340>], 'setShortcutEnabled': [<EmptyNode.setShortcutEnabled l.0 at 0x124765dc370>], 'setSizeIncrement': [<EmptyNode.setSizeIncrement l.0 at 0x124765dc3a0>], 'setSizePolicy': [<EmptyNode.setSizePolicy l.0 at 0x124765dc3d0>], 'setStatusBar': [<EmptyNode.setStatusBar l.0 at 0x124765dc400>], 'setStatusTip': [<EmptyNode.setStatusTip l.0 at 0x124765dc430>], 'setStyle': [<EmptyNode.setStyle l.0 at 0x124765dc460>], 'setStyleSheet': [<EmptyNode.setStyleSheet l.0 at 0x124765dc490>], 'setTabOrder': [<EmptyNode.setTabOrder l.0 at 0x124765dc4c0>], 'setTabPosition': [<EmptyNode.setTabPosition l.0 at 0x124765dc4f0>], 'setTabShape': [<EmptyNode.setTabShape l.0 at 0x124765dc520>], 'setTabletTracking': [<EmptyNode.setTabletTracking l.0 at 0x124765dc550>], 'setToolButtonStyle': [<EmptyNode.setToolButtonStyle l.0 at 0x124765dc580>], 'setToolTip': [<EmptyNode.setToolTip l.0 at 0x124765dc5b0>], 'setToolTipDuration': [<EmptyNode.setToolTipDuration l.0 at 0x124765dc5e0>], 'setUnifiedTitleAndToolBarOnMac': [<EmptyNode.setUnifiedTitleAndToolBarOnMac l.0 at 0x124765dc610>], 'setUpdatesEnabled': [<EmptyNode.setUpdatesEnabled l.0 at 0x124765dc640>], 'setVisible': [<EmptyNode.setVisible l.0 at 0x124765dc670>], 'setWhatsThis': [<EmptyNode.setWhatsThis l.0 at 0x124765dc6a0>], 'setWindowFilePath': [<EmptyNode.setWindowFilePath l.0 at 0x124765dc6d0>], 'setWindowFlag': [<EmptyNode.setWindowFlag l.0 at 0x124765dc700>], 'setWindowFlags': [<EmptyNode.setWindowFlags l.0 at 0x124765dc730>], 'setWindowIcon': [<EmptyNode.setWindowIcon l.0 at 0x124765dc760>], 'setWindowIconText': [<EmptyNode.setWindowIconText l.0 at 0x124765dc790>], 'setWindowModality': [<EmptyNode.setWindowModality l.0 at 0x124765dc7c0>], 'setWindowModified': [<EmptyNode.setWindowModified l.0 at 0x124765dc7f0>], 'setWindowOpacity': [<EmptyNode.setWindowOpacity l.0 at 0x124765dc820>], 'setWindowRole': [<EmptyNode.setWindowRole l.0 at 0x124765dc850>], 'setWindowState': [<EmptyNode.setWindowState l.0 at 0x124765dc880>], 'setWindowTitle': [<EmptyNode.setWindowTitle l.0 at 0x124765dc8b0>], 'sharedPainter': [<EmptyNode.sharedPainter l.0 at 0x124765dc8e0>], 'show': [<EmptyNode.show l.0 at 0x124765dc910>], 'showEvent': [<EmptyNode.showEvent l.0 at 0x124765dc940>], 'showFullScreen': [<EmptyNode.showFullScreen l.0 at 0x124765dc970>], 'showMaximized': [<EmptyNode.showMaximized l.0 at 0x124765dc9a0>], 'showMinimized': [<EmptyNode.showMinimized l.0 at 0x124765dc9d0>], 'showNormal': [<EmptyNode.showNormal l.0 at 0x124765dca00>], 'signalsBlocked': [<EmptyNode.signalsBlocked l.0 at 0x124765dca30>], 'size': [<EmptyNode.size l.0 at 0x124765dca60>], 'sizeHint': [<EmptyNode.sizeHint l.0 at 0x124765dca90>], 'sizeIncrement': [<EmptyNode.sizeIncrement l.0 at 0x124765dcac0>], 'sizePolicy': [<EmptyNode.sizePolicy l.0 at 0x124765dcaf0>], 'splitDockWidget': [<EmptyNode.splitDockWidget l.0 at 0x124765dcb20>], 'stackUnder': [<EmptyNode.stackUnder l.0 at 0x124765dcb50>], 'startTimer': [<EmptyNode.startTimer l.0 at 0x124765dcb80>], 'staticMetaObject': [<EmptyNode.staticMetaObject l.0 at 0x124765dcbb0>], 'statusBar': [<EmptyNode.statusBar l.0 at 0x124765dcbe0>], 'statusTip': [<EmptyNode.statusTip l.0 at 0x124765dcc10>], 'style': [<EmptyNode.style l.0 at 0x124765dcc40>], 'styleSheet': [<EmptyNode.styleSheet l.0 at 0x124765dcc70>], 'tabPosition': [<EmptyNode.tabPosition l.0 at 0x124765dcca0>], 'tabShape': [<EmptyNode.tabShape l.0 at 0x124765dccd0>], 'tabifiedDockWidgetActivated': [<FunctionDef.tabifiedDockWidgetActivated l.None at 0x124765dcd30>], 'tabifiedDockWidgets': [<EmptyNode.tabifiedDockWidgets l.0 at 0x124765dcd00>], 'tabifyDockWidget': [<EmptyNode.tabifyDockWidget l.0 at 0x124765dceb0>], 'tabletEvent': [<EmptyNode.tabletEvent l.0 at 0x124765dcee0>], 'takeCentralWidget': [<EmptyNode.takeCentralWidget l.0 at 0x124765dcf10>], 'testAttribute': [<EmptyNode.testAttribute l.0 at 0x124765dcf40>], 'thread': [<EmptyNode.thread l.0 at 0x124765dcf70>], 'timerEvent': [<EmptyNode.timerEvent l.0 at 0x124765dcfa0>], 'toolBarArea': [<EmptyNode.toolBarArea l.0 at 0x124765dcfd0>], 'toolBarBreak': [<EmptyNode.toolBarBreak l.0 at 0x124765dd000>], 'toolButtonStyle': [<EmptyNode.toolButtonStyle l.0 at 0x124765dd030>], 'toolButtonStyleChanged': [<FunctionDef.toolButtonStyleChanged l.None at 0x124765dd090>], 'toolTip': [<EmptyNode.toolTip l.0 at 0x124765dd060>], 'toolTipDuration': [<EmptyNode.toolTipDuration l.0 at 0x124765dd210>], 'tr': [<EmptyNode.tr l.0 at 0x124765dd240>], 'underMouse': [<EmptyNode.underMouse l.0 at 0x124765dd270>], 'ungrabGesture': [<EmptyNode.ungrabGesture l.0 at 0x124765dd2a0>], 'unifiedTitleAndToolBarOnMac': [<EmptyNode.unifiedTitleAndToolBarOnMac l.0 at 0x124765dd2d0>], 'unsetCursor': [<EmptyNode.unsetCursor l.0 at 0x124765dd300>], 'unsetLayoutDirection': [<EmptyNode.unsetLayoutDirection l.0 at 0x124765dd330>], 'unsetLocale': [<EmptyNode.unsetLocale l.0 at 0x124765dd360>], 'update': [<EmptyNode.update l.0 at 0x124765dd390>], 'updateGeometry': [<EmptyNode.updateGeometry l.0 at 0x124765dd3c0>], 'updateMicroFocus': [<EmptyNode.updateMicroFocus l.0 at 0x124765dd3f0>], 'updatesEnabled': [<EmptyNode.updatesEnabled l.0 at 0x124765dd420>], 'visibleRegion': [<EmptyNode.visibleRegion l.0 at 0x124765dd450>], 'whatsThis': [<EmptyNode.whatsThis l.0 at 0x124765dd480>], 'wheelEvent': [<EmptyNode.wheelEvent l.0 at 0x124765dd4b0>], 'width': [<EmptyNode.width l.0 at 0x124765dd4e0>], 'widthMM': [<EmptyNode.widthMM l.0 at 0x124765dd510>], 'winId': [<EmptyNode.winId l.0 at 0x124765dd540>], 'window': [<EmptyNode.window l.0 at 0x124765dd570>], 'windowFilePath': [<EmptyNode.windowFilePath l.0 at 0x124765dd5a0>], 'windowFlags': [<EmptyNode.windowFlags l.0 at 0x124765dd5d0>], 'windowHandle': [<EmptyNode.windowHandle l.0 at 0x124765dd600>], 'windowIcon': [<EmptyNode.windowIcon l.0 at 0x124765dd630>], 'windowIconChanged': [<FunctionDef.windowIconChanged l.None at 0x124765dd690>], 'windowIconText': [<EmptyNode.windowIconText l.0 at 0x124765dd660>], 'windowIconTextChanged': [<FunctionDef.windowIconTextChanged l.None at 0x124765dd840>], 'windowModality': [<EmptyNode.windowModality l.0 at 0x124765dd810>], 'windowOpacity': [<EmptyNode.windowOpacity l.0 at 0x124765dd9c0>], 'windowRole': [<EmptyNode.windowRole l.0 at 0x124765dd9f0>], 'windowState': [<EmptyNode.windowState l.0 at 0x124765dda20>], 'windowTitle': [<EmptyNode.windowTitle l.0 at 0x124765dda50>], 'windowTitleChanged': [<FunctionDef.windowTitleChanged l.None at 0x124765ddab0>], 'windowType': [<EmptyNode.windowType l.0 at 0x124765dda80>], 'x': [<EmptyNode.x l.0 at 0x124765ddc30>], 'y': [<EmptyNode.y l.0 at 0x124765ddc60>]})
leo\plugins\freewin.py:1:0: F0001: Fatal error while checking 'C:\leo.repo\leo-editor\leo\plugins\freewin.py'.

Please open an issue in our bug tracker so we address this.
There is a pre-filled template that you can use in
'C:\Users\Edward Ream\AppData\Local\pylint\pylint\Cache\pylint-crash-2022-04-22-08.txt'. (fatal)
.1 file, time:  8.90 sec.

Configuration

No response

Command used

C:\leo.repo\leo-editor>python -m pylint-leo -f leo\plugins\freewin.py

Pylint output

The crash shown in the details above.

Expected behavior

No crash.

Pylint version

C:\leo.repo\leo-editor>python -m pylint-leo -v

pylint 2.14.0-dev0
astroid 2.12.0-dev0
Python 3.10.4 (tags/v3.10.4:9d38120, Mar 23 2022, 23:13:41) [MSC v.1929 64 bit (AMD64)]

OS / Environment

Leo 6.6.1-devel, devel branch, build 6f62bc8c3c
2022-04-22 08:12:19 -0500
Python 3.10.4, PyQt version 6.2.4
Windows 10 AMD64 (build 10.0.22000) SP0

Additional dependencies

C:\leo.repo\leo-editor>python -m pip freeze

C:\leo.repo\leo-editor>echo off
astroid==2.12.0.dev0
atomicwrites==1.4.0
attrs==21.4.0
beautifulsoup4==4.10.0
black==22.3.0
bleach==4.1.0
certifi==2021.10.8
charset-normalizer==2.0.12
click==8.1.0
colorama==0.4.4
coverage==6.3.2
cycler==0.11.0
defusedxml==0.7.1
dialite==0.5.3
dill==0.3.4
distlib==0.3.4
docutils==0.18.1
entrypoints==0.4
feedparser==6.0.8
filelock==3.6.0
flexx==0.8.3
fonttools==4.31.2
idna==3.3
ImageHash==4.2.1
iniconfig==1.1.1
isort==5.10.1
Jinja2==3.1.1
jsonschema==4.4.0
jupyter-client==7.2.1
jupyter-core==4.9.2
jupyterlab-pygments==0.1.2
kiwisolver==1.4.1
lazy-object-proxy==1.7.1
leo==6.6
lxml==4.8.0
Markdown==3.3.6
MarkupSafe==2.1.1
matplotlib==3.5.1
mccabe==0.7.0
mistune==0.8.4
mypy-extensions==0.4.3
nbclient==0.5.13
nbconvert==6.4.5
nbformat==5.2.0
nest-asyncio==1.5.5
numpy==1.22.3
packaging==21.3
pandocfilters==1.5.0
pathspec==0.9.0
Pillow==9.0.1
platformdirs==2.5.1
pluggy==1.0.0
pscript==0.7.7
py==1.11.0
pyenchant==3.2.2
Pygments==2.11.2
# Editable install with no version control (pylint==2.14.0.dev0)
-e c:\users\edward ream\python\python310\lib\site-packages\pylint-2.14.0.dev0-py3.10.egg
pyparsing==3.0.7
PyQt5==5.15.6
PyQt5-Qt5==5.15.2
PyQt5-sip==12.9.1
PyQt6==6.2.3
PyQt6-Qt6==6.2.4
PyQt6-sip==13.2.1
PyQt6-WebEngine==6.2.1
PyQt6-WebEngine-Qt6==6.2.4
pyrsistent==0.18.1
pytest==7.1.1
pytest-cov==3.0.0
python-dateutil==2.8.2
PyWavelets==1.3.0
pywin32==303
PyYAML==6.0
pyzmq==22.3.0
requests==2.27.1
scipy==1.8.0
Send2Trash==1.8.0
sgmllib3k==1.0.0
six==1.16.0
soupsieve==2.3.1
testpath==0.6.0
toml==0.10.2
tomli==2.0.1
tomlkit==0.10.1
tornado==6.1
tox==3.24.5
traitlets==5.1.1
types-docutils==0.18.0
types-requests==2.27.15
types-urllib3==1.26.11
typing_extensions==4.1.1
urllib3==1.26.9
virtualenv==20.14.0
webencodings==0.5.1
webruntime==0.5.8
websockets==10.2
windows-curses==2.3.0
wrapt==1.14.0

Program that crashed

If this issue survives triage I'll be happy to attempt to condense this program to a shorter version that fails.

Content: When parsing the following file:
#@+leo-ver=5-thin
#@+node:tom.20210613135525.1: * @file ../plugins/freewin.py
r"""
#@+<< docstring >>
#@+node:tom.20210603022210.1: ** << docstring >>
Freewin - a plugin with a basic editor pane that tracks an
outline node.

Provides a free-floating window tied to one node in an outline.
The window functions as a plain text editor, and can also be
switched to render the node with Restructured Text.

:By: T\. B\. Passin
:Version: 2.0
:Date: 6 Apr 2022

#@+others
#@+node:tom.20210604174603.1: *3* Opening a Window
Opening a Window
~~~~~~~~~~~~~~~~~

To open a Freewin window, select a node in your outline and issue
the minibuffer command ``z-open-freewin``.

The window that opens will display an editor pane that contains the
text of the node.  The text can be edited in the window.  If the
text is edited in the outline instead, the changes will show in the
Freewin pane.

Editing changes made in the Freewin window will be echoed in the
underlying outline node even if a different node has been selected.
They will be visible in the outline when the original node is
selected again.

A given Freewin window will be synchronized with the node
that was selected when the Freewin window was opened, and
will only display that node.  It will remain synchronized even if
the node has been moved to a new position in its outline.

.. Note:: A Freewin window will close if the underlying node is removed.
          This will not change the body of the underlying node.

#@+node:tom.20210625220923.1: *3* Navigating
Navigating
~~~~~~~~~~~

#@@nocolor

A Freewin window only ever displays the content of the node it ws opened
on. However, the selected node in the outline in the host can be changed,
which will cause the host to navigate to the new selection. This navigation
can be done when a line in the visible text contains a `gnx` - a node
identifier. If the cursor is placed on a line with a gnx, or if that line
is selected, and then <CONTROL-F9> is pressed, the host outline will
navigate to the node having that gnx.

A gnx looks like this::

    tom.20210610132217.1

A line with a gnx might look like this::

    :event: tom.20210623002747.1 `John DeBoer Opens General Store`_

This capability is always available in the editor pane. It can be available
in the rendering pane (see below) if the setting::

    @string fw-render-pane = nav-view

is set in the @settings tree. The setting can be in the @settings tree of
an outline or in myLeoSettings.leo.
#@+node:tom.20210604181030.1: *3* Rendering with Restructured Text
Rendering with Restructured Text
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Pressing the ``Rendered <--> Plain`` button will switch between
text and RsT rendering.  In RsT mode, text cannot be edited but
changes to the node in the outline will be rendered as they are
made.

If RsT text in the focal node links to another node in the same
subtree, the Freewin window will not navigate to the
target.  This is because the window only represents a single,
unchangeable node. However, no RsT error will be shown, and the
link will be underlined even though it will not be active.

Two types of rendering views are available, and can be chosen by a setting in the @settings tree.

1. A well-rendered view with all the features of Restructured Text rendered
   in an appealing way (depending on the stylesheet used). This view can be
   zoomed in or out using the standard browser keys: CTRL-+ and CTRL--
   (Currently this feature does not work with Qt6). A light or dark themed
   stylesheet is selected based on the dark or light character of your Leo
   theme. You can supply your own stylesheet to use instead of the built-in
   ones.

2. A less fully-rendered view that has the ability to cause the host
   outline to navigate to a node with a selected gnx - see the section on
   `Navigating` above. Because of limitations of the Qt widget used for
   this view, the size cannot be zoomed and some visual features of the
   rendered RsT can be less refined. The stylesheets for this view cannot
   be changed. Automatic switching between light and dark themes is still
   done.

View 1 is the default view, except when using PyQt6, which does not
currently support its features. To use View 2 instead, add the following
setting to the setting tree of an outline or to myLeoSettings.leo:

    @string fw-render-pane = nav-view

#@+node:tom.20210626134532.1: *3* Hotkeys
Hotkeys
~~~~~~~

Freewin uses two hotkeys:

<CNTL-F7> --  copy the gnx of this Freewin window to the clipboard.
<CNTL-F9> -- Select host node that has gnx under the selection point.

<CNTL-F9> is available in the editor view, and in the rendered view
with limitations discussed above discussed above.
#@+node:tom.20210712005103.1: *3* Commands
Commands
~~~~~~~~~

Freewin has one minibuffer command: ``z-open-freewin``. This opens a
Freewin window linked to the currently selected node.
#@+node:tom.20210712005441.1: *3* Settings
Settings
~~~~~~~~~

Freewin has two settings:

1. ``@string fw-render-pane = nav-view``

If present with this value, the rendered view will allow the <CNTL>-F7/F9
keys to work as they do in the Editor view. The rendered view will not be
able to display all the features that a full rendered view can.

2. ``@bool fw-copy-html = False``

   Change to `True` to copy the rendered RsT to the clipboard.
#@+node:tom.20210614171220.1: *3* Stylesheets and Dark-themed Appearance
Stylesheets and Dark-themed Appearance
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The appearance of the editing and rendering view is determined
by stylesheets. Simple default stylesheets are built into the
plugin for the editing view.

For styling the Restructured Text rendering view (When the default "View 1"
is in use) and for customized editing view stylesheets, the plugin looks in
the user's `.leo/css directory`.

The plugin attempts to determine whether the Leo theme in use
is a dark theme or not.  If it is, a dark-themed stylesheet
will be used if it is found.  The "dark" determination is based
on the ``@color_theme_is_dark`` setting in the Leo theme file.
#@+node:tom.20210604181134.1: *4* Styling the Editor View
Styling the Editor View
~~~~~~~~~~~~~~~~~~~~~~~~
The editor panel styles will be set by a
css stylesheet file in the same directory as the
the RsT stylesheet above: the user's `.leo/css`
directory. There can be two stylesheets, one for light
and one for dark themes.

Light Stylesheet
-----------------
The light-themed stylesheet must be named `freewin_editor_light.css`.
The default Freewin values are::

    QTextEdit {
        color: #202020;
        background: #fdfdfd;
        font-family: Cousine, Consolas, Droid Sans Mono, DejaVu Sans Mono;
        font-size: 11pt;
}

Dark Stylesheet
-----------------
The dark-themed stylesheet must be named `freewin_editor_dark.css`.
The default Freewin values are::

    QTextEdit {
        color: #cbdedc;
        background: #202020;
        font-family: Cousine, Consolas, Droid Sans Mono, DejaVu Sans Mono;
        font-size: 11pt;
    }


No Stylesheet
--------------

If the correctly-named stylesheet is not present in the
user's ``.leo/css`` directory then the plugin will use the default values given above.
#@+node:tom.20210604181109.1: *4* Styling the RsT View
Styling the RsT View
~~~~~~~~~~~~~~~~~~~~~

The following on applies when the default rendereing view,
called "View 1" above, is being used.

The RsT view can be styled by extending or replacing
the default css stylesheet provided by docutils.
Custom stylesheets must be in the user's `.leo/css` directory.

For information on creating a customized css stylesheet, see

`docutils stylesheets <https://docutils.sourceforge.io/docs/howto/html-stylesheets.html>`_

As a starting point, the light and dark RsT stylesheets used
by the Viewrendered3 plugin could be used.  They can be found
in the Leo install directory in the ``leo\plugins\viewrendered3``
directory.  There are also a number of docutil stylesheets to be
found with an Internet search.

The VR3 stylesheets must be renamed for the Freewin plugin to
be able to use them.

Light Stylesheet
-----------------

The light-themed stylesheet must be named ``freewin_rst_light.css``.

Dark Stylesheet
-----------------

The dark-themed stylesheet must be named ``freewin_rst_dark.css``.

No Stylesheet
--------------

If no stylesheet exists for the Restructured Text view, the
default Docutils stylesheet will be used for either light or dark
Leo themes.
#@-others

#@-<< docstring >>
"""
# This file hangs pylint.
#@+<< imports >>
#@+node:tom.20210527153415.1: ** << imports >>
from os.path import exists, join as osp_join
import re

from leo.core import leoColorizer
from leo.plugins import qt_text

try:
    # pylint: disable=import-error
    # this can fix an issue with Qt Web views in Ubuntu
    from OpenGL import GL
    assert GL  # To keep pyflakes happy.
except Exception:
    # but no need to stop if it doesn't work
    pass

from leo.core import leoGlobals as g

qt_imports_ok = False
try:
    from leo.core.leoQt import QtCore, QtWidgets, QtGui
    from leo.core.leoQt import KeyboardModifier
    qt_imports_ok = True
except ImportError as e:
    g.trace(e)

if not qt_imports_ok:
    print('Freewin plugin: Qt imports failed')
    raise ImportError('Qt Imports failed')

#@+<<import  QWebView>>
#@+node:tom.20210603000519.1: *3* <<import QWebView>>
QWebView = None
# Not imported above because we might have PyQt without QWebEngineWidgets
from leo.core.leoQt import has_WebEngineWidgets  # pylint: disable=wrong-import-position
if has_WebEngineWidgets:
    from leo.core.leoQt import QtWebEngineWidgets  # pylint: disable=wrong-import-position
    QWebView = QtWebEngineWidgets.QWebEngineView
else:
    try:
        from leo.core.leoQt import QtWebKitWidgets
        QWebView = QtWebKitWidgets.QWebView
    except ImportError:
        if not g.unitTesting:
            print("Freewin: Can't import QtWebKitWidgets")
    except AttributeError:
        if not g.unitTesting:
            print("Freewin: limited RsT rendering in effect")
#@-<<import  QWebView>>
#@+<<import docutils>>
#@+node:tom.20210529002833.1: *3* <<import docutils>>
got_docutils = False
try:
    from docutils.core import publish_string
    from docutils.utils import SystemMessage
    got_docutils = True
except ModuleNotFoundError as e:
    print('Freewin:', e)
except ImportError as e:
    print('Freewin:', e)
except SyntaxError as e:
    print('Freewin:', e)
except Exception as e:
    print('Freewin:', e)

if not got_docutils:
    print('Freewin: no docutils - rendered view is not available')

#@-<<import docutils>>
#
# Fail fast, right after all imports.
g.assertUi('qt')  # May raise g.UiTypeException, caught by the plugins manager.

# Aliases.
QApplication = QtWidgets.QApplication
QFont = QtGui.QFont
QFontInfo = QtGui.QFontInfo
QFontMetrics = QtGui.QFontMetrics
QPushButton = QtWidgets.QPushButton

QRect = QtCore.QRect
QStackedWidget = QtWidgets.QStackedWidget
QTextEdit = QtWidgets.QTextEdit
QVBoxLayout = QtWidgets.QVBoxLayout
QWidget = QtWidgets.QWidget
QColor = QtGui.QColor

WrapMode = QtGui.QTextOption.WrapMode

#@-<< imports >>
#@+<< declarations >>
#@+node:tom.20210527153422.1: ** << declarations >>
# pylint: disable=invalid-name
# Dimensions and placing of editor windows
W = 570
H = 350
X = 1200
Y = 100
DELTA_Y = 35

clipboard = QApplication.clipboard()

FG_COLOR_LIGHT = '#6B5B53'
BG_COLOR_LIGHT = '#ededed'
BG_COLOR_DARK = '#202020'
FG_COLOR_DARK = '#cbdedc'
FONT_FAMILY = 'Cousine, Consolas, Droid Sans Mono, DejaVu Sans Mono'

EDITOR_FONT_SIZE = '11pt'
EDITOR_STYLESHEET_LIGHT_FILE = 'freewin_editor_light.css'
EDITOR_STYLESHEET_DARK_FILE = 'freewin_editor_dark.css'
ENCODING = 'utf-8'
BROWSER = 1
EDITOR = 0
BROWSER_VIEW = 'browser_view'
NAV_VIEW = 'nav-view'

RST_NO_WARNINGS = 5
RST_CUSTOM_STYLESHEET_LIGHT_FILE = 'freewin_rst_light.css'
RST_CUSTOM_STYLESHEET_DARK_FILE = 'freewin_rst_dark.css'

instances = {}

#@+others
#@+node:tom.20210709130401.1: *3* Fonts and Text
ENCODING = 'utf-8'

ZOOM_FACTOR = 1.1

F7_KEY = 0x01000036  # See https://doc.qt.io/qt-5/qt.html#Key-enum (enum Qt::Key)
F9_KEY = 0x01000038
KEY_S = 0x53

GNXre = r'^(.+\.\d+\.\d+)'  # For gnx at start of line
GNX1re = r'.*[([\s](\w+\.\d+\.\d+)'  # For gnx not at start of line

GNX = re.compile(GNXre)
GNX1 = re.compile(GNX1re)

fs = EDITOR_FONT_SIZE.split('pt', 1)[0]
qf = QFont(FONT_FAMILY[0], int(fs))
qfont = QFontInfo(qf)  # Uses actual font if different
FM = QFontMetrics(qf)

TABWIDTH = 36  # Best guess but may not alays be right.
TAB2SPACES = 4  # Tab replacement when writing back to host node
#@-others

#@-<< declarations >>
#@+<< Stylesheets >>
#@+node:tom.20210614172857.1: ** << Stylesheets >>

EDITOR_STYLESHEET_LIGHT = f'''QTextEdit {{
    color: {FG_COLOR_LIGHT};
    background: {BG_COLOR_LIGHT};
    font-family: {FONT_FAMILY};
    font-size: {EDITOR_FONT_SIZE};
    }}'''

EDITOR_STYLESHEET_DARK = f'''QTextEdit {{
    color: {FG_COLOR_DARK};
    background: {BG_COLOR_DARK};
    font-family: {FONT_FAMILY};
    font-size: {EDITOR_FONT_SIZE};
    }}'''

RENDER_BTN_STYLESHEET_LIGHT = f'''color: {FG_COLOR_LIGHT};
    background: {BG_COLOR_LIGHT};
    font-size: {EDITOR_FONT_SIZE};'''

RENDER_BTN_STYLESHEET_DARK = f'''color: {FG_COLOR_DARK};
    background: {BG_COLOR_DARK};
    font-size: {EDITOR_FONT_SIZE};'''

#@+others
#@+node:tom.20210625145324.1: *3* RsT Stylesheet Dark
RST_STYLESHEET_DARK = '''body {
  color: #cbdedc; /*#ededed;*/
  background: #202020;
  font-family: Verdana, Arial, "Bitstream Vera Sans", sans-serif;
  font-size: 10pt;
  line-height:120%;
  margin: 8px 0;
  margin-left: 7px;
  margin-right: 7px;
  }

  h1 {text-align: center; margin-top: 7px; margin-bottom: 12px;}
  a {color: lightblue; text-decoration: none}

  table {margin-top: 10px;}

  th {
    color: #ededed;
    background: #073642;
    vertical-align: top;
    border-bottom: thin solid #839496;
    text-align: center;
    padding-right: 6px; padding-left: 2px;
    padding: 2px;
  }

  th.docinfo-name {
    text-align: right;
  }

  td {
    padding-left: 10px;
  }

  div.admonition, div.note {
    margin: 2em;
    border: 2px solid;
    padding-right: 1em;
    padding-left: 1em;
    background-color: #073642;
    color: #ededed;
    border-color: #839496;
  }

  div.note p.admonition-title {
    color: #2aa198;
    font-weight: bold;
}

'''
#@+node:tom.20210625155534.1: *3* RsT Stylesheet Light
RST_STYLESHEET_LIGHT = '''body {
  color: #6B5B53;
  background: #ededed;
  font-family: Verdana, Arial, "Bitstream Vera Sans", sans-serif;
  font-size: 10pt;
  line-height: 120%;
  margin: 8px 0;
  margin-left: 7px;
  margin-right: 7px;
  }

  h1 {text-align: center; margin-top: 7px; margin-bottom: 12px;}
  a {color: darkblue; text-decoration: none}

  table {
    margin-top: 10px;
  }

  th {
    color: #093947;
    background: #b0ddee;
    vertical-align: top;
    border-bottom: thin solid #839496;
    text-align: center;
    padding-right: 6px; padding-left: 2px;
    padding: 2px;
  }

  td {
    padding-left: 10px;
  }

  th.docinfo-name {
    text-align: right;
  }

  div.admonition, div.system-message,
        div.warning, div.note {
    margin: 2em;
    border: 2px solid;
    padding-right: 1em;
    padding-left: 1em;
    background: #e0e0e0;
    color: #586e75;
    border-color: #657b83;
  }

  p.admonition-title {
    color: #2aa198;
    font-weight: bold;
  }

  div.caution p.admonition-title,
      div.attention p.admonition-title,
      div.warning p.admonition-title {
    color: #cb4b16;
  }

  div.note {
    border-radius: .5em;
  }
'''
#@-others
#@-<< Stylesheets >>

#@+others
#@+node:ekr.20210617074439.1: ** init
def init():
    """Return True if the plugin has loaded successfully."""
    return True
#@+node:tom.20210527153848.1: ** z-commands
@g.command('z-open-freewin')
def open_z_window(event):
    """Open or show editing window for the selected node."""
    if g.app.gui.guiName() != 'qt':
        return
    c = event.get('c')
    if not c:
        return
    id_ = c.p.gnx[:]

    zwin = instances.get(id_)
    if not zwin:
        zwin = instances[id_] = ZEditorWin(c)

    zwin.show()
    zwin.activateWindow()
#@+node:tom.20210625145842.1: ** getGnx
def getGnx(line):
    """Find and return a gnx in a line of text, or None.

    The gnx may be enclosed in parens or brackets.
    """

    matched = GNX1.match(line) or GNX.match(line)
    target = matched[1] if matched else None
    return target
#@+node:tom.20210625145905.1: ** getLine
def getLine(text_edit):
    """Return line of text at cursor position.

    Cursor may not be visible, but its location
    will be at the last mouse click.  If a block
    is selected, then the last line of the block
    is returned.

    ARGUMENT
    text_edit -- a QTextEdit instance

    RETURNS
    a line of text, or ''
    """

    curs = text_edit.textCursor()
    text = text_edit.toPlainText()
    pos = curs.position()
    before = text[:pos]
    after = text[pos:]
    line = before.split('\n')[-1] + after.split('\n')[0]
    return line
#@+node:tom.20210625161018.1: ** gotoHostGnx
def gotoHostGnx(c, target):
    """Change host node selection to target gnx.

    This will not change the node displayed by the
    invoking window.

    ARGUMENTS
    c -- the Leo commander of the outline hosting our window.
    target -- the gnx to be selected in the host, as a string.

    RETURNS
    True if target was found, else False
    """
    if c.p.gnx == target:
        return True
    for p in c.all_unique_positions():
        if p.v.gnx == target:
            c.selectPosition(p)
            return True
    return False
#@+node:tom.20210628002321.1: ** copy2clip
def copy2clip(text):
    #cb = QApplication.clipboard()
    clipboard.setText(text)
#@+node:tom.20220329145952.1: ** change_css_prop
def change_css_prop(css, prop, newval):
    """Change the value of a named property in a css stylesheet fragment.

    If there is more than one instance of prop, only
    the first one will be changed.

    The previous property value is replace with the new.
    """
    if prop not in css:
        return None
    start = css.find(prop)
    colon = css[start:].find(':')
    frag = css[start + colon + 1 :]
    end = frag.find(';')
    val = frag[:end].strip()

    return css.replace(val, newval, 1)

#@+node:tom.20220329150105.1: ** get_body_colors
# Get current colors from the body editor widget
def get_body_colors(c):
    wrapper = c.frame.body.wrapper
    w = wrapper.widget
    pallete = w.viewport().palette()
    fg_hex = pallete.text().color().rgb()
    bg_hex = pallete.window().color().rgb()
    fg = f'#{fg_hex:x}'
    bg = f'#{bg_hex:x}'

    return fg, bg
#@+node:tom.20220329231604.1: ** is_body_dark
def is_body_dark(c):
    """Return True if host's body appears to have a dark theme."""
    fg, bg = get_body_colors(c)
    # hsv =  hue, saturation, value
    # We assess the background to be dark if its value is < 90
    bg_color = QColor(bg)
    h, s, vbg, a = bg_color.getHsv()
    return vbg < 90
#@+node:tom.20210527153906.1: ** class ZEditorWin
class ZEditorWin(QtWidgets.QMainWindow):
    """An editing window that echos the contents of an outline node."""
    #@+others
    #@+node:tom.20210527185804.1: *3* ctor
    def __init__(self, c, title='Z-editor'):
        # pylint: disable=too-many-locals
        global TAB2SPACES
        super().__init__()
        QWidget().__init__()

        self.c = c
        self.p = c.p
        self.v = c.p.v
        self.host_id = c.p.gnx
        w = c.frame.body.wrapper
        self.host_editor = w.widget
        self.switching = False
        self.closing = False

        self.reloadSettings()

        # The rendering pane can be either a QWebView or a QTextEdit
        # depending on the features desired
        if not QWebView:  # Until Qt6 has a QWebEngineView, force QTextEdit
            self.render_pane_type = NAV_VIEW
        if self.render_pane_type == NAV_VIEW:
            self.render_widget = QTextEdit
        else:
            self.render_widget = QWebView
            self.render_pane_type = BROWSER_VIEW

        self.editor = QTextEdit()
        browser = self.browser = self.render_widget()

        wrapper = qt_text.QTextEditWrapper(self.editor, name='zwin', c=c)
        c.k.completeAllBindingsForWidget(wrapper)

        #@+<<set stylesheet paths>>
        #@+node:tom.20210604170628.1: *4* <<set stylesheet paths>>
        self.editor_csspath = ''
        self.rst_csspath = ''

        home = g.app.loadManager.computeHomeDir()
        cssdir = osp_join(home, '.leo', 'css')
        #dict_ = g.app.loadManager.globalSettingsDict

        #is_dark = dict_.get_setting('color-theme-is-dark')
        is_dark = is_body_dark(self.c)
        if is_dark:
            self.editor_csspath = osp_join(cssdir, EDITOR_STYLESHEET_DARK_FILE)
            self.rst_csspath = osp_join(cssdir, RST_CUSTOM_STYLESHEET_DARK_FILE)
        else:
            self.editor_csspath = osp_join(cssdir, EDITOR_STYLESHEET_LIGHT_FILE)
            self.rst_csspath = osp_join(cssdir, RST_CUSTOM_STYLESHEET_LIGHT_FILE)

        if g.isWindows:
            self.editor_csspath = self.editor_csspath.replace('/', '\\')
            self.rst_csspath = self.rst_csspath.replace('/', '\\')
        else:
            self.editor_csspath = self.editor_csspath.replace('\\', '/')
            self.rst_csspath = self.rst_csspath.replace('\\', '/')

        #@-<<set stylesheet paths>>
        #@+<<set stylesheets>>
        #@+node:tom.20210615101103.1: *4* <<set stylesheets>>
        # Check if editor stylesheet file exists. If so,
        # we cache its contents.
        if exists(self.editor_csspath):
            with open(self.editor_csspath, encoding=ENCODING) as f:
                self.editor_style = f.read()
        else:
            self.editor_style = EDITOR_STYLESHEET_DARK if is_dark \
                                else EDITOR_STYLESHEET_LIGHT

        # If a stylesheet exists for RsT, we cache its contents.
        self.rst_stylesheet = None
        if exists(self.rst_csspath):
            with open(self.rst_csspath, encoding=ENCODING) as f:
                self.rst_stylesheet = f.read()
        else:
            self.rst_stylesheet = RST_STYLESHEET_DARK if is_dark \
                                  else RST_STYLESHEET_LIGHT
        #@-<<set stylesheets>>
        #@+<<set up editor>>
        #@+node:tom.20210602172856.1: *4* <<set up editor>>
        self.doc = self.editor.document()
        self.editor.setWordWrapMode(WrapMode.WrapAtWordBoundaryOrAnywhere)  # pylint: disable=no-member

        # Adjust editor stylesheet color to match body fg, bg
        fg, bg = get_body_colors(self.c)
        css = change_css_prop(self.editor_style, 'color', fg)
        css = change_css_prop(css, 'background', bg)
        self.editor_style = css
        self.editor.setStyleSheet(css)

        colorizer = leoColorizer.make_colorizer(c, self.editor, wrapper)
        colorizer.highlighter.setDocument(self.doc)

        # Try to get tab width from the host's body
        # Used when writing edits back to host
        # "tabwidth" directive ought to be in first six lines
        lines = self.p.v.b.split('\n', 6)
        for line in lines:
            if line.startswith('@tabwidth') and line.find(' ') > 0:
                tabfield = line.split()[1]
                TAB2SPACES = abs(int(tabfield))
                break

        # Make tabs line up with 4 spaces (at least approximately)
        self.editor.setTabStopDistance(TABWIDTH)

        if self.render_pane_type == NAV_VIEW:
            # Different stylesheet mechanism if we are a QTextEdit
            stylesheet = RST_STYLESHEET_DARK if is_dark else RST_STYLESHEET_LIGHT
            browser.setReadOnly(True)
            browser_doc = browser.document()
            browser_doc.setDefaultStyleSheet(stylesheet)
        #@-<<set up editor>>
        #@+<<set up render button>>
        #@+node:tom.20210602173354.1: *4* <<set up render button>>
        self.render_button = QPushButton("Rendered <--> Plain")
        self.render_button.clicked.connect(self.switch_and_render)

        b_style = RENDER_BTN_STYLESHEET_DARK if is_dark \
            else RENDER_BTN_STYLESHEET_LIGHT
        self.render_button.setStyleSheet(b_style)
        #@-<<set up render button>>

        #@+<<build central widget>>
        #@+node:tom.20210528235126.1: *4* <<build central widget>>
        self.stacked_widget = QStackedWidget()
        self.stacked_widget.insertWidget(EDITOR, self.editor)
        self.stacked_widget.insertWidget(BROWSER, self.browser)

        layout = QVBoxLayout()
        layout.addWidget(self.render_button)
        layout.addWidget(self.stacked_widget)
        layout.setContentsMargins(0, 0, 0, 0)

        self.central_widget = central_widget = QWidget()
        central_widget.setLayout(layout)
        self.setCentralWidget(central_widget)

        #@-<<build central widget>>
        #@+<<set geometry>>
        #@+node:tom.20210528235451.1: *4* <<set geometry>>
        Y_ = Y + (len(instances) % 10) * DELTA_Y
        self.setGeometry(QtCore.QRect(X, Y_, W, H))
        #@-<<set geometry>>
        #@+<<set window title>>
        #@+node:tom.20210531235412.1: *4* <<set window title>>
        # Show parent's title-->our title, our gnx
        ph = ''
        parents_ = list(c.p.parents())
        if parents_:
            ph = parents_[0].h + '-->'
        self.setWindowTitle(f'{ph}{c.p.h}   {c.p.gnx}')
        #@-<<set window title>>

        self.render_kind = EDITOR

        self.handlers = [('idle', self.update)]
        self._register_handlers()

        self.current_text = c.p.b
        self.editor.setPlainText(self.current_text)

        # Load docutils without rendering anything real
        # Avoids initial delay when switching to RsT the first time.
        if got_docutils:
            dummy = publish_string('dummy', writer_name='html').decode(ENCODING)
            self.browser.setHtml(dummy)
            central_widget.keyPressEvent = self.keyPressEvent

        self.show()
    #@+node:tom.20210625205847.1: *3* reload settings
    def reloadSettings(self):
        c = self.c
        c.registerReloadSettings(self)
        self.render_pane_type = c.config.getString('fw-render-pane') or ''
        self.copy_html = c.config.getBool('fw-copy-html', default=False)

    #@+node:tom.20210528090313.1: *3* update
    # Must have this signature: called by leoPlugins.callTagHandler.
    def update(self, tag, keywords):
        """Update host node if this card's text has changed.

           Otherwise if the host node's text has changed, update
           the card's text with the host's changed text.
           Render as plain text or RsT.

           If the host node does not exist any more, delete ourself.
        """
        if self.closing:
            return

        # Make sure our host node still exists
        if not self.c.p.v == self.v:
            # Find our node
            found_us = False
            for p1 in self.c.all_unique_positions():
                if p1.v == self.v:
                    self.p = p1
                    found_us = True
                    break
            if not found_us:
                self.teardown(tag)
                return

        if self.switching:
            return

        if self.doc.isModified():
            self.current_text = self.doc.toPlainText()
            self.current_text = self.current_text.replace('\t', ' ' * TAB2SPACES)
            self.p.b = self.current_text
            self.doc.setModified(False)

        # If the current position in the outline is our own node,
        # then synchronize the text if it's changed in
        # the host outline.
        elif self.c.p.v == self.v:
            doc = self.host_editor.document()
            if doc.isModified():
                scrollbar = self.editor.verticalScrollBar()
                old_scroll = scrollbar.value()
                self.current_text = doc.toPlainText()
                self.editor.setPlainText(self.current_text)
                self.set_and_render(False)
                doc.setModified(False)
                scrollbar.setValue(old_scroll)

            self.doc.setModified(False)

    #@+node:tom.20210703173219.1: *3* teardown
    def teardown(self, tag=''):
        # Close window and delete it when host node is deleted.
        if self.closing:
            return

        self.closing = True
        g.unregisterHandler(tag, self.update)
        self.central_widget.keyPressEvent = None
        id_ = self.host_id
        self.editor.deleteLater()
        self.browser.deleteLater()
        self.stacked_widget.deleteLater()
        self.central_widget.deleteLater()
        instances[id_] = None  # Not sure if we need this
        del instances[id_]
        self.deleteLater()
    #@+node:tom.20210619000302.1: *3* keyPressEvent
    def keyPressEvent(self, event):
        """Take action on keypresses.

        A method of this name receives keystrokes for most or all
        QObject-descended objects. Currently, checks only for
        <CONTROL-F7>, <CONTROL-F9>, <CONTROL-EQUALS> and
        <CONTROL-MINUS> events for zooming or unzooming the rendering
        pane.
        """
        w = self.browser if self.render_kind == BROWSER else self.editor

        modifiers = event.modifiers()
        bare_key = event.text()
        keyval = event.key()

        if modifiers == KeyboardModifier.ControlModifier:
            if keyval == KEY_S:
                self.c.executeMinibufferCommand('save')
            elif keyval == F7_KEY:
                # Copy our gnx to clipboard.
                copy2clip(self.p.v.gnx)
            elif self.render_pane_type == NAV_VIEW \
                    or self.render_kind == EDITOR:
                # change host's selected node to new target
                if keyval == F9_KEY:
                    gnx = getGnx(getLine(w))
                    found_gnx = gotoHostGnx(self.c, gnx)
                    if not found_gnx:
                        g.es(f'Could not find gnx "{gnx}"')
            elif self.render_kind == BROWSER \
                    and self.render_pane_type == BROWSER_VIEW:
                # Zoom/unzoom
                if bare_key == '=':
                    _zf = w.zoomFactor()
                    w.setZoomFactor(_zf * ZOOM_FACTOR)
                elif bare_key == '-':
                    _zf = w.zoomFactor()
                    w.setZoomFactor(_zf / ZOOM_FACTOR)

    #@+node:tom.20210527234644.1: *3* _register_handlers
    def _register_handlers(self):
        """_register_handlers - attach to Leo signals"""
        for hook, handler in self.handlers:
            g.registerHandler(hook, handler)

    #@+node:tom.20210529000221.1: *3* set_and_render
    def set_and_render(self, switch=True):
        """Switch between the editor and RsT viewer, and render text."""
        self.switching = True
        if not got_docutils:
            self.render_kind = EDITOR
        elif switch:
            if self.render_kind == BROWSER:
                self.render_kind = EDITOR
            else:
                self.render_kind = BROWSER

            self.stacked_widget.setCurrentIndex(self.render_kind)

        if self.render_kind == BROWSER:
            #text = self.editor.document().toRawText()
            text = self.editor.document().toPlainText()
            if text[0] == '<':
                html = text
            else:
                html = self.render_rst(text)
            self.browser.setHtml(html)
            if self.copy_html:
                copy2clip(html)

        self.switching = False

    def switch_and_render(self):
        self.set_and_render(True)
    #@+node:tom.20210602174838.1: *3* render_rst
    def render_rst(self, text):
        """Render text of the editor widget as HTML and display it."""
        if not got_docutils:
            return "<h1>Can't find Docutils to Render This Node</h1>"

        # Call docutils to get the html rendering.
        _html = ''
        args = {'output_encoding': 'unicode',  # return a string, not a byte array
                'report_level': RST_NO_WARNINGS,
               }

        if self.rst_stylesheet:
            args['stylesheet_path'] = None  # omit stylesheet, we will insert one

        try:
            _html = publish_string(text, writer_name='html',
                                   settings_overrides=args)
        except SystemMessage as sm:
            msg = sm.args[0]
            if 'SEVERE' in msg or 'FATAL' in msg:
                _html = f'RsT error:\n{msg}\n\n{text}'

        # Insert stylesheet if our rendering view is a web browser widget
        if self.render_pane_type == BROWSER_VIEW:
            if self.rst_stylesheet:
                style_insert = ("<style type='text/css'>\n"
                        f'{self.rst_stylesheet}\n</style>\n</head>\n')
                _html = _html.replace('</head>', style_insert, 1)
        return _html
    #@-others
#@-others
#@-leo

</ details>

@edreamleo edreamleo added the Needs triage 📥 Just created, needs acknowledgment, triage, and proper labelling label Apr 22, 2022
@DanielNoord
Copy link
Collaborator

@edreamleo I think this is a duplicate of #6437 right?

@edreamleo
Copy link
Contributor Author

@DanielNoord I don't think so, but I can't be sure. Afaik, I am running the latest pylint code.

@edreamleo
Copy link
Contributor Author

@DanielNoord I pulled from master before testing.

@DanielNoord
Copy link
Collaborator

@DanielNoord I don't think so, but I can't be sure. Afaik, I am running the latest pylint code.

Sorry, should have been clearer! I meant the file that crashes: C:\leo.repo\leo-editor\leo\plugins\freewin.py

@edreamleo
Copy link
Contributor Author

edreamleo commented Apr 22, 2022

No, the file that crashes is something new. In #6437, Leo's ll command specifically excluded freewin.py, due to crashes and/or hangs (I don't recall which). In this issue, ll -f leo/plugins/freewin.py checks freewin.py despite the exclusions.

Sorry, this comment was all wrong. I was mistaking #6437 (a duplicate) with my much-earlier issue.

@edreamleo
Copy link
Contributor Author

@DanielNoord Oops. I didn't ping you in the last comment.

@DanielNoord
Copy link
Collaborator

Hmm are you sure -f works as expected? If you check the issue template you used to post this crash report at the end of the report we note that F0001: Fatal error while checking 'C:\leo.repo\leo-editor\leo\plugins\freewin.py'.. So it seems these are both crashes on the same line.

@Pierre-Sassoulas Pierre-Sassoulas added Astroid Related to astroid Needs astroid update Needs an astroid update (probably a release too) before being mergable labels Apr 22, 2022
@edreamleo
Copy link
Contributor Author

@DanielNoord I don't understand your comment:

  • Yes, I'm sure ll -f works.
  • There is a complaint about an unrecognized suppression, but there is only one traceback, whose first line is the same as given in the title of this issue.

@edreamleo
Copy link
Contributor Author

@DanielNoord At last I see what you mean. The title of this issue is incorrect.

@edreamleo
Copy link
Contributor Author

@DanielNoord The title should be something like:
Exception on node <FunctionDef.keyPressEvent l.909 at 0x12474830b80> in file...

@edreamleo
Copy link
Contributor Author

@DanielNoord Or maybe the title is correct. I'm not sure.

@DanielNoord
Copy link
Collaborator

DanielNoord commented Apr 22, 2022

@edreamleo Could you please re-check #6437 and make sure you have filed the correct data there? As I see it these are both issues reporting a traceback due to an AssertionError on assert hasattr(node, "assign_type") in File "C:\Users\Edward Ream\Python\Python310\lib\site-packages\astroid\filter_statements.py", line 115, in filter_stmts.

Both of these AssertionError occur while linting freewin.py as is shown by the final line of the Traceback/Issue Template which reads Fatal error while checking 'C:\leo.repo\leo-editor\leo\plugins\freewin.py'.

I'm not sure where the issue title for both issues comes from but indeed Exception on node <FunctionDef.keyPressEvent l.909 at 0x12474830b80> in seems correct.
So, to fix the crash due to the AssertionError we would need the code of freewin.py and specifically the function definition of keyPressEvent on line 909 as that is what seems to cause the AssertionError.

As to the There is a complaint about an unrecognized suppression I don't really know which suppression we are ignoring?

@edreamleo
Copy link
Contributor Author

@DanielNoord My apologies. I didn't realized I had filed #6437. I have just closed it as a duplicate.

@edreamleo
Copy link
Contributor Author

edreamleo commented Apr 22, 2022

@DanielNoord Here is what appears to be the smallest file illustrating the bug.

# This file crashes pylint.  Based on leo/plugins/freewin.py.

qt_imports_ok = False

try:
    from leo.core.leoQt import QtCore, QtWidgets, QtGui
    qt_imports_ok = True
except ImportError as e:
    g.trace(e)

# The crash does not happen without these aliases.
QPushButton = QtWidgets.QPushButton
QTextEdit = QtWidgets.QTextEdit
QWidget = QtWidgets.QWidget

class ZEditorWin(QtWidgets.QMainWindow):
    
    def __init__(self, c, title='Z-editor'):

        super().__init__()
        self.editor = QTextEdit()

        self.central_widget = central_widget = QWidget()
        central_widget.setLayout(layout)
        self.setCentralWidget(central_widget)

        if got_docutils:
            dummy = publish_string('dummy', writer_name='html').decode(ENCODING)
            self.browser.setHtml(dummy)
            central_widget.keyPressEvent = self.keyPressEvent

    # The crash does not happen if this method does not exist.
    def keyPressEvent(self, event):
        pass

@a-sidorova
Copy link

a-sidorova commented Apr 25, 2022

Hi everyone!
I think I have the same problem with PyQt5

Windows 10, Ubuntu 18.04 LTS
Python 3.6

Package Version


astroid 2.11.3
colorama 0.4.4
dill 0.3.4
isort 5.10.1
lazy-object-proxy 1.7.1
mccabe 0.7.0
pip 22.0.4
platformdirs 2.5.2
pyaml 21.10.1
pylint 2.13.7
PyQt5 5.15.6
PyQt5-Qt5 5.15.2
PyQt5-sip 12.10.1
PyYAML 6.0
setuptools 39.0.1
tomli 2.0.1
typed-ast 1.5.3
typing_extensions 4.2.0
wrapt 1.14.0

Code example:

class ConfigDialog(QDialog):
    def __init__():
        ok_btn = QPushButton('Ok')
        cancel_btn = QPushButton('Cancel')
        ok_btn.clicked.connect(self.accept)
        cancel_btn.clicked.connect(self.reject)

PyLint error for last code lines:
E1120: No value for argument 'slot' in method call (no-value-for-parameter)

But self.accept and self.reject are slots as parameters. I think that it's false positive error
Should I create a new issue (if it's the the same problem) or not?

Thanks!

@The-Compiler
Copy link
Contributor

The-Compiler commented Apr 25, 2022

I believe the issue @edreamleo originally reported (assert hasattr(node, "assign_type") crash) and the issue with No value for argument 'slot' in method call (mentioned here in the title, but not really the topic of the comments above) are two different issues - notably, the false positives are a recent regression, while the crash is not.

In my project, I don't see the crash, but I do see around 100 new false-positives with what @a-sidorova mentions (unfortunately making this update unusable for me).

Even simpler example for the latter:

from PyQt5.QtCore import QTimer
timer = QTimer()
timer.timeout.connect(lambda: None)

(note that --extension-pkg-whitelist=PyQt5 is needed).

Using this, I was able to bisect the issue to pylint-dev/astroid#1505 (namely pylint-dev/astroid@6b1bf6c), a fix for #6221. cc @jacobtylerwalls.


As for the crash reported by @edreamleo, the reproducer relies on leoQt being available, which is not the case of course. Here is a much simpler, standalone, reproducer:

# This file crashes pylint.  Based on leo/plugins/freewin.py.

from PyQt5 import QtCore, QtWidgets, QtGui

# The crash does not happen without these aliases.
QPushButton = QtWidgets.QPushButton
QTextEdit = QtWidgets.QTextEdit
QWidget = QtWidgets.QWidget

class ZEditorWin(QtWidgets.QMainWindow):
    
    def __init__(self):
        super().__init__()
        central_widget = QWidget()
        central_widget.keyPressEvent = self.keyPressEvent

    # The crash does not happen if this method does not exist.
    def keyPressEvent(self, event):
        pass

That one happens even with pylint 2.13.5 and astroid 2.11.2, so contrary to the false positives, does not seem to be a recent regression.

The-Compiler added a commit to qutebrowser/qutebrowser that referenced this issue Apr 25, 2022
@edreamleo
Copy link
Contributor Author

@The-Compiler Thanks for your investigations.

@jacobtylerwalls
Copy link
Member

jacobtylerwalls commented Apr 26, 2022

Thanks @The-Compiler, your reproducers are extremely helpful. I'll refocus this one on the crash, and I'll open a new one for the false positive for no-value-for-parameter (#6464)

We have quite a few open tickets for no-value-for-parameter, so what I suspect happened when we fixed the astroid brain in pylint-dev/astroid#1505 was to drive more traffic into existing bugs.

@jacobtylerwalls jacobtylerwalls added Crash 💥 A bug that makes pylint crash and removed Needs triage 📥 Just created, needs acknowledgment, triage, and proper labelling labels Apr 26, 2022
@jacobtylerwalls jacobtylerwalls changed the title E1120: No value for argument 'slot' in method call (no-value-for-parameter) Crash when linting LeoQt Apr 26, 2022
@jacobtylerwalls jacobtylerwalls added this to the 2.14.0 milestone Apr 30, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Astroid Related to astroid Crash 💥 A bug that makes pylint crash Needs astroid update Needs an astroid update (probably a release too) before being mergable
Projects
None yet
6 participants