Upgraded embedded vulture library to version 2.3.0 (no eric patches for slot support needed anymore).

Sun, 25 Apr 2021 16:13:53 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sun, 25 Apr 2021 16:13:53 +0200
changeset 69
3c2922b45a9f
parent 68
d7a6b7ea640d
child 70
0086bae8f563

Upgraded embedded vulture library to version 2.3.0 (no eric patches for slot support needed anymore).

ChangeLog file | annotate | diff | comparison | revisions
PluginCheckerVulture.epj file | annotate | diff | comparison | revisions
PluginVulture.py file | annotate | diff | comparison | revisions
PluginVulture.zip file | annotate | diff | comparison | revisions
VultureChecker/Documentation/source/Plugin_Checker_Vulture.PluginVulture.html file | annotate | diff | comparison | revisions
VultureChecker/Documentation/source/Plugin_Checker_Vulture.VultureChecker.EditWhiteListDialog.html file | annotate | diff | comparison | revisions
VultureChecker/Documentation/source/Plugin_Checker_Vulture.VultureChecker.VultureCheckerDialog.html file | annotate | diff | comparison | revisions
VultureChecker/Documentation/source/Plugin_Checker_Vulture.VultureChecker.VultureCheckerService.html file | annotate | diff | comparison | revisions
VultureChecker/EditWhiteListDialog.py file | annotate | diff | comparison | revisions
VultureChecker/EditWhiteListDialog.ui file | annotate | diff | comparison | revisions
VultureChecker/VultureCheckerDialog.py file | annotate | diff | comparison | revisions
VultureChecker/VultureCheckerDialog.ui file | annotate | diff | comparison | revisions
VultureChecker/VultureCheckerService.py file | annotate | diff | comparison | revisions
VultureChecker/vulture/LICENSE.txt file | annotate | diff | comparison | revisions
VultureChecker/vulture/__init__.py file | annotate | diff | comparison | revisions
VultureChecker/vulture/config.py file | annotate | diff | comparison | revisions
VultureChecker/vulture/core.py file | annotate | diff | comparison | revisions
VultureChecker/vulture/lines.py file | annotate | diff | comparison | revisions
VultureChecker/vulture/noqa.py file | annotate | diff | comparison | revisions
VultureChecker/vulture/utils.py file | annotate | diff | comparison | revisions
VultureChecker/vulture/version.py file | annotate | diff | comparison | revisions
VultureChecker/vulture/whitelists/argparse_whitelist.py file | annotate | diff | comparison | revisions
VultureChecker/vulture/whitelists/ast_whitelist.py file | annotate | diff | comparison | revisions
VultureChecker/vulture/whitelists/collections_whitelist.py file | annotate | diff | comparison | revisions
VultureChecker/vulture/whitelists/ctypes_whitelist.py file | annotate | diff | comparison | revisions
VultureChecker/vulture/whitelists/logging_whitelist.py file | annotate | diff | comparison | revisions
VultureChecker/vulture/whitelists/string_whitelist.py file | annotate | diff | comparison | revisions
VultureChecker/vulture/whitelists/sys_whitelist.py file | annotate | diff | comparison | revisions
VultureChecker/vulture/whitelists/unittest_whitelist.py file | annotate | diff | comparison | revisions
VultureChecker/vulture/whitelists/whitelist_utils.py file | annotate | diff | comparison | revisions
diff -r d7a6b7ea640d -r 3c2922b45a9f ChangeLog
--- a/ChangeLog	Wed Dec 30 11:02:01 2020 +0100
+++ b/ChangeLog	Sun Apr 25 16:13:53 2021 +0200
@@ -1,5 +1,9 @@
 ChangeLog
 ---------
+Version 4.0.0:
+- upgraded embedded vulture library to version 2.3.0
+  (no eric patches for slot support needed anymore)
+
 Version 3.1.1:
 - bug fixes
 
diff -r d7a6b7ea640d -r 3c2922b45a9f PluginCheckerVulture.epj
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PluginCheckerVulture.epj	Sun Apr 25 16:13:53 2021 +0200
@@ -0,0 +1,299 @@
+{
+  "header": {
+    "comment": "eric project file for project PluginCheckerVulture",
+    "copyright": "Copyright (C) 2021 Detlev Offenbach, detlev@die-offenbachs.de"
+  },
+  "project": {
+    "AUTHOR": "Detlev Offenbach",
+    "CHECKERSPARMS": {
+      "Pep8Checker": {
+        "AnnotationsChecker": {
+          "AllowUntypedDefs": false,
+          "AllowUntypedNested": false,
+          "DispatchDecorators": [
+            "singledispatch",
+            "singledispatchmethod"
+          ],
+          "MaximumComplexity": 3,
+          "MaximumLength": 7,
+          "MinimumCoverage": 75,
+          "MypyInitReturn": false,
+          "OverloadDecorators": [
+            "overload"
+          ],
+          "SuppressDummyArgs": false,
+          "SuppressNoneReturning": true
+        },
+        "BlankLines": [
+          2,
+          1
+        ],
+        "BuiltinsChecker": {
+          "bytes": [
+            "unicode"
+          ],
+          "chr": [
+            "unichr"
+          ],
+          "str": [
+            "unicode"
+          ]
+        },
+        "CommentedCodeChecker": {
+          "Aggressive": false,
+          "WhiteList": [
+            "pylint",
+            "pyright",
+            "noqa",
+            "type:\\s*ignore",
+            "fmt:\\s*(on|off)",
+            "TODO",
+            "FIXME",
+            "WARNING",
+            "NOTE",
+            "TEST",
+            "DOCU",
+            "XXX",
+            "- "
+          ]
+        },
+        "CopyrightAuthor": "",
+        "CopyrightMinFileSize": 0,
+        "DocstringType": "eric",
+        "EnabledCheckerCategories": "C, D, E, M, N, S, Y, W",
+        "ExcludeFiles": "*/Ui_*.py, */*_rc.py, */vulture/*",
+        "ExcludeMessages": "C101,E265,E266,E305,E402,M201,M301,M302,M303,M304,M305,M306,M307,M308,M311,M312,M313,M314,M315,M321,M701,M702,M811,M834,N802,N803,N807,N808,N821,W293,W504,Y119,Y401,Y402",
+        "FixCodes": "",
+        "FixIssues": false,
+        "FutureChecker": "",
+        "HangClosing": false,
+        "IncludeMessages": "",
+        "LineComplexity": 25,
+        "LineComplexityScore": 10,
+        "MaxCodeComplexity": 10,
+        "MaxDocLineLength": 79,
+        "MaxLineLength": 79,
+        "NoFixCodes": "E501",
+        "RepeatMessages": true,
+        "SecurityChecker": {
+          "CheckTypedException": false,
+          "HardcodedTmpDirectories": [
+            "/tmp",
+            "/var/tmp",
+            "/dev/shm",
+            "~/tmp"
+          ],
+          "InsecureHashes": [
+            "md4",
+            "md5",
+            "sha",
+            "sha1"
+          ],
+          "InsecureSslProtocolVersions": [
+            "PROTOCOL_SSLv2",
+            "SSLv2_METHOD",
+            "SSLv23_METHOD",
+            "PROTOCOL_SSLv3",
+            "PROTOCOL_TLSv1",
+            "SSLv3_METHOD",
+            "TLSv1_METHOD"
+          ],
+          "WeakKeySizeDsaHigh": "1024",
+          "WeakKeySizeDsaMedium": "2048",
+          "WeakKeySizeEcHigh": "160",
+          "WeakKeySizeEcMedium": "224",
+          "WeakKeySizeRsaHigh": "1024",
+          "WeakKeySizeRsaMedium": "2048"
+        },
+        "ShowIgnored": false,
+        "ValidEncodings": "latin-1, utf-8"
+      },
+      "Vulture": {
+        "ExcludeFiles": "",
+        "SlotsAreUsed": true,
+        "WhiteLists": {
+          "__patterns__": [
+            "on_*",
+            "visit_*"
+          ],
+          "attribute": [],
+          "class": [],
+          "function": [],
+          "import": [],
+          "property": [],
+          "slot": [],
+          "variable": []
+        }
+      }
+    },
+    "DESCRIPTION": "Plug-in to detect unused code using the vulture library.",
+    "DOCSTRING": "",
+    "DOCUMENTATIONPARMS": {
+      "ERIC4DOC": {
+        "cssFile": "%PYTHON%/eric6/CSSs/default.css",
+        "ignoreDirectories": [
+          ".hg",
+          ".ropeproject",
+          "_ropeproject",
+          ".eric6project",
+          "_eric6project",
+          "vulture"
+        ],
+        "ignoreFilePatterns": [
+          "Ui_*.py"
+        ],
+        "outputDirectory": "VultureChecker/Documentation/source",
+        "qtHelpEnabled": false,
+        "useRecursion": true
+      }
+    },
+    "EMAIL": "detlev@die-offenbachs.de",
+    "EOL": 1,
+    "FILETYPES": {
+      "*.idl": "INTERFACES",
+      "*.py": "SOURCES",
+      "*.py3": "SOURCES",
+      "*.pyw": "SOURCES",
+      "*.pyw3": "SOURCES",
+      "*.qm": "TRANSLATIONS",
+      "*.qrc": "RESOURCES",
+      "*.ts": "TRANSLATIONS",
+      "*.ui": "FORMS",
+      "Ui_*.py": "__IGNORE__"
+    },
+    "FORMS": [
+      "VultureChecker/EditWhiteListDialog.ui",
+      "VultureChecker/VultureCheckerDialog.ui"
+    ],
+    "HASH": "ba39fb6c6b6bbf35870f5dac20e42e8e562ca22b",
+    "IDLPARAMS": {
+      "DefinedNames": [],
+      "IncludeDirs": [],
+      "UndefinedNames": []
+    },
+    "INTERFACES": [],
+    "LEXERASSOCS": {},
+    "MAINSCRIPT": "PluginVulture.py",
+    "MAKEPARAMS": {
+      "MakeEnabled": false,
+      "MakeExecutable": "",
+      "MakeFile": "",
+      "MakeParameters": "",
+      "MakeTarget": "",
+      "MakeTestOnly": true
+    },
+    "MIXEDLANGUAGE": false,
+    "OTHERS": [
+      ".hgignore",
+      "ChangeLog",
+      "PKGLIST",
+      "PluginCheckerVulture.e4p",
+      "PluginVulture.zip",
+      "VultureChecker/Documentation/LICENSE.GPL3",
+      "VultureChecker/Documentation/source",
+      "PluginCheckerVulture.epj",
+      "VultureChecker/vulture/LICENSE.txt"
+    ],
+    "OTHERTOOLSPARMS": {},
+    "PACKAGERSPARMS": {},
+    "PROGLANGUAGE": "Python3",
+    "PROJECTTYPE": "E6Plugin",
+    "PROJECTTYPESPECIFICDATA": {},
+    "PROTOCOLS": [],
+    "RCCPARAMS": {
+      "CompressLevel": 0,
+      "CompressionDisable": false,
+      "CompressionThreshold": 70,
+      "PathPrefix": ""
+    },
+    "RESOURCES": [],
+    "SOURCES": [
+      "PluginVulture.py",
+      "VultureChecker/EditWhiteListDialog.py",
+      "VultureChecker/VultureCheckerDialog.py",
+      "VultureChecker/VultureCheckerService.py",
+      "VultureChecker/__init__.py",
+      "VultureChecker/vulture/__init__.py",
+      "VultureChecker/vulture/__main__.py",
+      "VultureChecker/vulture/core.py",
+      "VultureChecker/vulture/lines.py",
+      "VultureChecker/vulture/utils.py",
+      "VultureChecker/vulture/whitelists/argparse_whitelist.py",
+      "VultureChecker/vulture/whitelists/ast_whitelist.py",
+      "VultureChecker/vulture/whitelists/collections_whitelist.py",
+      "VultureChecker/vulture/whitelists/sys_whitelist.py",
+      "VultureChecker/vulture/whitelists/threading_whitelist.py",
+      "VultureChecker/vulture/whitelists/unittest_whitelist.py",
+      "VultureChecker/vulture/whitelists/whitelist_utils.py",
+      "__init__.py",
+      "VultureChecker/vulture/noqa.py",
+      "VultureChecker/vulture/config.py",
+      "VultureChecker/vulture/version.py",
+      "VultureChecker/vulture/whitelists/logging_whitelist.py",
+      "VultureChecker/vulture/whitelists/string_whitelist.py",
+      "VultureChecker/vulture/whitelists/ctypes_whitelist.py"
+    ],
+    "SPELLEXCLUDES": "",
+    "SPELLLANGUAGE": "en_US",
+    "SPELLWORDS": "",
+    "TRANSLATIONEXCEPTIONS": [],
+    "TRANSLATIONPATTERN": "VultureChecker/i18n/vulture_%language%.ts",
+    "TRANSLATIONS": [
+      "VultureChecker/i18n/vulture_de.qm",
+      "VultureChecker/i18n/vulture_de.ts",
+      "VultureChecker/i18n/vulture_en.qm",
+      "VultureChecker/i18n/vulture_en.ts",
+      "VultureChecker/i18n/vulture_es.qm",
+      "VultureChecker/i18n/vulture_es.ts",
+      "VultureChecker/i18n/vulture_ru.qm",
+      "VultureChecker/i18n/vulture_ru.ts"
+    ],
+    "TRANSLATIONSBINPATH": "",
+    "UICPARAMS": {
+      "Package": "",
+      "PackagesRoot": "",
+      "RcSuffix": ""
+    },
+    "VCS": "Mercurial",
+    "VCSOPTIONS": {
+      "add": [
+        ""
+      ],
+      "checkout": [
+        ""
+      ],
+      "commit": [
+        ""
+      ],
+      "diff": [
+        ""
+      ],
+      "export": [
+        ""
+      ],
+      "global": [
+        ""
+      ],
+      "history": [
+        ""
+      ],
+      "log": [
+        ""
+      ],
+      "remove": [
+        ""
+      ],
+      "status": [
+        ""
+      ],
+      "tag": [
+        ""
+      ],
+      "update": [
+        ""
+      ]
+    },
+    "VCSOTHERDATA": {},
+    "VERSION": "0.x"
+  }
+}
\ No newline at end of file
diff -r d7a6b7ea640d -r 3c2922b45a9f PluginVulture.py
--- a/PluginVulture.py	Wed Dec 30 11:02:01 2020 +0100
+++ b/PluginVulture.py	Sun Apr 25 16:13:53 2021 +0200
@@ -22,7 +22,7 @@
 author = "Detlev Offenbach <detlev@die-offenbachs.de>"
 autoactivate = True
 deactivateable = True
