Sat, 04 Dec 2021 18:06:17 +0100
Finished implementing a checker for import statements (unnecessary alias, banned relative and banned modules).
--- a/eric7/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleCheckerDialog.py Sat Dec 04 17:41:18 2021 +0100 +++ b/eric7/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleCheckerDialog.py Sat Dec 04 18:06:17 2021 +0100 @@ -405,6 +405,35 @@ self.__statistics["_IssuesFixed"] = 0 self.__statistics["_SecurityOK"] = 0 + def __getBanRelativeImportsValue(self): + """ + Private method to get the value corresponding the selected button. + + @return value for the BanRelativeImports argument + @rtype str + """ + if self.banParentsButton.isChecked(): + return "parents" + elif self.banAllButton.isChecked(): + return "true" + else: + return "" + + def __setBanRelativeImports(self, value): + """ + Private method to set the button according to the ban relative imports + setting. + + @param value value of the ban relative imports setting + @type str + """ + if value == "parents": + self.banParentsButton.setChecked(True) + elif value == "true": + self.banAllButton.setChecked(True) + else: + self.allowAllButton.setChecked(True) + def prepare(self, fileList, project): """ Public method to prepare the dialog with a list of filenames. @@ -549,6 +578,8 @@ if "ImportsChecker" not in self.__data: self.__data["ImportsChecker"] = { "ApplicationPackageNames": [], + "BannedModules": [], + "BanRelativeImports": "", } self.__initCategoriesList(self.__data["EnabledCheckerCategories"]) @@ -815,6 +846,9 @@ importsArgs = { "ApplicationPackageNames": sorted(self.appPackagesEdit.toPlainText().split()), + "BannedModules": + sorted(self.bannedModulesEdit.toPlainText().split()), + "BanRelativeImports": self.__getBanRelativeImportsValue(), } self.__options = [excludeMessages, includeMessages, repeatMessages, @@ -1251,7 +1285,10 @@ }, "ImportsChecker": { "ApplicationPackageNames": - sorted(self.appPackagesEdit.toPlainText().split()) + sorted(self.appPackagesEdit.toPlainText().split()), + "BannedModules": + sorted(self.bannedModulesEdit.toPlainText().split()), + "BanRelativeImports": self.__getBanRelativeImportsValue(), }, } if ( @@ -1599,6 +1636,12 @@ self.appPackagesEdit.setPlainText(" ".join( sorted(Preferences.toList(Preferences.getSettings().value( "PEP8/ApplicationPackageNames", []))))) + self.bannedModulesEdit.setPlainText(" ".join( + sorted(Preferences.toList(Preferences.getSettings().value( + "PEP8/BannedModules", []))))) + self.__setBanRelativeImports( + Preferences.getSettings().value( + "PEP8/BanRelativeImports", "")) self.__cleanupData() @@ -1740,6 +1783,12 @@ Preferences.getSettings().setValue( "PEP8/ApplicationPackageNames", sorted(self.appPackagesEdit.toPlainText().split())) + Preferences.getSettings().setValue( + "PEP8/BannedModules", + sorted(self.bannedModulesEdit.toPlainText().split())) + Preferences.getSettings().setValue( + "PEP8/BanRelativeImports", + self.__getBanRelativeImportsValue()) @pyqtSlot() def on_resetDefaultButton_clicked(self): @@ -1868,6 +1917,10 @@ # Imports Checker Preferences.getSettings().setValue( "PEP8/ApplicationPackageNames", []) + Preferences.getSettings().setValue( + "PEP8/BannedModules", []) + Preferences.getSettings().setValue( + "PEP8/BanRelativeImports", "") # Update UI with default values self.on_loadDefaultButton_clicked()
--- a/eric7/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleCheckerDialog.ui Sat Dec 04 17:41:18 2021 +0100 +++ b/eric7/Plugins/CheckerPlugins/CodeStyleChecker/CodeStyleCheckerDialog.ui Sat Dec 04 18:06:17 2021 +0100 @@ -39,7 +39,7 @@ </property> <widget class="QWidget" name="globalOptionsTab"> <attribute name="title"> - <string>Global Options</string> + <string>Globals</string> </attribute> <layout class="QGridLayout" name="gridLayout_2"> <item row="0" column="0"> @@ -245,7 +245,7 @@ </widget> <widget class="QWidget" name="specificOptionsTab"> <attribute name="title"> - <string>Specific Options</string> + <string>Specifics</string> </attribute> <layout class="QVBoxLayout" name="verticalLayout_9"> <item> @@ -267,8 +267,8 @@ <rect> <x>0</x> <y>0</y> - <width>365</width> - <height>1152</height> + <width>611</width> + <height>923</height> </rect> </property> <layout class="QVBoxLayout" name="verticalLayout_4"> @@ -725,292 +725,332 @@ </layout> </widget> </item> - <item> - <widget class="QGroupBox" name="groupBox_3"> - <property name="title"> - <string>Code Complexity</string> - </property> - <layout class="QGridLayout" name="gridLayout_4"> - <item row="0" column="2" rowspan="2"> - <spacer name="horizontalSpacer_5"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>313</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item row="0" column="0"> - <widget class="QLabel" name="label_8"> - <property name="text"> - <string>Max. McCabe Complexity:</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QSpinBox" name="complexitySpinBox"> - <property name="toolTip"> - <string>Enter the maximum allowed code complexity (McCabe: 10)</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - <property name="maximum"> - <number>100</number> - </property> - <property name="value"> - <number>10</number> - </property> - </widget> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="label_13"> - <property name="text"> - <string>Max. Line Complexity:</string> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QSpinBox" name="lineComplexitySpinBox"> - <property name="toolTip"> - <string>Enter the maximum complexity (number of nodes) for a line of code</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - <property name="maximum"> - <number>100</number> - </property> - <property name="value"> - <number>15</number> - </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="label_14"> - <property name="text"> - <string>Max. Line Complexity Score:</string> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QSpinBox" name="lineComplexityScoreSpinBox"> - <property name="toolTip"> - <string>Enter the maximum allowed median for line complexity</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - <property name="maximum"> - <number>100</number> - </property> - <property name="value"> - <number>10</number> - </property> - </widget> - </item> - </layout> - </widget> - </item> - <item> - <widget class="QGroupBox" name="groupBox_10"> - <property name="title"> - <string>Type Annotations</string> - </property> - <layout class="QVBoxLayout" name="verticalLayout_3"> - <item> - <layout class="QGridLayout" name="gridLayout_6"> - <item row="0" column="0"> - <widget class="QLabel" name="label_18"> - <property name="text"> - <string>Min. Coverage:</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QSpinBox" name="minAnnotationsCoverageSpinBox"> - <property name="toolTip"> - <string>Enter the minimum percentage of type annotations</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - <property name="specialValueText"> - <string>off</string> - </property> - <property name="suffix"> - <string>%</string> - </property> - <property name="maximum"> - <number>100</number> - </property> - </widget> - </item> - <item row="0" column="2"> - <spacer name="horizontalSpacer_7"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>352</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item row="1" column="0"> - <widget class="QLabel" name="label_19"> - <property name="text"> - <string>Max. Complexity:</string> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QSpinBox" name="maxAnnotationsComplexitySpinBox"> - <property name="toolTip"> - <string>Enter the maximum type annotation complexity</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - <property name="minimum"> - <number>1</number> - </property> - <property name="maximum"> - <number>9</number> - </property> - </widget> - </item> - <item row="2" column="0"> - <widget class="QLabel" name="label_32"> - <property name="text"> - <string>Max. Length:</string> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QSpinBox" name="maxAnnotationsLengthSpinBox"> - <property name="toolTip"> - <string>Enter the maximum type annotation length</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - <property name="minimum"> - <number>1</number> - </property> - <property name="maximum"> - <number>15</number> - </property> - </widget> - </item> - </layout> - </item> - <item> - <layout class="QGridLayout" name="gridLayout_9"> - <item row="0" column="0"> - <widget class="QCheckBox" name="suppressNoneReturningCheckBox"> - <property name="toolTip"> - <string>Select to not report functions without returns or with only bare returns</string> - </property> - <property name="text"> - <string>Suppress 'None' return</string> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QCheckBox" name="suppressDummyArgsCheckBox"> - <property name="toolTip"> - <string>Select to not report dummy (i.e. '_') arguments</string> - </property> - <property name="text"> - <string>Suppress Dummy Arguments</string> - </property> - </widget> - </item> - <item row="1" column="0"> - <widget class="QCheckBox" name="allowUntypedDefsCheckBox"> - <property name="toolTip"> - <string>Select to not report dynamically typed functions</string> - </property> - <property name="text"> - <string>Allow Untyped Functions</string> - </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QCheckBox" name="allowUntypedNestedCheckBox"> - <property name="toolTip"> - <string>Select to not report dynamically typed nested functions</string> - </property> - <property name="text"> - <string>Allow Untyped Nested Functions</string> - </property> - </widget> - </item> - <item row="2" column="0" colspan="2"> - <widget class="QCheckBox" name="mypyInitReturnCheckBox"> - <property name="toolTip"> - <string>Select to not report unhinted '__init__' return</string> - </property> - <property name="text"> - <string>Allow Untyped '__init__' function</string> - </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QLabel" name="label_33"> - <property name="text"> - <string>Dispatch Decorators:</string> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="dispatchDecoratorEdit"> - <property name="toolTip"> - <string>Enter the list of dispatch decorators separated by comma</string> - </property> - <property name="clearButtonEnabled"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="label_34"> - <property name="text"> - <string>Overload Decorators:</string> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="overloadDecoratorEdit"> - <property name="toolTip"> - <string>Enter the list of typing.overload decorators separated by comma</string> - </property> - <property name="clearButtonEnabled"> - <bool>true</bool> - </property> - </widget> - </item> - </layout> - </widget> - </item> </layout> </widget> </widget> </item> </layout> </widget> + <widget class="QWidget" name="codeComplexityTab"> + <attribute name="title"> + <string>Complexity</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_17"> + <item> + <widget class="QGroupBox" name="groupBox_3"> + <property name="title"> + <string>Code Complexity</string> + </property> + <layout class="QGridLayout" name="gridLayout_4"> + <item row="0" column="2" rowspan="2"> + <spacer name="horizontalSpacer_5"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>313</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label_8"> + <property name="text"> + <string>Max. McCabe Complexity:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QSpinBox" name="complexitySpinBox"> + <property name="toolTip"> + <string>Enter the maximum allowed code complexity (McCabe: 10)</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="maximum"> + <number>100</number> + </property> + <property name="value"> + <number>10</number> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_13"> + <property name="text"> + <string>Max. Line Complexity:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QSpinBox" name="lineComplexitySpinBox"> + <property name="toolTip"> + <string>Enter the maximum complexity (number of nodes) for a line of code</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="maximum"> + <number>100</number> + </property> + <property name="value"> + <number>15</number> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_14"> + <property name="text"> + <string>Max. Line Complexity Score:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QSpinBox" name="lineComplexityScoreSpinBox"> + <property name="toolTip"> + <string>Enter the maximum allowed median for line complexity</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="maximum"> + <number>100</number> + </property> + <property name="value"> + <number>10</number> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacer_8"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>802</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <widget class="QWidget" name="typeAnnotationsTab"> + <attribute name="title"> + <string>Annotations</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_18"> + <item> + <widget class="QGroupBox" name="groupBox_10"> + <property name="title"> + <string>Type Annotations</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <layout class="QGridLayout" name="gridLayout_6"> + <item row="0" column="0"> + <widget class="QLabel" name="label_18"> + <property name="text"> + <string>Min. Coverage:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QSpinBox" name="minAnnotationsCoverageSpinBox"> + <property name="toolTip"> + <string>Enter the minimum percentage of type annotations</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="specialValueText"> + <string>off</string> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="maximum"> + <number>100</number> + </property> + </widget> + </item> + <item row="0" column="2"> + <spacer name="horizontalSpacer_7"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>352</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_19"> + <property name="text"> + <string>Max. Complexity:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QSpinBox" name="maxAnnotationsComplexitySpinBox"> + <property name="toolTip"> + <string>Enter the maximum type annotation complexity</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>9</number> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_32"> + <property name="text"> + <string>Max. Length:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QSpinBox" name="maxAnnotationsLengthSpinBox"> + <property name="toolTip"> + <string>Enter the maximum type annotation length</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>15</number> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QGridLayout" name="gridLayout_9"> + <item row="0" column="0"> + <widget class="QCheckBox" name="suppressNoneReturningCheckBox"> + <property name="toolTip"> + <string>Select to not report functions without returns or with only bare returns</string> + </property> + <property name="text"> + <string>Suppress 'None' return</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QCheckBox" name="suppressDummyArgsCheckBox"> + <property name="toolTip"> + <string>Select to not report dummy (i.e. '_') arguments</string> + </property> + <property name="text"> + <string>Suppress Dummy Arguments</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QCheckBox" name="allowUntypedDefsCheckBox"> + <property name="toolTip"> + <string>Select to not report dynamically typed functions</string> + </property> + <property name="text"> + <string>Allow Untyped Functions</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QCheckBox" name="allowUntypedNestedCheckBox"> + <property name="toolTip"> + <string>Select to not report dynamically typed nested functions</string> + </property> + <property name="text"> + <string>Allow Untyped Nested Functions</string> + </property> + </widget> + </item> + <item row="2" column="0" colspan="2"> + <widget class="QCheckBox" name="mypyInitReturnCheckBox"> + <property name="toolTip"> + <string>Select to not report unhinted '__init__' return</string> + </property> + <property name="text"> + <string>Allow Untyped '__init__' function</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QLabel" name="label_33"> + <property name="text"> + <string>Dispatch Decorators:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="dispatchDecoratorEdit"> + <property name="toolTip"> + <string>Enter the list of dispatch decorators separated by comma</string> + </property> + <property name="clearButtonEnabled"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_34"> + <property name="text"> + <string>Overload Decorators:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="overloadDecoratorEdit"> + <property name="toolTip"> + <string>Enter the list of typing.overload decorators separated by comma</string> + </property> + <property name="clearButtonEnabled"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacer_9"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>596</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> <widget class="QWidget" name="securityOptionsTab"> <attribute name="title"> - <string>Security Options</string> + <string>Security</string> </attribute> <layout class="QGridLayout" name="gridLayout_7"> <item row="0" column="0"> @@ -1257,7 +1297,7 @@ <attribute name="title"> <string>Imports</string> </attribute> - <layout class="QVBoxLayout" name="verticalLayout_15"> + <layout class="QVBoxLayout" name="verticalLayout_16"> <item> <widget class="QGroupBox" name="groupBox_15"> <property name="title"> @@ -1275,7 +1315,79 @@ </widget> </item> <item> - <widget class="QPlainTextEdit" name="appPackagesEdit"/> + <widget class="QPlainTextEdit" name="appPackagesEdit"> + <property name="tabChangesFocus"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_16"> + <property name="title"> + <string>Banned Modules</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_15"> + <item> + <widget class="QLabel" name="label_36"> + <property name="text"> + <string>Enter the name of modules to be banned separated by a space character:</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPlainTextEdit" name="bannedModulesEdit"> + <property name="tabChangesFocus"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_17"> + <property name="title"> + <string>Ban Relative Imports</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_12"> + <item> + <widget class="QRadioButton" name="allowAllButton"> + <property name="toolTip"> + <string>Select to allow relative imports</string> + </property> + <property name="text"> + <string>Allow</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="banParentsButton"> + <property name="toolTip"> + <string>Select to ban relative imports of parents</string> + </property> + <property name="text"> + <string>Ban Parents Import</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="banAllButton"> + <property name="toolTip"> + <string>Select to ban all relative imports</string> + </property> + <property name="text"> + <string>Ban All</string> + </property> + </widget> </item> </layout> </widget> @@ -1691,10 +1803,10 @@ <tabstop>maxAnnotationsComplexitySpinBox</tabstop> <tabstop>maxAnnotationsLengthSpinBox</tabstop> <tabstop>suppressNoneReturningCheckBox</tabstop> + <tabstop>allowUntypedDefsCheckBox</tabstop> + <tabstop>mypyInitReturnCheckBox</tabstop> <tabstop>suppressDummyArgsCheckBox</tabstop> - <tabstop>allowUntypedDefsCheckBox</tabstop> <tabstop>allowUntypedNestedCheckBox</tabstop> - <tabstop>mypyInitReturnCheckBox</tabstop> <tabstop>dispatchDecoratorEdit</tabstop> <tabstop>overloadDecoratorEdit</tabstop> <tabstop>tmpDirectoriesEdit</tabstop> @@ -1708,6 +1820,10 @@ <tabstop>ecMediumRiskCombo</tabstop> <tabstop>typedExceptionsCheckBox</tabstop> <tabstop>appPackagesEdit</tabstop> + <tabstop>bannedModulesEdit</tabstop> + <tabstop>allowAllButton</tabstop> + <tabstop>banParentsButton</tabstop> + <tabstop>banAllButton</tabstop> <tabstop>startButton</tabstop> <tabstop>loadDefaultButton</tabstop> <tabstop>storeDefaultButton</tabstop>
--- a/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Imports/ImportNode.py Sat Dec 04 17:41:18 2021 +0100 +++ b/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Imports/ImportNode.py Sat Dec 04 18:06:17 2021 +0100 @@ -42,10 +42,7 @@ @exception ImportNodeException raised to indicate an invalid node was given to this class """ - if ( - self.nodeType not in (NodeTypeEnum.IMPORT, - NodeTypeEnum.IMPORT_FROM) - ): + if not isinstance(astNode, (ast.Import, ast.ImportFrom)): raise ImportNodeException( "Node type {0} not recognized".format(type(astNode)) )
--- a/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Imports/ImportsChecker.py Sat Dec 04 17:41:18 2021 +0100 +++ b/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Imports/ImportsChecker.py Sat Dec 04 18:06:17 2021 +0100 @@ -22,6 +22,9 @@ ## Imports order "I201", "I202", "I203", "I204", + + ## Various other import related + "I901", "I902", "I903", "I904", ] def __init__(self, source, filename, tree, select, ignore, expected, @@ -63,7 +66,8 @@ checkersWithCodes = [ (self.__checkLocalImports, ("I101", "I102", "I103")), - (self.__checkImportOrder, ("I201", "I202", "I203", "I204")) + (self.__checkImportOrder, ("I201", "I202", "I203", "I204")), + (self.__tidyImports, ("I901", "I902", "I903", "I904")), ] self.__checkers = [] @@ -321,3 +325,130 @@ return (node, "I204", ", ".join(expectedList)) return None + + ####################################################################### + ## Tidy imports + ## + ## adapted from: flake8-tidy-imports v4.5.0 + ####################################################################### + + def __tidyImports(self): + """ + Private method to check various other import related topics. + """ + self.__bannedModules = self.__args.get("BannedModules", []) + self.__banRelativeImports = self.__args.get("BanRelativeImports", "") + + ruleMethods = [] + if not self.__ignoreCode("I901"): + ruleMethods.append(self.__checkUnnecessaryAlias) + if ( + not self.__ignoreCode("I902") + and bool(self.__bannedModules) + ): + ruleMethods.append(self.__checkBannedImport) + if ( + (not self.__ignoreCode("I903") and + self.__banRelativeImports == "parents") or + (not self.__ignoreCode("I904") and + self.__banRelativeImports == "true") + ): + ruleMethods.append(self.__checkBannedRelativeImports) + + for node in ast.walk(self.__tree): + for method in ruleMethods: + method(node) + + def __checkUnnecessaryAlias(self, node): + """ + Private method to check unnecessary import aliases. + + @param node reference to the node to be checked + @type ast.AST + """ + if isinstance(node, ast.Import): + for alias in node.names: + if "." not in alias.name: + fromName = None + importedName = alias.name + else: + fromName, importedName = alias.name.rsplit(".", 1) + + if importedName == alias.asname: + if fromName: + rewritten = "from {0} import {1}".format( + fromName, importedName) + else: + rewritten = "import {0}".format(importedName) + + self.__error(node.lineno - 1, node.col_offset, "I901", + rewritten) + + elif isinstance(node, ast.ImportFrom): + for alias in node.names: + if alias.name == alias.asname: + rewritten = "from {0} import {1}".format( + node.module, alias.name) + + self.__error(node.lineno - 1, node.col_offset, "I901", + rewritten) + + def __checkBannedImport(self, node): + """ + Private method to check import of banned modules. + + @param node reference to the node to be checked + @type ast.AST + """ + if not bool(self.__bannedModules): + return + + if isinstance(node, ast.Import): + moduleNames = [alias.name for alias in node.names] + elif isinstance(node, ast.ImportFrom): + nodeModule = node.module or "" + moduleNames = [nodeModule] + for alias in node.names: + moduleNames.append("{0}.{1}".format(nodeModule, alias.name)) + else: + return + + # Sort from most to least specific paths. + moduleNames.sort(key=len, reverse=True) + + warned = set() + + for moduleName in moduleNames: + if moduleName in self.__bannedModules: + if any(mod.startswith(moduleName) for mod in warned): + # Do not show an error for this line if we already showed + # a more specific error. + continue + else: + warned.add(moduleName) + self.__error(node.lineno - 1, node.col_offset, "I902", + moduleName) + + def __checkBannedRelativeImports(self, node): + """ + Private method to check if relative imports are banned. + + @param node reference to the node to be checked + @type ast.AST + """ + if not self.__banRelativeImports: + return + + elif self.__banRelativeImports == "parents": + minNodeLevel = 1 + msgCode = "I903" + else: + minNodeLevel = 0 + msgCode = "I904" + + if ( + self.__banRelativeImports and + isinstance(node, ast.ImportFrom) and + node.level > minNodeLevel + ): + self.__error(node.lineno - 1, node.col_offset, msgCode)
--- a/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Imports/translations.py Sat Dec 04 17:41:18 2021 +0100 +++ b/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Imports/translations.py Sat Dec 04 18:06:17 2021 +0100 @@ -28,7 +28,7 @@ "I202": QCoreApplication.translate( "ImportsChecker", "Imported names are in the wrong order. " - "Should be {0}"), + "Should be '{0}'"), "I203": QCoreApplication.translate( "ImportsChecker", "Import statements should be combined. " @@ -36,12 +36,27 @@ "I204": QCoreApplication.translate( "ImportsChecker", "The names in __all__ are in the wrong order. " - "The order should be {0}"), + "The order should be '{0}'"), + + "I901": QCoreApplication.translate( + "ImportsChecker", + "unnecessary import alias - rewrite as '{0}'"), + "I902": QCoreApplication.translate( + "ImportsChecker", + "banned import '{0}' used"), + "I903": QCoreApplication.translate( + "ImportsChecker", + "relative imports from parent modules are banned"), + "I904": QCoreApplication.translate( + "ImportsChecker", + "relative imports are banned"), } _importsMessagesSampleArgs = { - "I201": ["import os", "import sys"], - "I202": ["copy, os, sys"], + "I201": ["import bar", "import foo"], + "I202": ["bar, baz, foo"], "I203": ["from foo import bar", "from foo import baz"], "I204": ["bar, baz, foo"], + "I901": ["from foo import bar"], + "I902": ["foo"], }