Removed, Added Modules¶
Errors are produced when modules are added to or removed from the namespace of a module within the public API.
external-module errors are used when the added/removed
module is outside of the public API root.
Example: adding internal module¶
Initial version of code:
# in kitchen/__init__.py def fry(eggs): return FryingPan().fry(eggs)
Updated version of code:
from kitchen.containers import cabinet def fry(eggs): return cabinet.find(FryingPan).fry(eggs)
As an (internal) module has been added to the namespace, an error will be produced:
kitchen/__init__.py:0: N210 module added: cabinet
This constitutes a minor API change since new code
could be written including
from kitchen import cabinet,
which would not work with the older version of the code.
Example: removing external module¶
Initial version of code uses
re module for regular expressions:
# in haystack/__init__.py import re class Haystack: def search(self, needle): return re.search(needle, self._content)
Updated version of code migrated to
import regex class Haystack: def search(self, needle): return regex.search(needle, self._content)
This change will produce an error for the major API change
of removing a formerly available module (as well as the
less severe change of adding a new module). Since
lives outside of the
haystack namespace, the module is
referred to as “external”.
haystack/__init__.py:0: B111 external module removed: re haystack/__init__.py:0: N211 external module added: regex
Adding a module is, of course, often expected when adding new functionality. However, it can also happen accidentally as a result of changing implementation.
The first example above shows how this can result in the same module becoming available under multiple paths. In that example, with the new version of the code, a client is able to write either of the following:
from kitchen import cabinet
from kitchen.containers import cabinet
Both appear valid, and there’s no hint to the client that
only available from
kitchen due to implementation details.
If backwards compatibility is valued, both imports must continue to work
even as the implementation is further revised, adding to the maintenance
Adding or removing external modules can also cause compatibility issues.
Consider the second example above. It seems unlikely that a client would
intentionally write code such as
from haystack import re to access the
re module. On the other hand, it’s quite easy for clients to accidentally
write code accessing external modules through other modules without realizing
it, as in example:
# 're' imported into this file's namespace here from haystack import * # ...later, some code using re re.search(pattern, value)