-version = "3.1.1"
+version = "4.0.0"
 className = "VulturePlugin"
 packageName = "VultureChecker"
 shortDescription = "Plug-in to detect unused code using the vulture library"
@@ -55,7 +55,7 @@
         
         @param ui reference to the user interface object (UI.UserInterface)
         """
-        super(VulturePlugin, self).__init__(ui)
+        super().__init__(ui)
         self.__ui = ui
         self.__initialize()
         
@@ -234,11 +234,10 @@
             self.__projectClosed)
         
         menu = e5App().getObject("Project").getMenu("Checks")
-        if menu:
-            if self.__projectAct is not None:
-                menu.removeAction(self.__projectAct)
-                e5App().getObject("Project").removeE5Actions(
-                    [self.__projectAct])
+        if menu is not None and self.__projectAct is not None:
+            menu.removeAction(self.__projectAct)
+            e5App().getObject("Project").removeE5Actions(
+                [self.__projectAct])
         
         self.__initialize()
     
@@ -272,12 +271,11 @@
         @param menu reference to the menu
         @type QMenu
         """
-        if menuName == "Check":
-            if self.__projectAct is not None:
-                self.__projectAct.setEnabled(
-                    e5App().getObject("Project")
-                    .getProjectLanguage() == "Python3"
-                )
+        if menuName == "Check" and self.__projectAct is not None:
+            self.__projectAct.setEnabled(
+                e5App().getObject("Project")
+                .getProjectLanguage() == "Python3"
+            )
     
     def __projectVultureCheck(self):
         """
diff -r d7a6b7ea640d -r 3c2922b45a9f PluginVulture.zip
Binary file PluginVulture.zip has changed
diff -r d7a6b7ea640d -r 3c2922b45a9f VultureChecker/Documentation/source/Plugin_Checker_Vulture.PluginVulture.html
--- a/VultureChecker/Documentation/source/Plugin_Checker_Vulture.PluginVulture.html	Wed Dec 30 11:02:01 2020 +0100
+++ b/VultureChecker/Documentation/source/Plugin_Checker_Vulture.PluginVulture.html	Sun Apr 25 16:13:53 2021 +0200
@@ -239,7 +239,7 @@
         Public method to activate this plug-in.
 </p>
 <dl>
-<dt>Returns:</dt>
+<dt>Return:</dt>
 <dd>
 tuple of None and activation status (boolean)
 </dd>
diff -r d7a6b7ea640d -r 3c2922b45a9f VultureChecker/Documentation/source/Plugin_Checker_Vulture.VultureChecker.EditWhiteListDialog.html
--- a/VultureChecker/Documentation/source/Plugin_Checker_Vulture.VultureChecker.EditWhiteListDialog.html	Wed Dec 30 11:02:01 2020 +0100
+++ b/VultureChecker/Documentation/source/Plugin_Checker_Vulture.VultureChecker.EditWhiteListDialog.html	Sun Apr 25 16:13:53 2021 +0200
@@ -113,6 +113,10 @@
 <td>Private slot handling the selection of tab.</td>
 </tr>
 <tr>
+<td><a href="#EditWhiteListDialog.on_methodsList_itemSelectionChanged">on_methodsList_itemSelectionChanged</a></td>
+<td>Private slot to react upon a change of selection in the methods list.</td>
+</tr>
+<tr>
 <td><a href="#EditWhiteListDialog.on_patternsList_itemSelectionChanged">on_patternsList_itemSelectionChanged</a></td>
 <td>Private slot to react upon a change of selection in the patterns list.</td>
 </tr>
@@ -129,10 +133,6 @@
 <td>Private slot to remove the selected entries from the current list.</td>
 </tr>
 <tr>
-<td><a href="#EditWhiteListDialog.on_slotsList_itemSelectionChanged">on_slotsList_itemSelectionChanged</a></td>
-<td>Private slot to react upon a change of selection in the slots list.</td>
-</tr>
-<tr>
 <td><a href="#EditWhiteListDialog.on_variablesList_itemSelectionChanged">on_variablesList_itemSelectionChanged</a></td>
 <td>Private slot to react upon a change of selection in the variables list.</td>
 </tr>
@@ -176,7 +176,7 @@
 </dd>
 </dl>
 <dl>
-<dt>Returns:</dt>
+<dt>Return:</dt>
 <dd>
 whitelisted names
 </dd>
@@ -202,7 +202,7 @@
 </dd>
 </dl>
 <dl>
-<dt>Returns:</dt>
+<dt>Return:</dt>
 <dd>
 flag indicating a wildcard pattern
 </dd>
@@ -228,7 +228,7 @@
         Public methods to retrieve the various whitelists.
 </p>
 <dl>
-<dt>Returns:</dt>
+<dt>Return:</dt>
 <dd>
 dictionary containing the whitelists
 </dd>
@@ -289,6 +289,13 @@
 index of the selected tab
 </dd>
 </dl>
+<a NAME="EditWhiteListDialog.on_methodsList_itemSelectionChanged" ID="EditWhiteListDialog.on_methodsList_itemSelectionChanged"></a>
+<h4>EditWhiteListDialog.on_methodsList_itemSelectionChanged</h4>
+<b>on_methodsList_itemSelectionChanged</b>(<i></i>)
+
+<p>
+        Private slot to react upon a change of selection in the methods list.
+</p>
 <a NAME="EditWhiteListDialog.on_patternsList_itemSelectionChanged" ID="EditWhiteListDialog.on_patternsList_itemSelectionChanged"></a>
 <h4>EditWhiteListDialog.on_patternsList_itemSelectionChanged</h4>
 <b>on_patternsList_itemSelectionChanged</b>(<i></i>)
@@ -318,13 +325,6 @@
 <p>
         Private slot to remove the selected entries from the current list.
 </p>
-<a NAME="EditWhiteListDialog.on_slotsList_itemSelectionChanged" ID="EditWhiteListDialog.on_slotsList_itemSelectionChanged"></a>
-<h4>EditWhiteListDialog.on_slotsList_itemSelectionChanged</h4>
-<b>on_slotsList_itemSelectionChanged</b>(<i></i>)
-
-<p>
-        Private slot to react upon a change of selection in the slots list.
-</p>
 <a NAME="EditWhiteListDialog.on_variablesList_itemSelectionChanged" ID="EditWhiteListDialog.on_variablesList_itemSelectionChanged"></a>
 <h4>EditWhiteListDialog.on_variablesList_itemSelectionChanged</h4>
 <b>on_variablesList_itemSelectionChanged</b>(<i></i>)
diff -r d7a6b7ea640d -r 3c2922b45a9f VultureChecker/Documentation/source/Plugin_Checker_Vulture.VultureChecker.VultureCheckerDialog.html
--- a/VultureChecker/Documentation/source/Plugin_Checker_Vulture.VultureChecker.VultureCheckerDialog.html	Wed Dec 30 11:02:01 2020 +0100
+++ b/VultureChecker/Documentation/source/Plugin_Checker_Vulture.VultureChecker.VultureCheckerDialog.html	Sun Apr 25 16:13:53 2021 +0200
@@ -105,6 +105,38 @@
 <td>Private slot to edit the whitelist.</td>
 </tr>
 <tr>
+<td><a href="#VultureCheckerDialog.__filterUnusedAttributes">__filterUnusedAttributes</a></td>
+<td>Private method to get the list of unused attributes.</td>
+</tr>
+<tr>
+<td><a href="#VultureCheckerDialog.__filterUnusedClasses">__filterUnusedClasses</a></td>
+<td>Private method to get the list of unused classes.</td>
+</tr>
+<tr>
+<td><a href="#VultureCheckerDialog.__filterUnusedFunctions">__filterUnusedFunctions</a></td>
+<td>Private method to get the list of unused functions.</td>
+</tr>
+<tr>
+<td><a href="#VultureCheckerDialog.__filterUnusedImports">__filterUnusedImports</a></td>
+<td>Private method to get a list of unused imports.</td>
+</tr>
+<tr>
+<td><a href="#VultureCheckerDialog.__filterUnusedItems">__filterUnusedItems</a></td>
+<td>Private method to get a list of unused items.</td>
+</tr>
+<tr>
+<td><a href="#VultureCheckerDialog.__filterUnusedMethods">__filterUnusedMethods</a></td>
+<td>Private method to get the list of unused methods.</td>
+</tr>
+<tr>
+<td><a href="#VultureCheckerDialog.__filterUnusedProperties">__filterUnusedProperties</a></td>
+<td>Private method to get the list of unused properties.</td>
+</tr>
+<tr>
+<td><a href="#VultureCheckerDialog.__filterUnusedVariables">__filterUnusedVariables</a></td>
+<td>Private method to get the list of unused variables.</td>
+</tr>
+<tr>
 <td><a href="#VultureCheckerDialog.__filteredList">__filteredList</a></td>
 <td>Private method to filter a list against the whitelist patterns returning items not matching the whitelist.</td>
 </tr>
@@ -117,10 +149,6 @@
 <td>Private method to get a list of selected non file items.</td>
 </tr>
 <tr>
-<td><a href="#VultureCheckerDialog.__getUnusedItems">__getUnusedItems</a></td>
-<td>Private method to get a list of unused items.</td>
-</tr>
-<tr>
 <td><a href="#VultureCheckerDialog.__prepareResultLists">__prepareResultLists</a></td>
 <td>Private method to prepare the result lists.</td>
 </tr>
@@ -153,34 +181,6 @@
 <td>Private method to store the new whitelists, if they have changed.</td>
 </tr>
 <tr>
-<td><a href="#VultureCheckerDialog.__unusedAttributes">__unusedAttributes</a></td>
-<td>Private method to get the list of unused attributes.</td>
-</tr>
-<tr>
-<td><a href="#VultureCheckerDialog.__unusedClasses">__unusedClasses</a></td>
-<td>Private method to get the list of unused classes.</td>
-</tr>
-<tr>
-<td><a href="#VultureCheckerDialog.__unusedFunctions">__unusedFunctions</a></td>
-<td>Private method to get the list of unused functions.</td>
-</tr>
-<tr>
-<td><a href="#VultureCheckerDialog.__unusedImports">__unusedImports</a></td>
-<td>Private method to get a list of unused imports.</td>
-</tr>
-<tr>
-<td><a href="#VultureCheckerDialog.__unusedProperties">__unusedProperties</a></td>
-<td>Private method to get the list of unused properties.</td>
-</tr>
-<tr>
-<td><a href="#VultureCheckerDialog.__unusedSlots">__unusedSlots</a></td>
-<td>Private method to get the list of unused PyQt/PySide slots.</td>
-</tr>
-<tr>
-<td><a href="#VultureCheckerDialog.__unusedVariables">__unusedVariables</a></td>
-<td>Private method to get the list of unused variables.</td>
-</tr>
-<tr>
 <td><a href="#VultureCheckerDialog.__whiteList">__whiteList</a></td>
 <td>Private slot to add entries to the whitelist.</td>
 </tr>
@@ -281,7 +281,7 @@
 </dd>
 </dl>
 <dl>
-<dt>Returns:</dt>
+<dt>Return:</dt>
 <dd>
 reference to the created item
 </dd>
@@ -332,7 +332,7 @@
 </dd>
 </dl>
 <dl>
-<dt>Returns:</dt>
+<dt>Return:</dt>
 <dd>
 vulture item
 </dd>
@@ -350,6 +350,169 @@
 <p>
         Private slot to edit the whitelist.
 </p>
+<a NAME="VultureCheckerDialog.__filterUnusedAttributes" ID="VultureCheckerDialog.__filterUnusedAttributes"></a>
+<h4>VultureCheckerDialog.__filterUnusedAttributes</h4>
+<b>__filterUnusedAttributes</b>(<i></i>)
+
+<p>
+        Private method to get the list of unused attributes.
+</p>
+<dl>
+<dt>Return:</dt>
+<dd>
+list of unused attributes
+</dd>
+</dl>
+<dl>
+<dt>Return Type:</dt>
+<dd>
+list of VultureItem
+</dd>
+</dl>
+<a NAME="VultureCheckerDialog.__filterUnusedClasses" ID="VultureCheckerDialog.__filterUnusedClasses"></a>
+<h4>VultureCheckerDialog.__filterUnusedClasses</h4>
+<b>__filterUnusedClasses</b>(<i></i>)
+
+<p>
+        Private method to get the list of unused classes.
+</p>
+<dl>
+<dt>Return:</dt>
+<dd>
+list of unused classes
+</dd>
+</dl>
+<dl>
+<dt>Return Type:</dt>
+<dd>
+list of VultureItem
+</dd>
+</dl>
+<a NAME="VultureCheckerDialog.__filterUnusedFunctions" ID="VultureCheckerDialog.__filterUnusedFunctions"></a>
+<h4>VultureCheckerDialog.__filterUnusedFunctions</h4>
+<b>__filterUnusedFunctions</b>(<i></i>)
+
+<p>
+        Private method to get the list of unused functions.
+</p>
+<dl>
+<dt>Return:</dt>
+<dd>
+list of unused functions
+</dd>
+</dl>
+<dl>
+<dt>Return Type:</dt>
+<dd>
+list of VultureItem
+</dd>
+</dl>
+<a NAME="VultureCheckerDialog.__filterUnusedImports" ID="VultureCheckerDialog.__filterUnusedImports"></a>
+<h4>VultureCheckerDialog.__filterUnusedImports</h4>
+<b>__filterUnusedImports</b>(<i></i>)
+
+<p>
+        Private method to get a list of unused imports.
+</p>
+<dl>
+<dt>Return:</dt>
+<dd>
+list of unused imports
+</dd>
+</dl>
+<dl>
+<dt>Return Type:</dt>
+<dd>
+list of VultureItem
+</dd>
+</dl>
+<a NAME="VultureCheckerDialog.__filterUnusedItems" ID="VultureCheckerDialog.__filterUnusedItems"></a>
+<h4>VultureCheckerDialog.__filterUnusedItems</h4>
+<b>__filterUnusedItems</b>(<i>unused, whitelistName</i>)
+
+<p>
+        Private method to get a list of unused items.
+</p>
+<dl>
+
+<dt><i>unused</i> (list of VultureItem)</dt>
+<dd>
+list of unused items
+</dd>
+<dt><i>whitelistName</i> (str)</dt>
+<dd>
+name of the whitelist to use as a filter
+</dd>
+</dl>
+<dl>
+<dt>Return:</dt>
+<dd>
+list of unused items
+</dd>
+</dl>
+<dl>
+<dt>Return Type:</dt>
+<dd>
+list of VultureItem
+</dd>
+</dl>
+<a NAME="VultureCheckerDialog.__filterUnusedMethods" ID="VultureCheckerDialog.__filterUnusedMethods"></a>
+<h4>VultureCheckerDialog.__filterUnusedMethods</h4>
+<b>__filterUnusedMethods</b>(<i></i>)
+
+<p>
+        Private method to get the list of unused methods.
+</p>
+<dl>
+<dt>Return:</dt>
+<dd>
+list of unused methods
+</dd>
+</dl>
+<dl>
+<dt>Return Type:</dt>
+<dd>
+list of VultureItem
+</dd>
+</dl>
+<a NAME="VultureCheckerDialog.__filterUnusedProperties" ID="VultureCheckerDialog.__filterUnusedProperties"></a>
+<h4>VultureCheckerDialog.__filterUnusedProperties</h4>
+<b>__filterUnusedProperties</b>(<i></i>)
+
+<p>
+        Private method to get the list of unused properties.
+</p>
+<dl>
+<dt>Return:</dt>
+<dd>
+list of unused properties
+</dd>
+</dl>
+<dl>
+<dt>Return Type:</dt>
+<dd>
+list of VultureItem
+</dd>
+</dl>
+<a NAME="VultureCheckerDialog.__filterUnusedVariables" ID="VultureCheckerDialog.__filterUnusedVariables"></a>
+<h4>VultureCheckerDialog.__filterUnusedVariables</h4>
+<b>__filterUnusedVariables</b>(<i></i>)
+
+<p>
+        Private method to get the list of unused variables.
+</p>
+<dl>
+<dt>Return:</dt>
+<dd>
+list of unused variables
+</dd>
+</dl>
+<dl>
+<dt>Return Type:</dt>
+<dd>
+list of VultureItem
+</dd>
+</dl>
 <a NAME="VultureCheckerDialog.__filteredList" ID="VultureCheckerDialog.__filteredList"></a>
 <h4>VultureCheckerDialog.__filteredList</h4>
 <b>__filteredList</b>(<i>itemList</i>)
@@ -366,7 +529,7 @@
 </dd>
 </dl>
 <dl>
-<dt>Returns:</dt>
+<dt>Return:</dt>
 <dd>
 list of filtered items
 </dd>
@@ -393,7 +556,7 @@
         Private method to get a list of selected non file items.
 </p>
 <dl>
-<dt>Returns:</dt>
+<dt>Return:</dt>
 <dd>
 list of selected non file items
 </dd>
@@ -404,36 +567,6 @@
 list of QTreeWidgetItem
 </dd>
 </dl>
-<a NAME="VultureCheckerDialog.__getUnusedItems" ID="VultureCheckerDialog.__getUnusedItems"></a>
-<h4>VultureCheckerDialog.__getUnusedItems</h4>
-<b>__getUnusedItems</b>(<i>defined, used</i>)
-
-<p>
-        Private method to get a list of unused items.
-</p>
-<dl>
-
-<dt><i>defined</i> (list of VultureItem)</dt>
-<dd>
-list of defined items
-</dd>
-<dt><i>used</i> (list of str)</dt>
-<dd>
-list of used names
-</dd>
-</dl>
-<dl>
-<dt>Returns:</dt>
-<dd>
-list of unused items
-</dd>
-</dl>
-<dl>
-<dt>Return Type:</dt>
-<dd>
-list of VultureItem
-</dd>
-</dl>
 <a NAME="VultureCheckerDialog.__prepareResultLists" ID="VultureCheckerDialog.__prepareResultLists"></a>
 <h4>VultureCheckerDialog.__prepareResultLists</h4>
 <b>__prepareResultLists</b>(<i></i>)
@@ -533,139 +666,6 @@
 dictionary of lists of whitelisted names
 </dd>
 </dl>
-<a NAME="VultureCheckerDialog.__unusedAttributes" ID="VultureCheckerDialog.__unusedAttributes"></a>
-<h4>VultureCheckerDialog.__unusedAttributes</h4>
-<b>__unusedAttributes</b>(<i></i>)
-
-<p>
-        Private method to get the list of unused attributes.
-</p>
-<dl>
-<dt>Returns:</dt>
-<dd>
-list of unused attributes
-</dd>
-</dl>
-<dl>
-<dt>Return Type:</dt>
-<dd>
-list of VultureItem
-</dd>
-</dl>
-<a NAME="VultureCheckerDialog.__unusedClasses" ID="VultureCheckerDialog.__unusedClasses"></a>
-<h4>VultureCheckerDialog.__unusedClasses</h4>
-<b>__unusedClasses</b>(<i></i>)
-
-<p>
-        Private method to get the list of unused classes.
-</p>
-<dl>
-<dt>Returns:</dt>
-<dd>
-list of unused classes
-</dd>
-</dl>
-<dl>
-<dt>Return Type:</dt>
-<dd>
-list of VultureItem
-</dd>
-</dl>
-<a NAME="VultureCheckerDialog.__unusedFunctions" ID="VultureCheckerDialog.__unusedFunctions"></a>
-<h4>VultureCheckerDialog.__unusedFunctions</h4>
-<b>__unusedFunctions</b>(<i></i>)
-
-<p>
-        Private method to get the list of unused functions.
-</p>
-<dl>
-<dt>Returns:</dt>
-<dd>
-list of unused functions
-</dd>
-</dl>
-<dl>
-<dt>Return Type:</dt>
-<dd>
-list of VultureItem
-</dd>
-</dl>
-<a NAME="VultureCheckerDialog.__unusedImports" ID="VultureCheckerDialog.__unusedImports"></a>
-<h4>VultureCheckerDialog.__unusedImports</h4>
-<b>__unusedImports</b>(<i></i>)
-
-<p>
-        Private method to get a list of unused imports.
-</p>
-<dl>
-<dt>Returns:</dt>
-<dd>
-list of unused imports
-</dd>
-</dl>
-<dl>
-<dt>Return Type:</dt>
-<dd>
-list of VultureItem
-</dd>
-</dl>
-<a NAME="VultureCheckerDialog.__unusedProperties" ID="VultureCheckerDialog.__unusedProperties"></a>
-<h4>VultureCheckerDialog.__unusedProperties</h4>
-<b>__unusedProperties</b>(<i></i>)
-
-<p>
-        Private method to get the list of unused properties.
-</p>
-<dl>
-<dt>Returns:</dt>
-<dd>
-list of unused properties
-</dd>
-</dl>
-<dl>
-<dt>Return Type:</dt>
-<dd>
-list of VultureItem
-</dd>
-</dl>
-<a NAME="VultureCheckerDialog.__unusedSlots" ID="VultureCheckerDialog.__unusedSlots"></a>
-<h4>VultureCheckerDialog.__unusedSlots</h4>
-<b>__unusedSlots</b>(<i></i>)
-
-<p>
-        Private method to get the list of unused PyQt/PySide slots.
-</p>
-<dl>
-<dt>Returns:</dt>
-<dd>
-list of unused PyQt/PySide slots
-</dd>
-</dl>
-<dl>
-<dt>Return Type:</dt>
-<dd>
-list of VultureItem
-</dd>
-</dl>
-<a NAME="VultureCheckerDialog.__unusedVariables" ID="VultureCheckerDialog.__unusedVariables"></a>
-<h4>VultureCheckerDialog.__unusedVariables</h4>
-<b>__unusedVariables</b>(<i></i>)
-
-<p>
-        Private method to get the list of unused variables.
-</p>
-<dl>
-<dt>Returns:</dt>
-<dd>
-list of unused variables
-</dd>
-</dl>
-<dl>
-<dt>Return Type:</dt>
-<dd>
-list of VultureItem
-</dd>
-</dl>
 <a NAME="VultureCheckerDialog.__whiteList" ID="VultureCheckerDialog.__whiteList"></a>
 <h4>VultureCheckerDialog.__whiteList</h4>
 <b>__whiteList</b>(<i></i>)
@@ -782,7 +782,7 @@
     Class to hold the name, type, confidence and location of defined code.
 </p>
 <h3>Derived from</h3>
-object
+None
 <h3>Class Attributes</h3>
 
 <table>
diff -r d7a6b7ea640d -r 3c2922b45a9f VultureChecker/Documentation/source/Plugin_Checker_Vulture.VultureChecker.VultureCheckerService.html
--- a/VultureChecker/Documentation/source/Plugin_Checker_Vulture.VultureChecker.VultureCheckerService.html	Wed Dec 30 11:02:01 2020 +0100
+++ b/VultureChecker/Documentation/source/Plugin_Checker_Vulture.VultureChecker.VultureCheckerService.html	Sun Apr 25 16:13:53 2021 +0200
@@ -122,7 +122,7 @@
 </dd>
 </dl>
 <dl>
-<dt>Returns:</dt>
+<dt>Return:</dt>
 <dd>
 item dictionary
 </dd>
@@ -141,7 +141,7 @@
         Public method to get the scan results.
 </p>
 <dl>
-<dt>Returns:</dt>
+<dt>Return:</dt>
 <dd>
 scan results
 </dd>
@@ -174,7 +174,7 @@
 </dd>
 </dl>
 <dl>
-<dt>Returns:</dt>
+<dt>Return:</dt>
 <dd>
 tuple containing the result dictionary
 </dd>
@@ -229,7 +229,7 @@
     Initialize the batch service and return the entry point.
 </p>
 <dl>
-<dt>Returns:</dt>
+<dt>Return:</dt>
 <dd>
 the entry point for the background client (function)
 </dd>
@@ -245,7 +245,7 @@
     Initialize the service and return the entry point.
 </p>
 <dl>
-<dt>Returns:</dt>
+<dt>Return:</dt>
 <dd>
 the entry point for the background client (function)
 </dd>
@@ -272,7 +272,7 @@
 </dd>
 </dl>
 <dl>
-<dt>Returns:</dt>
+<dt>Return:</dt>
 <dd>
 tuple containing the result dictionary
 </dd>
diff -r d7a6b7ea640d -r 3c2922b45a9f VultureChecker/EditWhiteListDialog.py
--- a/VultureChecker/EditWhiteListDialog.py	Wed Dec 30 11:02:01 2020 +0100
+++ b/VultureChecker/EditWhiteListDialog.py	Sun Apr 25 16:13:53 2021 +0200
@@ -26,13 +26,13 @@
         @param parent reference to the parent widget
         @type QWidget
         """
