SCR102: unused-import
Imported name is never used
| Code | SCR102 |
| Name | unused-import |
| Category | Scripts (SCR) |
| Default severity | warning |
| Enabled by default | yes |
| Fix available | sometimes |
| Fix safety | safe or unsafe, depending on the finding (see below) |
| ign-lint equivalent | none |
What it does
Section titled “What it does”Reports imports whose name is never read. Usage is flow-sensitive: an import that is rebound before any read is unused even if the name is read later.
Reads are resolved the way Jython compiles them: a class body reads a
name it binds from the class namespace and, before the class binds it,
from the module (x = m.pi before m = 1 in the class reads the
module's m); private names in class bodies are mangled (__m in class
D reads _D__m); and the names the interpreter reads by itself count:
__metaclass__ for a class without bases, AssertionError for assert,
__name__ for class and function definitions.
Not reported:
from __future__ import ...;- names listed in the module's
__all__(and every module-level import when__all__is built dynamically); - imports inside a
trywhose handlers catchImportError(availability checks such astry: import json except ImportError: json = None); - scopes that read their names dynamically (
locals(),vars(),eval,exec), every scope of a module with code that reads or writes a frame's or a module's names by itself (sys._getframe(),.f_locals,.tb_frame,e = eval,import __builtin__; the table is indocs/internal/fix-safety.md, "Frame routes: findings and fixes"), and module-level imports when any code of the module may read globals by name (globals(),eval,execfile,exec,sys.modules, frames,__dict__,func_globals). Reflection or introspection that is only a first hop (a Java member that leads to reflection such as.type,getattrwith a computed name,sys.exc_info(), line numbers) does not silence the rule; it makes the fix unsafe; - imports in a class body: they are attributes of the class (
self.os,C.os), used in ways iglint cannot follow (pyflakes and pylint do not report them either); from x import yat module level of a library module: other scripts may use it aslibrary.module.y(re-export). Plainimport xis still reported there.
Why is this bad?
Section titled “Why is this bad?”Unused imports slow down module loading (library modules are imported in every scope that uses them) and hide which dependencies a script really has. They also often point at a typo where the import was meant to be used.
Example
Section titled “Example”import jsonfrom java.lang import Thread
def onMessageReceived(self, payload): self.view.custom.data = system.util.jsonDecode(payload)Use instead:
def onMessageReceived(self, payload): self.view.custom.data = system.util.jsonDecode(payload)Removes the unused names from the import statement, or the statement when
none of its names is used (replaced by pass when it is the only
statement of its block; when every statement of a block is removed, the
last one becomes pass). The safe fix empties the statement's lines and
keeps them, so no line number changes; the unsafe fix removes the lines.
Safe only when all of these hold:
- the import is known to succeed and to do nothing outside the importing
module, so that removing it cannot change what runs: a Java class or
package of the vetted JDK packages that was measured to exist in the
Ignition image, or a plain
import xof a standard library module that is known to have no import-time effects in Jython 2.7 (os,re,json,datetime,math,time, ...) and was measured to import, orfrom x import yof such a module when it is not a package andywas measured to exist; and no library package of the project (or of a project inheriting from it) has the same top-level name; - the module has no reflection or introspection route of any kind
(
iglint_python_semantic::dynamic: the routes that silence the rule, and the first hops that do not, such as.typeorgetattr(o, name)): code reached through one may read the import by name. The reason names the route; - the module uses only vetted code: every module, Java class, builtin
and module attribute it uses is on iglint's allowlist, and every
library module it imports or calls is vetted too (see
docs/internal/fix-safety.md). Anything else may read the module's names in ways iglint does not know (cPickle.loadscan callglobals()in the caller's frame); - the module calls no library name that is not a plain function, class
or literal constant (it may be
evalorlocalsunder another name); - the import is not the only binding of a name that is still read in its
scope or a scope nested in it (
print len(x)beforeimport math as lenin a function raisesUnboundLocalError; without the import it calls the builtin); - the import does not come before a
from __future__import; - when the whole statement goes from a library module, nothing may read
__package__: there every import that runs binds it (toNone, in Ignition 8.3), so removing the only one turns a read into aNameError. A read or binding of__package__or__warningregistry__in the module, a binding of__name__, code that reads the module's globals by name, or another resource that reads them, reads the module's__dict__or uses the module as a value makes the fix unsafe. (Event scripts run without__name__: their imports bind nothing.)
Unsafe otherwise: importing a project library module runs its top level,
other Java classes run static initializers, some standard modules start
threads (socket) or configure logging (logging), and a dotted import
makes the submodule an attribute of its package for every other script.
Also unsafe for top-level imports of library modules when iglint cannot
see every use of the module's names (the module is used as a value,
references are computed at run time, a resource of the project uses code
that is not vetted, the project is inheritable or has scripts iglint does
not model, or the file is linted outside its project), and for an import
that may fail (a Java class or module attribute not measured to exist):
removing it turns a failing import into a successful one.
Like every fix, it is unsafe while code in the workspace may read the
files a fix edits (any file access, read, write, rename or delete, whose path
is not a literal outside the
workspace, or code that is not vetted); the project setting [fix] assume-scripts-do-not-read-workspace-files = true declares that no script
does (see docs/internal/fix-safety.md, "File reads").
Top-level names of a library module are attributes of the module: an
import that another resource uses as pkg.mod.name is not reported (also
through another library module's import: m0.util.name when m0 does
from pkg import util, at any depth; a module that another module's name
may hold, try: from pkg import util / except: util = None, counts as
used as a value, and so does every library module that a module used as a
value imports), nor
one that from pkg.mod import * in another resource binds there (every
name not starting with _, or the names of __all__; any name when
__all__ is changed in a way not read, such as __all__[:] = [...],
__all__[1] = 'json', __all__.insert(...), __all__ += [...], an
alias of it or a change from a function, and when its one binding is not
an assignment to the bare name: from x import __all__, for __all__ in ..., with c as __all__, __all__, y = ...), nor one that another resource
deletes (del pkg.mod.name: without the binding, a KeyError on 8.3.3)
or changes with an augmented assignment (pkg.mod.name += 1 reads it
first), also through aliases and other modules' imports. An assignment
from another resource (pkg.mod.name = 5, also as a for, except or
tuple target) or from a function of the module through global
replaces what the import binds: the import is reported, but its fix is
unsafe. A resource that
reaches the module object other than by its names, with a special
attribute (pkg.mod.__dict__, pkg.mod.__findattr_ex__(n),
pkg.mod.__getattribute__('name') counts as pkg.mod.name) or a member
of Ignition's module object (pkg.mod.dict, pkg.mod.getDict()), uses
the module as a value: none of its imports is reported (see
docs/internal/fix-safety.md, "Library module objects"). While a script
of the project does not parse, what it will use is not known: the fix of
a top-level import of a library module is unsafe.
pylint
Section titled “pylint”Corresponds to W0611 unused-import. Differences: iglint also reports an
import that is rebound before any read (pylint counts any read of the
name), and does not report availability checks in try/except ImportError or from x import y re-exports in library modules (pylint
does).
Generated from the rule metadata by iglint generate-docs; iglint rule SCR102 prints the same page in the terminal. Source: docs/rules/SCR102.md.