-        super(EditWhiteListDialog, self).__init__(parent)
+        super().__init__(parent)
         self.setupUi(self)
         
         self.__lists = [
             self.classesList,
             self.functionsList,
-            self.slotsList,
+            self.methodsList,
             self.attributesList,
             self.variablesList,
             self.propertiesList,
@@ -42,7 +42,7 @@
         
         self.classesList.addItems(whitelists["class"])
         self.functionsList.addItems(whitelists["function"])
-        self.slotsList.addItems(whitelists["slot"])
+        self.methodsList.addItems(whitelists["method"])
         self.attributesList.addItems(whitelists["attribute"])
         self.variablesList.addItems(whitelists["variable"])
         self.propertiesList.addItems(whitelists["property"])
@@ -89,16 +89,16 @@
         self.__setButtonEnabledStates()
     
     @pyqtSlot()
-    def on_classesList_itemSelectionChanged(self):
+    def on_methodsList_itemSelectionChanged(self):
         """
-        Private slot to react upon a change of selection in the classes list.
+        Private slot to react upon a change of selection in the methods list.
         """
         self.__setButtonEnabledStates()
     
     @pyqtSlot()
-    def on_slotsList_itemSelectionChanged(self):
+    def on_classesList_itemSelectionChanged(self):
         """
-        Private slot to react upon a change of selection in the slots list.
+        Private slot to react upon a change of selection in the classes list.
         """
         self.__setButtonEnabledStates()
     
@@ -118,11 +118,7 @@
         @return flag indicating a wildcard pattern
         @rtype bool
         """
-        for char in "*?[":
-            if char in name:
-                return True
-        
-        return False
+        return any(char in name for char in "*?[")
     
     @pyqtSlot()
     def on_addButton_clicked(self):
@@ -203,7 +199,7 @@
         return {
             "class": self.__getWhiteList(self.classesList),
             "function": self.__getWhiteList(self.functionsList),
-            "slot": self.__getWhiteList(self.slotsList),
+            "method": self.__getWhiteList(self.methodsList),
             "attribute": self.__getWhiteList(self.attributesList),
             "variable": self.__getWhiteList(self.variablesList),
             "property": self.__getWhiteList(self.propertiesList),
diff -r d7a6b7ea640d -r 3c2922b45a9f VultureChecker/EditWhiteListDialog.ui
--- a/VultureChecker/EditWhiteListDialog.ui	Wed Dec 30 11:02:01 2020 +0100
+++ b/VultureChecker/EditWhiteListDialog.ui	Sun Apr 25 16:13:53 2021 +0200
@@ -22,7 +22,7 @@
      <item>
       <widget class="QTabWidget" name="listsWidget">
        <property name="currentIndex">
-        <number>0</number>
+        <number>7</number>
        </property>
        <widget class="QWidget" name="tab_1">
         <attribute name="title">
@@ -64,13 +64,13 @@
          </item>
         </layout>
        </widget>
-       <widget class="QWidget" name="tab_3">
+       <widget class="QWidget" name="tab_9">
         <attribute name="title">
-         <string>PyQt/PySide Slots</string>
+         <string>Methods</string>
         </attribute>
-        <layout class="QVBoxLayout" name="verticalLayout_8">
+        <layout class="QVBoxLayout" name="verticalLayout_11">
          <item>
-          <widget class="QListWidget" name="slotsList">
+          <widget class="QListWidget" name="methodsList">
            <property name="alternatingRowColors">
             <bool>true</bool>
            </property>
@@ -274,7 +274,7 @@
   <tabstop>listsWidget</tabstop>
   <tabstop>classesList</tabstop>
   <tabstop>functionsList</tabstop>
-  <tabstop>slotsList</tabstop>
+  <tabstop>methodsList</tabstop>
   <tabstop>attributesList</tabstop>
   <tabstop>variablesList</tabstop>
   <tabstop>propertiesList</tabstop>
diff -r d7a6b7ea640d -r 3c2922b45a9f VultureChecker/VultureCheckerDialog.py
--- a/VultureChecker/VultureCheckerDialog.py	Wed Dec 30 11:02:01 2020 +0100
+++ b/VultureChecker/VultureCheckerDialog.py	Sun Apr 25 16:13:53 2021 +0200
@@ -9,6 +9,7 @@
 
 import os
 import fnmatch
+import contextlib
 
 from PyQt5.QtCore import pyqtSlot, qVersion, Qt, QTimer, QRegExp
 from PyQt5.QtWidgets import (
@@ -24,7 +25,7 @@
 import Utilities
 
 
-class VultureItem(object):
+class VultureItem:
     """
     Class to hold the name, type, confidence and location of defined code.
     """
@@ -70,7 +71,7 @@
         @param parent reference to the parent widget
         @type QWidget
         """
-        super(VultureCheckerDialog, self).__init__(parent)
+        super().__init__(parent)
         self.setupUi(self)
         self.setWindowFlags(Qt.Window)
         
@@ -112,7 +113,7 @@
         self.__translatedTypes = {
             "property": self.tr("Property"),
             "function": self.tr("Function"),
-            "slot": self.tr("Slot"),
+            "method": self.tr("Methode"),
             "attribute": self.tr("Attribute"),
             "variable": self.tr("Variable"),
             "class": self.tr("Class"),
@@ -169,7 +170,6 @@
             self.__data["WhiteLists"] = {
                 "property": [],
                 "function": [],
-                "slot": [],
                 "attribute": [],
                 "variable": [],
                 "class": [],
@@ -179,13 +179,12 @@
                     "visit_*",
                 ],
             }
+        if "method" not in self.__data["WhiteLists"]:
+            self.__data["WhiteLists"]["method"] = []
         if "import" not in self.__data["WhiteLists"]:
             self.__data["WhiteLists"]["import"] = []
-        if "SlotsAreUsed" not in self.__data:
-            self.__data["SlotsAreUsed"] = True
         
         self.excludeFilesEdit.setText(self.__data["ExcludeFiles"])
-        self.slotsCheckBox.setChecked(self.__data["SlotsAreUsed"])
     
     def start(self, fn):
         """
@@ -285,11 +284,9 @@
         The results are reported to the __processResult slot.
         """
         self.checkProgressLabel.setPath(self.tr("Preparing files..."))
-        progress = 0
         
         argumentsList = []
-        for filename in self.files:
-            progress += 1
+        for progress, filename in enumerate(self.files, start=1):
             self.checkProgress.setValue(progress)
             QApplication.processEvents()
             
@@ -438,12 +435,6 @@
                 fileList = [f for f in fileList
                             if not fnmatch.fnmatch(f, fileFilter)]
         
-        self.__slotsAreUsed = self.slotsCheckBox.isChecked()
-        if self.__slotsAreUsed != self.__data["SlotsAreUsed"]:
-            self.__data["SlotsAreUsed"] = self.__slotsAreUsed
-            self.__project.setData(
-                "CHECKERSPARMS", "Vulture", self.__data)
-        
         self.start(fileList)
     
     def clear(self):
@@ -476,16 +467,13 @@
         """
         Private method to prepare the result lists.
         """
-        self.__definedAttrs = []
-        self.__definedClasses = []
-        self.__definedFuncs = []
-        self.__definedImports = []
-        self.__definedSlots = []
-        self.__definedProps = []
-        self.__definedVars = []
-        
-        self.__usedAttrs = []
-        self.__usedNames = []
+        self.__unusedAttrs = []
+        self.__unusedClasses = []
+        self.__unusedFuncs = []
+        self.__unusedMethods = []
+        self.__unusedImports = []
+        self.__unusedProps = []
+        self.__unusedVars = []
     
     def __storeResult(self, result):
         """
@@ -494,22 +482,20 @@
         @param result result dictionary
         @type dict
         """
-        self.__definedAttrs.extend(self.__filteredList(
-            [self.__dict2Item(d) for d in result["DefinedAttributes"]]))
-        self.__definedClasses.extend(self.__filteredList(
-            [self.__dict2Item(d) for d in result["DefinedClasses"]]))
-        self.__definedFuncs.extend(self.__filteredList(
-            [self.__dict2Item(d) for d in result["DefinedFunctions"]]))
-        self.__definedImports.extend(self.__filteredList(
-            [self.__dict2Item(d) for d in result["DefinedImports"]]))
-        self.__definedSlots.extend(self.__filteredList(
-            [self.__dict2Item(d) for d in result["DefinedSlots"]]))
-        self.__definedProps.extend(self.__filteredList(
-            [self.__dict2Item(d) for d in result["DefinedProperties"]]))
-        self.__definedVars.extend(self.__filteredList(
-            [self.__dict2Item(d) for d in result["DefinedVariables"]]))
-        self.__usedAttrs.extend(result["UsedAttributes"])
-        self.__usedNames.extend(result["UsedNames"])
+        self.__unusedAttrs.extend(self.__filteredList(
+            [self.__dict2Item(d) for d in result["UnusedAttributes"]]))
+        self.__unusedClasses.extend(self.__filteredList(
+            [self.__dict2Item(d) for d in result["UnusedClasses"]]))
+        self.__unusedFuncs.extend(self.__filteredList(
+            [self.__dict2Item(d) for d in result["UnusedFunctions"]]))
+        self.__unusedMethods.extend(self.__filteredList(
+            [self.__dict2Item(d) for d in result["UnusedMethods"]]))
+        self.__unusedImports.extend(self.__filteredList(
+            [self.__dict2Item(d) for d in result["UnusedImports"]]))
+        self.__unusedProps.extend(self.__filteredList(
+            [self.__dict2Item(d) for d in result["UnusedProperties"]]))
+        self.__unusedVars.extend(self.__filteredList(
+            [self.__dict2Item(d) for d in result["UnusedVariables"]]))
     
     def __dict2Item(self, d):
         """
@@ -540,109 +526,84 @@
                             if not regExp.exactMatch(item.name)]
         return filteredList     # __IGNORE_WARNING_M834__
     
-    def __getUnusedItems(self, defined, used):
+    def __filterUnusedItems(self, unused, whitelistName):
         """
         Private method to get a list of unused items.
         
-        @param defined list of defined items
+        @param unused list of unused items
         @type list of VultureItem
-        @param used list of used names
-        @type list of str
+        @param whitelistName name of the whitelist to use as a filter
+        @type str
         @return list of unused items
         @rtype list of VultureItem
         """
-        return [item for item in set(defined) if item.name not in used]
+        return [
+            item for item in set(unused)
+            if item.name not in self.__data["WhiteLists"][whitelistName]
+        ]
 
-    def __unusedFunctions(self):
+    def __filterUnusedFunctions(self):
         """
         Private method to get the list of unused functions.
         
         @return list of unused functions
         @rtype list of VultureItem
         """
-        return self.__getUnusedItems(
-            self.__definedFuncs,
-            self.__usedAttrs + self.__usedNames +
-            self.__data["WhiteLists"]["function"]
-        )
+        return self.__filterUnusedItems(self.__unusedFuncs, "function")
 
-    def __unusedSlots(self):
+    def __filterUnusedMethods(self):
         """
-        Private method to get the list of unused PyQt/PySide slots.
+        Private method to get the list of unused methods.
         
-        @return list of unused PyQt/PySide slots
+        @return list of unused methods
         @rtype list of VultureItem
         """
-        return self.__getUnusedItems(
-            self.__definedSlots,
-            self.__usedAttrs + self.__usedNames +
-            self.__data["WhiteLists"]["slot"]
-        )
+        return self.__filterUnusedItems(self.__unusedMethods, "method")
 
-    def __unusedClasses(self):
+    def __filterUnusedClasses(self):
         """
         Private method to get the list of unused classes.
         
         @return list of unused classes
         @rtype list of VultureItem
         """
-        return self.__getUnusedItems(
-            self.__definedClasses,
-            self.__usedAttrs + self.__usedNames +
-            self.__data["WhiteLists"]["class"]
-        )
+        return self.__filterUnusedItems(self.__unusedClasses, "class")
 
-    def __unusedImports(self):
+    def __filterUnusedImports(self):
         """
         Private method to get a list of unused imports.
         
         @return list of unused imports
         @rtype list of VultureItem
         """
-        return self.__getUnusedItems(
-            self.__definedClasses,
-            self.__usedAttrs + self.__usedNames +
-            self.__data["WhiteLists"]["import"]
-        )
+        return self.__filterUnusedItems(self.__unusedImports, "import")
     
-    def __unusedProperties(self):
+    def __filterUnusedProperties(self):
         """
         Private method to get the list of unused properties.
         
         @return list of unused properties
         @rtype list of VultureItem
         """
-        return self.__getUnusedItems(
-            self.__definedProps,
-            self.__usedAttrs +
-            self.__data["WhiteLists"]["property"]
-        )
+        return self.__filterUnusedItems(self.__unusedProps, "property")
 
-    def __unusedVariables(self):
+    def __filterUnusedVariables(self):
         """
         Private method to get the list of unused variables.
         
         @return list of unused variables
         @rtype list of VultureItem
         """
-        return self.__getUnusedItems(
-            self.__definedVars,
-            self.__usedAttrs + self.__usedNames +
-            self.__data["WhiteLists"]["variable"]
-        )
+        return self.__filterUnusedItems(self.__unusedVars, "variable")
 
-    def __unusedAttributes(self):
+    def __filterUnusedAttributes(self):
         """
         Private method to get the list of unused attributes.
         
         @return list of unused attributes
         @rtype list of VultureItem
         """
-        return self.__getUnusedItems(
-            self.__definedAttrs,
-            self.__usedAttrs +
-            self.__data["WhiteLists"]["attribute"]
-        )
+        return self.__filterUnusedItems(self.__unusedAttrs, "attribute")
     
     def __createResultItems(self):
         """
@@ -650,14 +611,13 @@
         """
         lastFileItem = None
         lastFileName = ""
-        items = (self.__unusedFunctions() +
-                 self.__unusedClasses() +
-                 self.__unusedImports() +
-                 self.__unusedProperties() +
-                 self.__unusedVariables() +
-                 self.__unusedAttributes())
-        if not self.__slotsAreUsed:
-            items += self.__unusedSlots()
+        items = (self.__filterUnusedFunctions() +
+                 self.__filterUnusedMethods() +
+                 self.__filterUnusedClasses() +
+                 self.__filterUnusedImports() +
+                 self.__filterUnusedProperties() +
+                 self.__filterUnusedVariables() +
+                 self.__filterUnusedAttributes())
         for item in sorted(items, key=lambda item: item.filename):
             if lastFileItem is None or lastFileName != item.filename:
                 lastFileItem = self.__createFileItem(item.filename)
@@ -761,11 +721,8 @@
         for key in self.__data["WhiteLists"]:
             whitelists[key] = self.__data["WhiteLists"][key][:]
         for itm in self.__getSelectedNonFileItems():
-            try:
+            with contextlib.suppress(KeyError):
                 whitelists[itm.data(0, self.TypeRole)].append(itm.text(1))
-            except KeyError:
-                # ignore non-existing types
-                pass
             # remove the item from the result list
             pitm = itm.parent()
             pitm.removeChild(itm)
@@ -786,13 +743,10 @@
         changed = False
         for key in whitelists:
             whitelist = list(set(whitelists[key]))
-            try:
+            with contextlib.suppress(KeyError):
                 if sorted(whitelist) != sorted(self.__data["WhiteLists"][key]):
                     self.__data["WhiteLists"][key] = whitelist[:]
                     changed = True
-            except KeyError:
-                # ignore non-existing types
-                pass
         
         if changed:
             self.__project.setData("CHECKERSPARMS", "Vulture", self.__data)
diff -r d7a6b7ea640d -r 3c2922b45a9f VultureChecker/VultureCheckerDialog.ui
--- a/VultureChecker/VultureCheckerDialog.ui	Wed Dec 30 11:02:01 2020 +0100
+++ b/VultureChecker/VultureCheckerDialog.ui	Sun Apr 25 16:13:53 2021 +0200
@@ -29,7 +29,7 @@
      <property name="frameShadow">
       <enum>QFrame::Raised</enum>
      </property>
-     <layout class="QGridLayout" name="gridLayout">
+     <layout class="QHBoxLayout" name="horizontalLayout">
       <property name="leftMargin">
        <number>0</number>
       </property>
@@ -42,21 +42,21 @@
       <property name="bottomMargin">
        <number>0</number>
       </property>
-      <item row="0" column="0">
+      <item>
        <widget class="QLabel" name="label_2">
         <property name="text">
          <string>Exclude Files:</string>
         </property>
        </widget>
       </item>
-      <item row="0" column="1">
+      <item>
        <widget class="E5ClearableLineEdit" name="excludeFilesEdit">
         <property name="toolTip">
          <string>Enter filename patterns of files to be excluded separated by a comma</string>
         </property>
        </widget>
       </item>
-      <item row="0" column="2" rowspan="2">
+      <item>
        <widget class="Line" name="line">
         <property name="lineWidth">
          <number>2</number>
@@ -66,7 +66,7 @@
         </property>
        </widget>
       </item>
-      <item row="0" column="3" rowspan="2">
+      <item>
        <widget class="QPushButton" name="startButton">
         <property name="toolTip">
          <string>Press to start the check</string>
@@ -76,16 +76,6 @@
         </property>
        </widget>
       </item>
-      <item row="1" column="0" colspan="2">
-       <widget class="QCheckBox" name="slotsCheckBox">
-        <property name="toolTip">
-         <string>Select to treat all PyQt slots as used</string>
-        </property>
-        <property name="text">
-         <string>Treat PyQt slots as used</string>
-        </property>
-       </widget>
-      </item>
      </layout>
     </widget>
    </item>
@@ -180,7 +170,6 @@
  <tabstops>
   <tabstop>startButton</tabstop>
   <tabstop>excludeFilesEdit</tabstop>
-  <tabstop>slotsCheckBox</tabstop>
   <tabstop>resultList</tabstop>
  </tabstops>
  <resources/>
diff -r d7a6b7ea640d -r 3c2922b45a9f VultureChecker/VultureCheckerService.py
--- a/VultureChecker/VultureCheckerService.py	Wed Dec 30 11:02:01 2020 +0100
+++ b/VultureChecker/VultureCheckerService.py	Sun Apr 25 16:13:53 2021 +0200
@@ -166,7 +166,7 @@
         return {
             "name": item.name,
             "type": item.typ,
-            "file": item.filename,
+            "file": str(item.filename),
             "first_line": item.first_lineno,
             "last_line": item.last_lineno,
             "confidence": item.confidence,
@@ -180,20 +180,18 @@
         @rtype dict
         """
         return {
-            "DefinedAttributes":
-                [self.__item2Dict(i) for i in self.defined_attrs],
-            "DefinedClasses":
-                [self.__item2Dict(i) for i in self.defined_classes],
-            "DefinedFunctions":
-                [self.__item2Dict(i) for i in self.defined_funcs],
-            "DefinedImports":
-                [self.__item2Dict(i) for i in self.defined_imports],
-            "DefinedSlots":
-                [self.__item2Dict(i) for i in self.defined_slots],
-            "DefinedProperties":
-                [self.__item2Dict(i) for i in self.defined_props],
-            "DefinedVariables":
-                [self.__item2Dict(i) for i in self.defined_vars],
-            "UsedAttributes": list(self.used_attrs),
-            "UsedNames": list(self.used_names),
+            "UnusedAttributes":
+                [self.__item2Dict(i) for i in self.unused_attrs],
+            "UnusedClasses":
+                [self.__item2Dict(i) for i in self.unused_classes],
+            "UnusedFunctions":
+                [self.__item2Dict(i) for i in self.unused_funcs],
+            "UnusedMethods":
+                [self.__item2Dict(i) for i in self.unused_methods],
+            "UnusedImports":
+                [self.__item2Dict(i) for i in self.unused_imports],
+            "UnusedProperties":
+                [self.__item2Dict(i) for i in self.unused_props],
+            "UnusedVariables":
+                [self.__item2Dict(i) for i in self.unused_vars],
         }
diff -r d7a6b7ea640d -r 3c2922b45a9f VultureChecker/vulture/LICENSE.txt
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/VultureChecker/vulture/LICENSE.txt	Sun Apr 25 16:13:53 2021 +0200
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2012-2020 Jendrik Seipp (jendrikseipp@gmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff -r d7a6b7ea640d -r 3c2922b45a9f VultureChecker/vulture/__init__.py
--- a/VultureChecker/vulture/__init__.py	Wed Dec 30 11:02:01 2020 +0100
+++ b/VultureChecker/vulture/__init__.py	Sun Apr 25 16:13:53 2021 +0200
@@ -1,4 +1,5 @@
-from vulture.core import __version__, Vulture
+from vulture.core import Vulture
+from vulture.version import __version__
 
 assert __version__
 assert Vulture
diff -r d7a6b7ea640d -r 3c2922b45a9f VultureChecker/vulture/config.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/VultureChecker/vulture/config.py	Sun Apr 25 16:13:53 2021 +0200
@@ -0,0 +1,208 @@
+"""
+This module handles retrieval of configuration values from either the
+command-line arguments or the pyproject.toml file.
+"""
+import argparse
+import pathlib
+import sys
+
+import toml
+
+from .version import __version__
+
+#: Possible configuration options and their respective defaults
+DEFAULTS = {
+    "min_confidence": 0,
+    "paths": [],
+    "exclude": [],
+    "ignore_decorators": [],
+    "ignore_names": [],
+    "make_whitelist": False,
+    "sort_by_size": False,
+    "verbose": False,
+}
+
+
+def _check_input_config(data):
+    """
+    Checks the types of the values in *data* against the expected types of
+    config-values. If a value is of the wrong type it will raise a SystemExit.
+    """
+    for key, value in data.items():
+        if key not in DEFAULTS:
+            sys.exit(f"Unknown configuration key: {key}")
+        # The linter suggests to use "isinstance" here but this fails to
+        # detect the difference between `int` and `bool`.
+        if type(value) is not type(DEFAULTS[key]):  # noqa: E721
+            expected_type = type(DEFAULTS[key]).__name__
+            sys.exit(f"Data type for {key} must be {expected_type!r}")
+
+
+def _check_output_config(config):
+    """
+    Run sanity checks on the generated config after all parsing and
+    preprocessing is done.
+
+    Exit the application if an error is encountered.
+    """
+    if not config["paths"]:
+        sys.exit("Please pass at least one file or directory")
+
+
+def _parse_toml(infile):
+    """
+    Parse a TOML file for config values.
+
+    It will search for a section named ``[tool.vulture]`` which contains the
+    same keys as the CLI arguments seen with ``--help``. All leading dashes are
+    removed and other dashes are replaced by underscores (so ``--sort-by-size``
+    becomes ``sort_by_size``).
+
+    Arguments containing multiple values are standard TOML lists.
+
+    Example::
+
+        [tool.vulture]
+        exclude = ["file*.py", "dir/"]
+        ignore_decorators = ["deco1", "deco2"]
+        ignore_names = ["name1", "name2"]
+        make_whitelist = true
+        min_confidence = 10
+        sort_by_size = true
+        verbose = true
+        paths = ["path1", "path2"]
+    """
+    data = toml.load(infile)
+    settings = data.get("tool", {}).get("vulture", {})
+    _check_input_config(settings)
+    return settings
+
+
+def _parse_args(args=None):
+    """
+    Parse CLI arguments.
+
+    :param args: A list of strings representing the CLI arguments. If left to
+        the default, this will default to ``sys.argv``.
+    """
+
+    # Sentinel value to distinguish between "False" and "no default given".
+    missing = object()
+
+    def csv(exclude):
+        return exclude.split(",")
+
+    usage = "%(prog)s [options] [PATH ...]"
+    version = f"vulture {__version__}"
+    glob_help = "Patterns may contain glob wildcards (*, ?, [abc], [!abc])."
+    parser = argparse.ArgumentParser(prog="vulture", usage=usage)
+    parser.add_argument(
+        "paths",
+        nargs="*",
+        metavar="PATH",
+        default=missing,
+        help="Paths may be Python files or directories. For each directory"
+        " Vulture analyzes all contained *.py files.",
+    )
+    parser.add_argument(
+        "--exclude",
+        metavar="PATTERNS",
+        type=csv,
+        default=missing,
+        help=f"Comma-separated list of paths to ignore (e.g.,"
+        f' "*settings.py,docs/*.py"). {glob_help} A PATTERN without glob'
+        f" wildcards is treated as *PATTERN*.",
+    )
+    parser.add_argument(
+        "--ignore-decorators",
+        metavar="PATTERNS",
+        type=csv,
+        default=missing,
+        help=f"Comma-separated list of decorators. Functions and classes using"
+        f' these decorators are ignored (e.g., "@app.route,@require_*").'
+        f" {glob_help}",
+    )
+    parser.add_argument(
+        "--ignore-names",
+        metavar="PATTERNS",
+        type=csv,
+        default=missing,
+        help=f'Comma-separated list of names to ignore (e.g., "visit_*,do_*").'
+        f" {glob_help}",
+    )
+    parser.add_argument(
+        "--make-whitelist",
+        action="store_true",
+        default=missing,
+        help="Report unused code in a format that can be added to a"
+        " whitelist module.",
+    )
+    parser.add_argument(
+        "--min-confidence",
+        type=int,
+        default=missing,
+        help="Minimum confidence (between 0 and 100) for code to be"
+        " reported as unused.",
+    )
+    parser.add_argument(
+        "--sort-by-size",
+        action="store_true",
+        default=missing,
+        help="Sort unused functions and classes by their lines of code.",
+    )
+    parser.add_argument(
+        "-v", "--verbose", action="store_true", default=missing
+    )
+    parser.add_argument("--version", action="version", version=version)
+    namespace = parser.parse_args(args)
+    cli_args = {
+        key: value
+        for key, value in vars(namespace).items()
+        if value is not missing
+    }
+    _check_input_config(cli_args)
+    return cli_args
+
+
+def make_config(argv=None, tomlfile=None):
+    """
+    Returns a config object for vulture, merging both ``pyproject.toml`` and
+    CLI arguments (CLI arguments have precedence).
+
+    :param argv: The CLI arguments to be parsed. This value is transparently
+        passed through to :py:meth:`argparse.ArgumentParser.parse_args`.
+    :param tomlfile: An IO instance containing TOML data. By default this will
+        auto-detect an existing ``pyproject.toml`` file and exists solely for
+        unit-testing.
+    """
+    # If we loaded data from a TOML file, we want to print this out on stdout
+    # in verbose mode so we need to keep the value around.
+    detected_toml_path = ""
+
+    if tomlfile:
+        config = _parse_toml(tomlfile)
+        detected_toml_path = str(tomlfile)
+    else:
+        toml_path = pathlib.Path("pyproject.toml").resolve()
+        if toml_path.is_file():
+            with open(toml_path) as fconfig:
+                config = _parse_toml(fconfig)
+            detected_toml_path = str(toml_path)
+        else:
+            config = {}
+
+    cli_config = _parse_args(argv)
+
+    # Overwrite TOML options with CLI options, if given.
+    config.update(cli_config)
+
+    # Set defaults for missing options.
+    for key, value in DEFAULTS.items():
+        config.setdefault(key, value)
+
+    if detected_toml_path and config["verbose"]:
+        print(f"Reading configuration from {detected_toml_path}")
+
+    _check_output_config(config)
+
+    return config
diff -r d7a6b7ea640d -r 3c2922b45a9f VultureChecker/vulture/core.py
--- a/VultureChecker/vulture/core.py	Wed Dec 30 11:02:01 2020 +0100
+++ b/VultureChecker/vulture/core.py	Sun Apr 25 16:13:53 2021 +0200
@@ -1,91 +1,79 @@
-#! /usr/bin/env python
-# -*- coding: utf-8 -*-
-#
-# vulture - Find dead code.
-#
-# Copyright (c) 2012-2018 Jendrik Seipp (jendrikseipp@gmail.com)
-#
-# Permission is hereby granted, free of charge, to any person obtaining a
-# copy of this software and associated documentation files (the
-# "Software"), to deal in the Software without restriction, including
-# without limitation the rights to use, copy, modify, merge, publish,
-# distribute, sublicense, and/or sell copies of the Software, and to
-# permit persons to whom the Software is furnished to do so, subject to
-# the following conditions:
-#
-# The above copyright notice and this permission notice shall be included
-# in all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
-# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-from __future__ import print_function
-
-import argparse
 import ast
 from fnmatch import fnmatch, fnmatchcase
-import os.path
+from pathlib import Path
 import pkgutil
 import re
 import string
 import sys
 
 from vulture import lines
+from vulture import noqa
 from vulture import utils
+from vulture.config import make_config
 
-__version__ = '1.0-eric6'
 
 DEFAULT_CONFIDENCE = 60
 
-# The ast module in Python 2 trips over "coding" cookies, so strip them.
-ENCODING_REGEX = re.compile(
-    r"^[ \t\v]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+).*?$", flags=re.M)
+IGNORED_VARIABLE_NAMES = {"object", "self"}
 
-IGNORED_VARIABLE_NAMES = set(['object', 'self'])
-# True and False are NameConstants since Python 3.4.
-if sys.version_info < (3, 4):
-    IGNORED_VARIABLE_NAMES |= set(['True', 'False'])
+ERROR_CODES = {
+    "attribute": "V101",
+    "class": "V102",
+    "function": "V103",
+    "import": "V104",
+    "method": "V105",
+    "property": "V106",
+    "variable": "V107",
+    "unreachable_code": "V201",
+}
 
 
 def _get_unused_items(defined_items, used_names):
-    unused_items = [item for item in set(defined_items)
-                    if item.name not in used_names]
+    unused_items = [
+        item for item in set(defined_items) if item.name not in used_names
+    ]
     unused_items.sort(key=lambda item: item.name.lower())
     return unused_items
 
 
 def _is_special_name(name):
-    return name.startswith('__') and name.endswith('__')
+    return name.startswith("__") and name.endswith("__")
 
 
-def _match(name, patterns):
-    return any(fnmatchcase(name, pattern) for pattern in patterns)
+def _match(name, patterns, case=True):
+    func = fnmatchcase if case else fnmatch
+    return any(func(name, pattern) for pattern in patterns)
 
 
 def _is_test_file(filename):
-    return any(
-        fnmatch(os.path.basename(filename), pattern)
-        for pattern in ['test*.py', '*_test.py', '*-test.py'])
+    return _match(
+        filename.resolve(),
+        ["*/test/*", "*/tests/*", "*/test*.py", "*[-_]test.py"],
+        case=False,
+    )
 
 
 def _ignore_class(filename, class_name):
-    return _is_test_file(filename) and 'Test' in class_name
+    return _is_test_file(filename) and "Test" in class_name
 
 
-def _ignore_import(_filename, import_name):
-    # Ignore star-imported names, since we can't detect whether they are used.
-    return import_name == '*'
+def _ignore_import(filename, import_name):
+    """
+    Ignore star-imported names since we can't detect whether they are used.
+    Ignore imports from __init__.py files since they're commonly used to
+    collect objects from a package.
+    """
+    return filename.name == "__init__.py" or import_name == "*"
 
 
 def _ignore_function(filename, function_name):
-    return (
-        _is_special_name(function_name) or
-        (function_name.startswith('test_') and _is_test_file(filename)))
+    return function_name.startswith("test_") and _is_test_file(filename)
+
+
+def _ignore_method(filename, method_name):
+    return _is_special_name(method_name) or (
+        method_name.startswith("test_") and _is_test_file(filename)
+    )
 
 
 def _ignore_variable(filename, varname):
@@ -94,25 +82,43 @@
     __x__ (special variable or method), but not __x.
     """
     return (
-        varname in IGNORED_VARIABLE_NAMES or
-        (varname.startswith('_') and not varname.startswith('__')) or
-        _is_special_name(varname))
+        varname in IGNORED_VARIABLE_NAMES
+        or (varname.startswith("_") and not varname.startswith("__"))
+        or _is_special_name(varname)
+    )
 
 
-class Item(object):
+class Item:
     """
     Hold the name, type and location of defined code.
     """
 
-    def __init__(self, name, typ, filename, first_lineno, last_lineno,
-                 message='',
-                 confidence=DEFAULT_CONFIDENCE):
+    __slots__ = (
+        "name",
+        "typ",
+        "filename",
+        "first_lineno",
+        "last_lineno",
+        "message",
+        "confidence",
+    )
+
+    def __init__(
+        self,
+        name,
+        typ,
+        filename,
+        first_lineno,
+        last_lineno,
+        message="",
+        confidence=DEFAULT_CONFIDENCE,
+    ):
         self.name = name
         self.typ = typ
         self.filename = filename
         self.first_lineno = first_lineno
         self.last_lineno = last_lineno
-        self.message = message or "unused {typ} '{name}'".format(**locals())
+        self.message = message or f"unused {typ} '{name}'"
         self.confidence = confidence
 
     @property
@@ -122,23 +128,29 @@
 
     def get_report(self, add_size=False):
         if add_size:
-            line_format = 'line' if self.size == 1 else 'lines'
-            size_report = ', {0:d} {1}'.format(self.size, line_format)
+            line_format = "line" if self.size == 1 else "lines"
+            size_report = f", {self.size:d} {line_format}"
         else:
-            size_report = ''
-        return "{0}:{1:d}: {2} ({3}% confidence{4})".format(
-            utils.format_path(self.filename), self.first_lineno,
-            self.message, self.confidence, size_report)
+            size_report = ""
+        return "{}:{:d}: {} ({}% confidence{})".format(
+            utils.format_path(self.filename),
+            self.first_lineno,
+            self.message,
+            self.confidence,
+            size_report,
+        )
 
     def get_whitelist_string(self):
         filename = utils.format_path(self.filename)
-        if self.typ == 'unreachable_code':
-            return ('# {} ({}:{})'.format(
-                self.message, filename, self.first_lineno))
+        if self.typ == "unreachable_code":
+            return f"# {self.message} ({filename}:{self.first_lineno})"
         else:
-            prefix = '_.' if self.typ in ['attribute', 'property'] else ''
+            prefix = ""
+            if self.typ in ["attribute", "method", "property"]:
+                prefix = "_."
             return "{}{}  # unused {} ({}:{:d})".format(
-                    prefix, self.name, self.typ, filename, self.first_lineno)
+                prefix, self.name, self.typ, filename, self.first_lineno
+            )
 
     def _tuple(self):
         return (self.filename, self.first_lineno, self.name)
@@ -156,94 +168,110 @@
 class Vulture(ast.NodeVisitor):
     """Find dead code."""
 
-    def __init__(self, verbose=False, ignore_names=None,
-                 ignore_decorators=None):
+    def __init__(
+        self, verbose=False, ignore_names=None, ignore_decorators=None
+    ):
         self.verbose = verbose
 
         def get_list(typ):
             return utils.LoggingList(typ, self.verbose)
 
-        def get_set(typ):
-            return utils.LoggingSet(typ, self.verbose)
+        self.defined_attrs = get_list("attribute")
+        self.defined_classes = get_list("class")
+        self.defined_funcs = get_list("function")
+        self.defined_imports = get_list("import")
+        self.defined_methods = get_list("method")
+        self.defined_props = get_list("property")
+        self.defined_vars = get_list("variable")
+        self.unreachable_code = get_list("unreachable_code")
 
-        self.defined_attrs = get_list('attribute')
-        self.defined_classes = get_list('class')
-        self.defined_funcs = get_list('function')
-        self.defined_imports = get_list('import')
-        self.defined_props = get_list('property')
-        self.defined_slots = get_list('slot')
-        # @pyqtSlot and @Slot support - eric6
-        self.defined_vars = get_list('variable')
-        self.unreachable_code = get_list('unreachable_code')
-
-        self.used_attrs = get_set('attribute')
-        self.used_names = get_set('name')
+        self.used_names = utils.LoggingSet("name", self.verbose)
 
         self.ignore_names = ignore_names or []
         self.ignore_decorators = ignore_decorators or []
 
-        self.filename = ''
+        self.filename = Path()
         self.code = []
         self.found_dead_code_or_error = False
 
-    def scan(self, code, filename=''):
-        code = ENCODING_REGEX.sub("", code, count=1)
+    def scan(self, code, filename=""):
+        filename = Path(filename)
         self.code = code.splitlines()
+        self.noqa_lines = noqa.parse_noqa(self.code)
         self.filename = filename
+
+        def handle_syntax_error(e):
+            text = f' at "{e.text.strip()}"' if e.text else ""
+            print(
+                f"{utils.format_path(filename)}:{e.lineno}: {e.msg}{text}",
+                file=sys.stderr,
+            )
+            self.found_dead_code_or_error = True
+
         try:
-            node = ast.parse(code, filename=self.filename)
+            node = (
+                ast.parse(
+                    code, filename=str(self.filename), type_comments=True
+                )
+                if sys.version_info >= (3, 8)  # type_comments requires 3.8+
+                else ast.parse(code, filename=str(self.filename))
+            )
         except SyntaxError as err:
-            text = ' at "{0}"'.format(err.text.strip()) if err.text else ''
-            print('{0}:{1:d}: {2}{3}'.format(
-                utils.format_path(filename), err.lineno, err.msg, text),
-                file=sys.stderr)
-            self.found_dead_code_or_error = True
-        except (TypeError, ValueError) as err:
-            # Python < 3.5 raises TypeError and Python >= 3.5 raises
-            # ValueError if source contains null bytes.
-            print('{0}: invalid source code "{1}"'.format(
-                utils.format_path(filename), err), file=sys.stderr)
+            handle_syntax_error(err)
+        except ValueError as err:
+            # ValueError is raised if source contains null bytes.
+            print(
+                f'{utils.format_path(filename)}: invalid source code "{err}"',
+                file=sys.stderr,
+            )
             self.found_dead_code_or_error = True
         else:
-            self.visit(node)
+            # When parsing type comments, visiting can throw SyntaxError.
+            try:
+                self.visit(node)
+            except SyntaxError as err:
+                handle_syntax_error(err)
 
     def scavenge(self, paths, exclude=None):
         def prepare_pattern(pattern):
-            if not any(char in pattern for char in ['*', '?', '[']):
-                pattern = '*{pattern}*'.format(**locals())
+            if not any(char in pattern for char in "*?["):
+                pattern = f"*{pattern}*"
             return pattern
 
         exclude = [prepare_pattern(pattern) for pattern in (exclude or [])]
 
-        def exclude_file(name):
-            return any(fnmatch(name, pattern) for pattern in exclude)
+        def exclude_path(path):
+            return _match(path, exclude, case=False)
+
+        paths = [Path(path) for path in paths]
 
         for module in utils.get_modules(paths):
-            if exclude_file(module):
-                self._log('Excluded:', module)
+            if exclude_path(module):
+                self._log("Excluded:", module)
                 continue
 
-            self._log('Scanning:', module)
+            self._log("Scanning:", module)
             try:
                 module_string = utils.read_file(module)
-            except utils.VultureInputException as err:
+            except utils.VultureInputException as err:  # noqa: F841
                 print(
-                    'Error: Could not read file {module} - {err}\n'
-                    'Try to change the encoding to UTF-8.'.format(**locals()),
-                    file=sys.stderr)
+                    f"Error: Could not read file {module} - {err}\n"
+                    f"Try to change the encoding to UTF-8.",
+                    file=sys.stderr,
+                )
                 self.found_dead_code_or_error = True
             else:
                 self.scan(module_string, filename=module)
 
-        unique_imports = set(item.name for item in self.defined_imports)
+        unique_imports = {item.name for item in self.defined_imports}
         for import_name in unique_imports:
-            path = os.path.join('whitelists', import_name) + '_whitelist.py'
-            if exclude_file(path):
-                self._log('Excluded whitelist:', path)
+            path = Path("whitelists") / (import_name + "_whitelist.py")
+            if exclude_path(path):
+                self._log("Excluded whitelist:", path)
             else:
                 try:
-                    module_data = pkgutil.get_data('vulture', path)
-                    self._log('Included whitelist:', path)
+                    module_data = pkgutil.get_data("vulture", str(path))
+                    self._log("Included whitelist:", path)
                 except OSError:
                     # Most imported modules don't have a whitelist.
                     continue
@@ -255,68 +283,77 @@
         Return ordered list of unused Item objects.
         """
         if not 0 <= min_confidence <= 100:
-            raise ValueError('min_confidence must be between 0 and 100.')
+            raise ValueError("min_confidence must be between 0 and 100.")
 
         def by_name(item):
-            return (item.filename.lower(), item.first_lineno)
+            return (str(item.filename).lower(), item.first_lineno)
 
         def by_size(item):
             return (item.size,) + by_name(item)
 
-        unused_code = (self.unused_attrs + self.unused_classes +
-                       self.unused_funcs + self.unused_imports +
-                       self.unused_props + self.unused_vars +
-                       self.unreachable_code)
+        unused_code = (
+            self.unused_attrs
+            + self.unused_classes
+            + self.unused_funcs
+            + self.unused_imports
+            + self.unused_methods
+            + self.unused_props
+            + self.unused_vars
+            + self.unreachable_code
+        )
 
-        confidently_unused = [obj for obj in unused_code
-                              if obj.confidence >= min_confidence]
+        confidently_unused = [
+            obj for obj in unused_code if obj.confidence >= min_confidence
+        ]
 
-        return sorted(confidently_unused,
-                      key=by_size if sort_by_size else by_name)
+        return sorted(
+            confidently_unused, key=by_size if sort_by_size else by_name
+        )
 
-    def report(self, min_confidence=0, sort_by_size=False,
-               make_whitelist=False):
+    def report(
+        self, min_confidence=0, sort_by_size=False, make_whitelist=False
+    ):
         """
         Print ordered list of Item objects to stdout.
         """
         for item in self.get_unused_code(
-                min_confidence=min_confidence, sort_by_size=sort_by_size):
-            print(item.get_whitelist_string() if make_whitelist
-                  else item.get_report(add_size=sort_by_size))
+            min_confidence=min_confidence, sort_by_size=sort_by_size
+        ):
+            print(
+                item.get_whitelist_string()
+                if make_whitelist
+                else item.get_report(add_size=sort_by_size)
+            )
             self.found_dead_code_or_error = True
         return self.found_dead_code_or_error
 
     @property
     def unused_classes(self):
-        return _get_unused_items(
-            self.defined_classes,
-            self.used_attrs | self.used_names)
+        return _get_unused_items(self.defined_classes, self.used_names)
 
     @property
     def unused_funcs(self):
-        return _get_unused_items(
-            self.defined_funcs,
-            self.used_attrs | self.used_names)
+        return _get_unused_items(self.defined_funcs, self.used_names)
 
     @property
     def unused_imports(self):
-        return _get_unused_items(
-            self.defined_imports,
-            self.used_names | self.used_attrs)
+        return _get_unused_items(self.defined_imports, self.used_names)
+
+    @property
+    def unused_methods(self):
+        return _get_unused_items(self.defined_methods, self.used_names)
 
     @property
     def unused_props(self):
-        return _get_unused_items(self.defined_props, self.used_attrs)
+        return _get_unused_items(self.defined_props, self.used_names)
 
     @property
     def unused_vars(self):
-        return _get_unused_items(
-            self.defined_vars,
-            self.used_attrs | self.used_names)
+        return _get_unused_items(self.defined_vars, self.used_names)
 
     @property
     def unused_attrs(self):
-        return _get_unused_items(self.defined_attrs, self.used_attrs)
+        return _get_unused_items(self.defined_attrs, self.used_names)
 
     def _log(self, *args):
         if self.verbose:
@@ -331,54 +368,107 @@
         for name_and_alias in node.names:
             # Store only top-level module name ("os.path" -> "os").
             # We can't easily detect when "os.path" is used.
-            name = name_and_alias.name.partition('.')[0]
+            name = name_and_alias.name.partition(".")[0]
             alias = name_and_alias.asname
             self._define(
-                self.defined_imports, alias or name, node,
-                confidence=90, ignore=_ignore_import)
+                self.defined_imports,
+                alias or name,
+                node,
+                confidence=90,
+                ignore=_ignore_import,
+            )
             if alias is not None:
                 self.used_names.add(name_and_alias.name)
 
     def _handle_conditional_node(self, node, name):
         if utils.condition_is_always_false(node.test):
             self._define(
-                self.unreachable_code, name, node,
-                last_node=node.body[-1],
-                message="unsatisfiable '{name}' condition".format(**locals()),
-                confidence=100)
-        else:
-            else_body = getattr(node, 'orelse')
-            if utils.condition_is_always_true(node.test) and else_body:
+                self.unreachable_code,
+                name,
+                node,
+                last_node=node.body
+                if isinstance(node, ast.IfExp)
+                else node.body[-1],
+                message=f"unsatisfiable '{name}' condition",
+                confidence=100,
+            )
+        elif utils.condition_is_always_true(node.test):
+            else_body = node.orelse
+            if name == "ternary":
                 self._define(
-                    self.unreachable_code, 'else', else_body[0],
+                    self.unreachable_code,
+                    name,
+                    else_body,
+                    message="unreachable 'else' expression",
+                    confidence=100,
+                )
+            elif else_body:
+                self._define(
+                    self.unreachable_code,
+                    "else",
+                    else_body[0],
                     last_node=else_body[-1],
                     message="unreachable 'else' block",
-                    confidence=100)
+                    confidence=100,
+                )
+            elif name == "if":
+                # Redundant if-condition without else block.
+                self._define(
+                    self.unreachable_code,
+                    name,
+                    node,
+                    message="redundant if-condition",
+                    confidence=100,
+                )
 
-    def _define(self, collection, name, first_node, last_node=None,
-                message='', confidence=DEFAULT_CONFIDENCE, ignore=None):
+    def _define(
+        self,
+        collection,
+        name,
+        first_node,
+        last_node=None,
+        message="",
+        confidence=DEFAULT_CONFIDENCE,
+        ignore=None,
+    ):
+        def ignored(lineno):
+            return (
+                (ignore and ignore(self.filename, name))
+                or _match(name, self.ignore_names)
+                or noqa.ignore_line(self.noqa_lines, lineno, ERROR_CODES[typ])
+            )
+
         last_node = last_node or first_node
         typ = collection.typ
-        if (ignore and ignore(self.filename, name)) or _match(
-                name, self.ignore_names):
-            self._log('Ignoring {typ} "{name}"'.format(**locals()))
+        first_lineno = lines.get_first_line_number(first_node)
+
+        if ignored(first_lineno):
+            self._log(f'Ignoring {typ} "{name}"')
         else:
-            first_lineno = first_node.lineno
             last_lineno = lines.get_last_line_number(last_node)
             collection.append(
-                Item(name, typ, self.filename, first_lineno, last_lineno,
-                     message=message, confidence=confidence))
+                Item(
+                    name,
+                    typ,
+                    self.filename,
+                    first_lineno,
+                    last_lineno,
+                    message=message,
+                    confidence=confidence,
+                )
+            )
 
     def _define_variable(self, name, node, confidence=DEFAULT_CONFIDENCE):
-        self._define(self.defined_vars, name, node, confidence=confidence,
-                     ignore=_ignore_variable)
+        self._define(
+            self.defined_vars,
+            name,
+            node,
+            confidence=confidence,
+            ignore=_ignore_variable,
+        )
 
     def visit_arg(self, node):
-        """Function argument.
-
-        ast.arg was added in Python 3.0.
-        ast.arg.lineno was added in Python 3.4.
-        """
+        """Function argument"""
         self._define_variable(node.arg, node, confidence=100)
 
     def visit_AsyncFunctionDef(self, node):
@@ -388,107 +478,170 @@
         if isinstance(node.ctx, ast.Store):
             self._define(self.defined_attrs, node.attr, node)
         elif isinstance(node.ctx, ast.Load):
-            self.used_attrs.add(node.attr)
+            self.used_names.add(node.attr)
+
+    def visit_BinOp(self, node):
+        """
+        Parse variable names in old format strings:
+
+        "%(my_var)s" % locals()
+        """
+        if (
+            isinstance(node.left, ast.Str)
+            and isinstance(node.op, ast.Mod)
+            and self._is_locals_call(node.right)
+        ):
+            self.used_names |= set(re.findall(r"%\((\w+)\)", node.left.s))
+
+    def visit_Call(self, node):
+        # Count getattr/hasattr(x, "some_attr", ...) as usage of some_attr.
+        if isinstance(node.func, ast.Name) and (
+            (node.func.id == "getattr" and 2 <= len(node.args) <= 3)
+            or (node.func.id == "hasattr" and len(node.args) == 2)
+        ):
+            attr_name_arg = node.args[1]
+            if isinstance(attr_name_arg, ast.Str):
+                self.used_names.add(attr_name_arg.s)
+
+        # Parse variable names in new format strings:
+        # "{my_var}".format(**locals())
+        if (
+            isinstance(node.func, ast.Attribute)
+            and isinstance(node.func.value, ast.Str)
+            and node.func.attr == "format"
+            and any(
+                kw.arg is None and self._is_locals_call(kw.value)
+                for kw in node.keywords
+            )
+        ):
+            self._handle_new_format_string(node.func.value.s)
+
+    def _handle_new_format_string(self, s):
+        def is_identifier(name):
+            return bool(re.match(r"[a-zA-Z_][a-zA-Z0-9_]*", name))
+
+        parser = string.Formatter()
+        try:
+            names = [name for _, name, _, _ in parser.parse(s) if name]
+        except ValueError:
+            # Invalid format string.
+            names = []
+
+        for field_name in names:
+            # Remove brackets and their contents: "a[0][b].c[d].e" -> "a.c.e",
+            # then split the resulting string: "a.b.c" -> ["a", "b", "c"]
+            vars = re.sub(r"\[\w*\]", "", field_name).split(".")
+            for var in vars:
+                if is_identifier(var):
+                    self.used_names.add(var)
+
+    @staticmethod
+    def _is_locals_call(node):
+        """Return True if the node is `locals()`."""
+        return (
+            isinstance(node, ast.Call)
+            and isinstance(node.func, ast.Name)
+            and node.func.id == "locals"
+            and not node.args
+            and not node.keywords
+        )
 
     def visit_ClassDef(self, node):
         for decorator in node.decorator_list:
-            if _match(utils.get_decorator_name(decorator),
-                      self.ignore_decorators):
-                self._log('Ignoring class "{}" (decorator whitelisted)'.format(
-                    node.name))
+            if _match(
+                utils.get_decorator_name(decorator), self.ignore_decorators
+            ):
+                self._log(
+                    f'Ignoring class "{node.name}" (decorator whitelisted)'
+                )
                 break
         else:
             self._define(
-                self.defined_classes, node.name, node, ignore=_ignore_class)
+                self.defined_classes, node.name, node, ignore=_ignore_class
+            )
 
     def visit_FunctionDef(self, node):
-        decorator_names = [utils.get_decorator_name(
-            decorator) for decorator in node.decorator_list]
-        typ = 'property' if '@property' in decorator_names else 'function'
-        if any(_match(name, self.ignore_decorators)
-               for name in decorator_names):
-            self._log('Ignoring {} "{}" (decorator whitelisted)'.format(
-                    typ, node.name))
-        elif typ == 'property':
+        decorator_names = [
+            utils.get_decorator_name(decorator)
+            for decorator in node.decorator_list
+        ]
+
+        first_arg = node.args.args[0].arg if node.args.args else None
+
+        if "@property" in decorator_names:
+            typ = "property"
+        elif (
+            "@staticmethod" in decorator_names
+            or "@classmethod" in decorator_names
+            or first_arg == "self"
+        ):
+            typ = "method"
+        else:
+            typ = "function"
+
+        if any(
+            _match(name, self.ignore_decorators) for name in decorator_names
+        ):
+            self._log(f'Ignoring {typ} "{node.name}" (decorator whitelisted)')
+        elif typ == "property":
             self._define(self.defined_props, node.name, node)
-        elif '@pyqtSlot' in decorator_names or '@Slot' in decorator_names:
-            # @pyqtSlot and @Slot support - eric6
-            self._define(self.defined_slots, node.name, node)
-        else:
-            # Function is not a property.
+        elif typ == "method":
             self._define(
-                self.defined_funcs, node.name, node,
-                ignore=_ignore_function)
-
-        # Detect *args and **kwargs parameters. Python 3 recognizes them
-        # in visit_Name. For Python 2 we use this workaround. We can't
-        # use visit_arguments, because its node has no lineno.
-        for param in [node.args.vararg, node.args.kwarg]:
-            if param and isinstance(param, str):
-                self._define_variable(param, node, confidence=100)
+                self.defined_methods, node.name, node, ignore=_ignore_method
+            )
+        else:
+            self._define(
+                self.defined_funcs, node.name, node, ignore=_ignore_function
+            )
 
     def visit_If(self, node):
-        self._handle_conditional_node(node, 'if')
+        self._handle_conditional_node(node, "if")
+
+    def visit_IfExp(self, node):
+        self._handle_conditional_node(node, "ternary")
 
     def visit_Import(self, node):
         self._add_aliases(node)
 
     def visit_ImportFrom(self, node):
-        if node.module != '__future__':
+        if node.module != "__future__":
             self._add_aliases(node)
 
     def visit_Name(self, node):
-        if (isinstance(node.ctx, ast.Load) and
-                node.id not in IGNORED_VARIABLE_NAMES):
+        if (
+            isinstance(node.ctx, ast.Load)
+            and node.id not in IGNORED_VARIABLE_NAMES
+        ):
             self.used_names.add(node.id)
         elif isinstance(node.ctx, (ast.Param, ast.Store)):
             self._define_variable(node.id, node)
 
-    def visit_Str(self, node):
-        """
-        Parse variable names in format strings:
-
-        '%(my_var)s' % locals()
-        '{my_var}'.format(**locals())
-
-        """
-        # Old format strings.
-        self.used_names |= set(re.findall(r'\%\((\w+)\)', node.s))
-
-        def is_identifier(s):
-            return bool(re.match(r'[a-zA-Z_][a-zA-Z0-9_]*', s))
-
-        # New format strings.
-        parser = string.Formatter()
-        try:
-            names = [name for _, name, _, _ in parser.parse(node.s) if name]
-        except ValueError:
-            # Invalid format string.
-            names = []
-
-        for field_name in names:
-            # Remove brackets and contents: "a[0][b].c[d].e" -> "a.c.e".
-            # "a.b.c" -> name = "a", attributes = ["b", "c"]
-            name_and_attrs = re.sub(r'\[\w*\]', '', field_name).split('.')
-            name = name_and_attrs[0]
-            if is_identifier(name):
-                self.used_names.add(name)
-            for attr in name_and_attrs[1:]:
-                if is_identifier(attr):
-                    self.used_attrs.add(attr)
-
     def visit_While(self, node):
-        self._handle_conditional_node(node, 'while')
+        self._handle_conditional_node(node, "while")
 
     def visit(self, node):
-        method = 'visit_' + node.__class__.__name__
+        method = "visit_" + node.__class__.__name__
         visitor = getattr(self, method, None)
         if self.verbose:
-            lineno = getattr(node, 'lineno', 1)
-            line = self.code[lineno - 1] if self.code else ''
+            lineno = getattr(node, "lineno", 1)
+            line = self.code[lineno - 1] if self.code else ""
             self._log(lineno, ast.dump(node), line)
         if visitor:
             visitor(node)
+
+        # There isn't a clean subset of node types that might have type
+        # comments, so just check all of them.
+        type_comment = getattr(node, "type_comment", None)
+        if type_comment is not None:
+            mode = (
+                "func_type"
+                if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))
+                else "eval"
+            )
+            self.visit(
+                ast.parse(type_comment, filename="<type_comment>", mode=mode)
+            )
+
         return self.generic_visit(node)
 
     def _handle_ast_list(self, ast_list):
@@ -496,8 +649,9 @@
         Find unreachable nodes in the given sequence of ast nodes.
         """
         for index, node in enumerate(ast_list):
-            if isinstance(node, (ast.Break, ast.Continue, ast.Raise,
-                                 ast.Return)):
+            if isinstance(
+                node, (ast.Break, ast.Continue, ast.Raise, ast.Return)
+            ):
                 try:
                     first_unreachable_node = ast_list[index + 1]
                 except IndexError:
@@ -508,9 +662,9 @@
                     class_name,
                     first_unreachable_node,
                     last_node=ast_list[-1],
-                    message="unreachable code after '{class_name}'".format(
-                        **locals()),
-                    confidence=100)
+                    message=f"unreachable code after '{class_name}'",
+                    confidence=100,
+                )
                 return
 
     def generic_visit(self, node):
@@ -525,54 +679,18 @@
                 self.visit(value)
 
 
-def _parse_args():
-    def csv(exclude):
-        return exclude.split(',')
-
-    usage = "%(prog)s [options] PATH [PATH ...]"
-    version = "vulture {0}".format(__version__)
-    glob_help = 'Patterns may contain glob wildcards (*, ?, [abc], [!abc]).'
-    parser = argparse.ArgumentParser(prog='vulture', usage=usage)
-    parser.add_argument(
-        'paths', nargs='+', metavar='PATH',
-        help='Paths may be Python files or directories. For each directory'
-        ' Vulture analyzes all contained *.py files.')
-    parser.add_argument(
-        '--exclude', metavar='PATTERNS', type=csv,
-        help='Comma-separated list of paths to ignore (e.g.,'
-        ' "*settings.py,docs/*.py"). {glob_help} A PATTERN without glob'
-        ' wildcards is treated as *PATTERN*.'.format(**locals()))
-    parser.add_argument(
-        '--ignore-decorators', metavar='PATTERNS', type=csv,
-        help='Comma-separated list of decorators. Functions and classes using'
-        ' these decorators are ignored (e.g., "@app.route,@require_*").'
-        ' {glob_help}'.format(**locals()))
-    parser.add_argument(
-        '--ignore-names', metavar='PATTERNS', type=csv, default=None,
-        help='Comma-separated list of names to ignore (e.g., "visit_*,do_*").'
-        ' {glob_help}'.format(**locals()))
-    parser.add_argument(
-        '--make-whitelist', action='store_true',
-        help='Report unused code in a format that can be added to a'
-        ' whitelist module.')
-    parser.add_argument(
-        '--min-confidence', type=int, default=0,
-        help='Minimum confidence (between 0 and 100) for code to be'
-        ' reported as unused.')
-    parser.add_argument(
-        "--sort-by-size", action="store_true",
-        help='Sort unused functions and classes by their lines of code.')
-    parser.add_argument('-v', '--verbose', action='store_true')
-    parser.add_argument('--version', action='version', version=version)
-    return parser.parse_args()
-
-
 def main():
-    args = _parse_args()
-    vulture = Vulture(verbose=args.verbose, ignore_names=args.ignore_names,
-                      ignore_decorators=args.ignore_decorators)
-    vulture.scavenge(args.paths, exclude=args.exclude)
-    sys.exit(vulture.report(
-        min_confidence=args.min_confidence,
-        sort_by_size=args.sort_by_size,
-        make_whitelist=args.make_whitelist))
+    config = make_config()
+    vulture = Vulture(
+        verbose=config["verbose"],
+        ignore_names=config["ignore_names"],
+        ignore_decorators=config["ignore_decorators"],
+    )
+    vulture.scavenge(config["paths"], exclude=config["exclude"])
+    sys.exit(
+        vulture.report(
+            min_confidence=config["min_confidence"],
+            sort_by_size=config["sort_by_size"],
+            make_whitelist=config["make_whitelist"],
+        )
+    )
diff -r d7a6b7ea640d -r 3c2922b45a9f VultureChecker/vulture/lines.py
--- a/VultureChecker/vulture/lines.py	Wed Dec 30 11:02:01 2020 +0100
+++ b/VultureChecker/vulture/lines.py	Sun Apr 25 16:13:53 2021 +0200
@@ -1,4 +1,5 @@
 import ast
+import sys
 
 
 def _get_last_child_with_lineno(node):
@@ -15,11 +16,11 @@
     list of nodes, we return the last one.
 
     """
-    ignored_fields = set(['ctx', 'decorator_list', 'names', 'returns'])
+    ignored_fields = {"ctx", "decorator_list", "names", "returns"}
     fields = node._fields
     # The fields of ast.Call are in the wrong order.
     if isinstance(node, ast.Call):
-        fields = ('func', 'args', 'starargs', 'keywords', 'kwargs')
+        fields = ("func", "args", "starargs", "keywords", "kwargs")
     for name in reversed(fields):
         if name in ignored_fields:
             continue
@@ -63,3 +64,20 @@
             except AttributeError:
                 pass
         node = last_child
+
+
+def get_first_line_number(node):
+    """
+    From Python 3.8 onwards, lineno for decorated objects is the line at which
+    the object definition starts, which is different from what Python < 3.8
+    reported -- the lineno of the first decorator. To preserve this behaviour
+    of Vulture for newer Python versions, which is also more accurate for
+    counting the size of the unused code chunk (if the property is unused, we
+    also don't need it's decorators), we return the lineno of the first
+    decorator, if there are any.
+    """
+    if sys.version_info >= (3, 8):
+        decorators = getattr(node, "decorator_list", [])
+        if decorators:
+            return decorators[0].lineno
+    return node.lineno
diff -r d7a6b7ea640d -r 3c2922b45a9f VultureChecker/vulture/noqa.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/VultureChecker/vulture/noqa.py	Sun Apr 25 16:13:53 2021 +0200
@@ -0,0 +1,44 @@
+from collections import defaultdict
+import re
+
+NOQA_REGEXP = re.compile(
+    # Use the same regex as flake8 does.
+    # https://gitlab.com/pycqa/flake8/-/tree/master/src/flake8/defaults.py
+    # We're looking for items that look like this:
+    # `# noqa`
+    # `# noqa: E123`
+    # `# noqa: E123,W451,F921`
+    # `# NoQA: E123,W451,F921`
+    r"# noqa(?::[\s]?(?P<codes>([A-Z]+[0-9]+(?:[,\s]+)?)+))?",
+    re.IGNORECASE,
+)
+
+NOQA_CODE_MAP = {
+    # flake8 F401: module imported but unused.
+    "F401": "V104",
+    # flake8 F841: local variable is assigned to but never used.
+    "F841": "V107",
+}
+
+
+def _parse_error_codes(match):
+    # If no error code is specified, add the line to the "all" category.
+    return [
+        c.strip() for c in (match.groupdict()["codes"] or "all").split(",")
+    ]
+
+
+def parse_noqa(code):
+    noqa_lines = defaultdict(set)
+    for lineno, line in enumerate(code, start=1):
+        match = NOQA_REGEXP.search(line)
+        if match:
+            for error_code in _parse_error_codes(match):
+                error_code = NOQA_CODE_MAP.get(error_code, error_code)
+                noqa_lines[error_code].add(lineno)
+    return noqa_lines
+
+
+def ignore_line(noqa_lines, lineno, error_code):
+    """Check if the reported line is annotated with "# noqa"."""
+    return lineno in noqa_lines[error_code] or lineno in noqa_lines["all"]
diff -r d7a6b7ea640d -r 3c2922b45a9f VultureChecker/vulture/utils.py
--- a/VultureChecker/vulture/utils.py	Wed Dec 30 11:02:01 2020 +0100
+++ b/VultureChecker/vulture/utils.py	Sun Apr 25 16:13:53 2021 +0200
@@ -1,12 +1,8 @@
 import ast
-import codecs
 import os
 import sys
 import tokenize
 
-# Encoding to use when converting input files to unicode.
-ENCODING = 'utf-8'
-
 
 class VultureInputException(Exception):
     pass
@@ -50,10 +46,11 @@
 
 
 def format_path(path):
-    if not path:
+    try:
+        return path.relative_to(os.curdir)
+    except ValueError:
+        # Path is not below the current directory.
         return path
-    relpath = os.path.relpath(path)
-    return relpath if not relpath.startswith('..') else path
 
 
 def get_decorator_name(decorator):
@@ -64,46 +61,39 @@
         parts.append(decorator.attr)
         decorator = decorator.value
     parts.append(decorator.id)
-    return '@' + '.'.join(reversed(parts))
+    return "@" + ".".join(reversed(parts))
 
 
-def get_modules(paths, toplevel=True):
-    """Take files from the command line even if they don't end with .py."""
+def get_modules(paths):
+    """Retrieve Python files to check.
+
+    Loop over all given paths, abort if any ends with .pyc and add collect
+    the other given files (even those not ending with .py) and all .py
+    files under the given directories.
+
+    """
     modules = []
     for path in paths:
-        path = os.path.abspath(path)
-        if toplevel and path.endswith('.pyc'):
-            sys.exit('.pyc files are not supported: {0}'.format(path))
-        if os.path.isfile(path) and (path.endswith('.py') or toplevel):
-            modules.append(path)
-        elif os.path.isdir(path):
-            subpaths = [
-                os.path.join(path, filename)
-                for filename in sorted(os.listdir(path))]
-            modules.extend(get_modules(subpaths, toplevel=False))
-        elif toplevel:
-            sys.exit('Error: {0} could not be found.'.format(path))
+        path = path.resolve()
+        if path.is_file():
+            if path.suffix == ".pyc":
+                sys.exit(f"Error: *.pyc files are not supported: {path}")
+            else:
+                modules.append(path)
+        elif path.is_dir():
+            modules.extend(path.rglob("*.py"))
+        else:
+            sys.exit(f"Error: {path} could not be found.")
     return modules
 
 
 def read_file(filename):
-    # Python >= 3.2
     try:
         # Use encoding detected by tokenize.detect_encoding().
         with tokenize.open(filename) as f:
             return f.read()
     except (SyntaxError, UnicodeDecodeError) as err:
         raise VultureInputException(err)
-    except AttributeError:
-        # tokenize.open was added in Python 3.2.
-        pass
-
-    # Python < 3.2
-    try:
-        with codecs.open(filename, encoding=ENCODING) as f:
-            return f.read()
-    except UnicodeDecodeError as err:
-        raise VultureInputException(err)
 
 
 class LoggingList(list):
@@ -114,7 +104,7 @@
 
     def append(self, item):
         if self._verbose:
-            print('define {0} "{1}"'.format(self.typ, item.name))
+            print(f'define {self.typ} "{item.name}"')
         list.append(self, item)
 
 
@@ -126,5 +116,5 @@
 
     def add(self, name):
         if self._verbose:
-            print('use {0} "{1}"'.format(self.typ, name))
+            print(f'use {self.typ} "{name}"')
         set.add(self, name)
diff -r d7a6b7ea640d -r 3c2922b45a9f VultureChecker/vulture/version.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/VultureChecker/vulture/version.py	Sun Apr 25 16:13:53 2021 +0200
@@ -0,0 +1,1 @@
+__version__ = "2.3"
diff -r d7a6b7ea640d -r 3c2922b45a9f VultureChecker/vulture/whitelists/argparse_whitelist.py
--- a/VultureChecker/vulture/whitelists/argparse_whitelist.py	Wed Dec 30 11:02:01 2020 +0100
+++ b/VultureChecker/vulture/whitelists/argparse_whitelist.py	Sun Apr 25 16:13:53 2021 +0200
@@ -1,7 +1,6 @@
 import argparse
 
-_ = argparse.ArgumentParser()
-_.epilog
+argparse.ArgumentParser().epilog
 
-argparse.ArgumentDefaultsHelpFormatter._fill_text
-argparse.ArgumentDefaultsHelpFormatter._get_help_string
+argparse.ArgumentDefaultsHelpFormatter("prog")._fill_text
+argparse.ArgumentDefaultsHelpFormatter("prog")._get_help_string
diff -r d7a6b7ea640d -r 3c2922b45a9f VultureChecker/vulture/whitelists/ast_whitelist.py
--- a/VultureChecker/vulture/whitelists/ast_whitelist.py	Wed Dec 30 11:02:01 2020 +0100
+++ b/VultureChecker/vulture/whitelists/ast_whitelist.py	Sun Apr 25 16:13:53 2021 +0200
@@ -17,6 +17,7 @@
 whitelist_node_visitor.visit_Call
 whitelist_node_visitor.visit_ClassDef
 whitelist_node_visitor.visit_Compare
+whitelist_node_visitor.visit_Constant
 whitelist_node_visitor.visit_Delete
 whitelist_node_visitor.visit_Dict
 whitelist_node_visitor.visit_DictComp
diff -r d7a6b7ea640d -r 3c2922b45a9f VultureChecker/vulture/whitelists/collections_whitelist.py
--- a/VultureChecker/vulture/whitelists/collections_whitelist.py	Wed Dec 30 11:02:01 2020 +0100
+++ b/VultureChecker/vulture/whitelists/collections_whitelist.py	Sun Apr 25 16:13:53 2021 +0200
@@ -1,4 +1,4 @@
 import collections
 
 # To free memory, the "default_factory" attribute can be set to None.
-collections.defaultdict.default_factory
+collections.defaultdict().default_factory
diff -r d7a6b7ea640d -r 3c2922b45a9f VultureChecker/vulture/whitelists/ctypes_whitelist.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/VultureChecker/vulture/whitelists/ctypes_whitelist.py	Sun Apr 25 16:13:53 2021 +0200
@@ -0,0 +1,8 @@
+from ctypes import _CFuncPtr
+from ctypes import _Pointer
+
+_CFuncPtr.argtypes
+_CFuncPtr.errcheck
+_CFuncPtr.restype
+
+_Pointer.contents
diff -r d7a6b7ea640d -r 3c2922b45a9f VultureChecker/vulture/whitelists/logging_whitelist.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/VultureChecker/vulture/whitelists/logging_whitelist.py	Sun Apr 25 16:13:53 2021 +0200
@@ -0,0 +1,5 @@
+import logging
+
+logging.Filter.filter
+logging.getLogger().propagate
+logging.StreamHandler.emit
diff -r d7a6b7ea640d -r 3c2922b45a9f VultureChecker/vulture/whitelists/string_whitelist.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/VultureChecker/vulture/whitelists/string_whitelist.py	Sun Apr 25 16:13:53 2021 +0200
@@ -0,0 +1,10 @@
+import string
+
+string.Formatter.check_unused_args
+string.Formatter.convert_field
+string.Formatter.format
+string.Formatter.format_field
+string.Formatter.get_field
+string.Formatter.get_value
+string.Formatter.parse
+string.Formatter.vformat
diff -r d7a6b7ea640d -r 3c2922b45a9f VultureChecker/vulture/whitelists/sys_whitelist.py
--- a/VultureChecker/vulture/whitelists/sys_whitelist.py	Wed Dec 30 11:02:01 2020 +0100
+++ b/VultureChecker/vulture/whitelists/sys_whitelist.py	Sun Apr 25 16:13:53 2021 +0200
@@ -1,5 +1,7 @@
 import sys
 
+sys.excepthook
+
 # Never report redirected streams as unused.
 sys.stderr
 sys.stdin
diff -r d7a6b7ea640d -r 3c2922b45a9f VultureChecker/vulture/whitelists/unittest_whitelist.py
--- a/VultureChecker/vulture/whitelists/unittest_whitelist.py	Wed Dec 30 11:02:01 2020 +0100
+++ b/VultureChecker/vulture/whitelists/unittest_whitelist.py	Sun Apr 25 16:13:53 2021 +0200
@@ -1,4 +1,4 @@
-from unittest import TestCase
+from unittest import mock, TestCase
 
 TestCase.setUp
 TestCase.tearDown
@@ -10,17 +10,7 @@
 TestCase.failureException
 TestCase.longMessage
 TestCase.maxDiff
-try:
-    # new in Python 3.4
-    TestCase.subTest
-except AttributeError:
-    pass
+TestCase.subTest
 
-try:
-    # unittest.mock was introduced in Python 3.3
-    from unittest import mock
-except ImportError:
-    pass
-else:
-    mock.Mock.return_value
-    mock.Mock.side_effect
+mock.Mock.return_value
+mock.Mock.side_effect
diff -r d7a6b7ea640d -r 3c2922b45a9f VultureChecker/vulture/whitelists/whitelist_utils.py
--- a/VultureChecker/vulture/whitelists/whitelist_utils.py	Wed Dec 30 11:02:01 2020 +0100
+++ b/VultureChecker/vulture/whitelists/whitelist_utils.py	Sun Apr 25 16:13:53 2021 +0200
@@ -25,6 +25,7 @@
     correct, but can also be executed.
 
     """
+
     def __getattr__(self, _):
         pass
 

eric ide

mercurial