Fix import check interaction with namespace modules
When running hacking from an oslo.* lib, all other oslo.* libs get categorized as project because the import check was only looking at the base module. So when running in oslo.db, oslo.db should be categorized as project and oslo.config as third-party, but both were being seen as project because _get_import_type was only looking at the 'oslo' part of the import. This change extends _get_import_type to look at the entire module path and adds the necessary logic for handling namespace modules and non-module imports. Since this is fairly complex and can't be tested with doctests, standalone test cases are also added to verify the behavior of the new code. Note that prior to this change, oslo.db had 5 apparent H305 violations and 3 H307, which was incorrect. After this change, hacking correctly reports 3 H305 violations and 1 H307. Also, because some of the stdlib modules don't play nice with the new lookup method, this also includes a static list of stdlib modules that will also be used for py2/3 compatibility in a followup commit. Change-Id: Ibb8c982bafcd69c9d642e86aa357a3a11b4395af
This commit is contained in:
@@ -156,51 +156,329 @@ def hacking_import_rules(logical_line, physical_line, filename, noqa):
|
||||
" '%s' is a relative import" % logical_line)
|
||||
|
||||
|
||||
# Get the location of a known stdlib module
|
||||
_, p, _ = imp.find_module('imp')
|
||||
stdlib_path_prefix = os.path.dirname(p)
|
||||
module_cache = dict()
|
||||
|
||||
|
||||
def _find_module(module, path=None):
|
||||
mod_base = module
|
||||
parent_path = None
|
||||
while '.' in mod_base:
|
||||
first, _, mod_base = mod_base.partition('.')
|
||||
parent_path = path
|
||||
_, path, _ = imp.find_module(first, path)
|
||||
path = [path]
|
||||
try:
|
||||
_, path, _ = imp.find_module(mod_base, path)
|
||||
except ImportError:
|
||||
# NOTE(bnemec): There are two reasons we might get here: 1) A
|
||||
# non-module import and 2) an import of a namespace module that is
|
||||
# in the same namespace as the current project, which caused us to
|
||||
# recurse into the project namespace but fail to find the third-party
|
||||
# module. For 1), we won't be able to import it as a module, so we
|
||||
# return the parent module's path, but for 2) the import below should
|
||||
# succeed, so we re-raise the ImportError because the module was
|
||||
# legitimately not found in this path.
|
||||
try:
|
||||
__import__(module)
|
||||
except ImportError:
|
||||
# Non-module import, return the parent path if we have it
|
||||
if parent_path:
|
||||
return parent_path
|
||||
raise
|
||||
raise
|
||||
return path
|
||||
|
||||
|
||||
# List of all Python 2 stdlib modules - anything not in this list will be
|
||||
# allowed in either the stdlib or third-party groups to allow for Python 3
|
||||
# stdlib additions.
|
||||
# The list was generated via the following script, which is a variation on
|
||||
# the one found here:
|
||||
# http://stackoverflow.com/questions/6463918/how-can-i-get-a-list-of-all-the-python-standard-library-modules
|
||||
"""
|
||||
from distutils import sysconfig
|
||||
import os
|
||||
import sys
|
||||
|
||||
std_lib = sysconfig.get_python_lib(standard_lib=True)
|
||||
prefix_len = len(std_lib) + 1
|
||||
modules = ''
|
||||
line = '['
|
||||
mod_list = []
|
||||
for top, dirs, files in os.walk(std_lib):
|
||||
for name in files:
|
||||
if 'site-packages' not in top:
|
||||
if name == '__init__.py':
|
||||
full_name = top[prefix_len:].replace('/', '.')
|
||||
mod_list.append(full_name)
|
||||
elif name.endswith('.py'):
|
||||
full_name = top.replace('/', '.') + '.'
|
||||
full_name += name[:-3]
|
||||
full_name = full_name[prefix_len:]
|
||||
mod_list.append(full_name)
|
||||
elif name.endswith('.so') and top.endswith('lib-dynload'):
|
||||
full_name = name[:-3]
|
||||
if full_name.endswith('module'):
|
||||
full_name = full_name[:-6]
|
||||
mod_list.append(full_name)
|
||||
for name in sys.builtin_module_names:
|
||||
mod_list.append(name)
|
||||
mod_list.sort()
|
||||
for mod in mod_list:
|
||||
if len(line + mod) + 8 > 79:
|
||||
modules += '\n' + line
|
||||
line = ' '
|
||||
line += "'%s', " % mod
|
||||
print modules + ']'
|
||||
"""
|
||||
py2_stdlib = [
|
||||
'BaseHTTPServer', 'Bastion', 'CGIHTTPServer', 'ConfigParser', 'Cookie',
|
||||
'DocXMLRPCServer', 'HTMLParser', 'MimeWriter', 'Queue',
|
||||
'SimpleHTTPServer', 'SimpleXMLRPCServer', 'SocketServer', 'StringIO',
|
||||
'UserDict', 'UserList', 'UserString', '_LWPCookieJar',
|
||||
'_MozillaCookieJar', '__builtin__', '__future__', '__main__',
|
||||
'__phello__.foo', '_abcoll', '_ast', '_bisect', '_bsddb', '_codecs',
|
||||
'_codecs_cn', '_codecs_hk', '_codecs_iso2022', '_codecs_jp',
|
||||
'_codecs_kr', '_codecs_tw', '_collections', '_crypt', '_csv',
|
||||
'_ctypes', '_curses', '_curses_panel', '_elementtree', '_functools',
|
||||
'_hashlib', '_heapq', '_hotshot', '_io', '_json', '_locale',
|
||||
'_lsprof', '_multibytecodec', '_multiprocessing', '_osx_support',
|
||||
'_pyio', '_random', '_socket', '_sqlite3', '_sre', '_ssl',
|
||||
'_strptime', '_struct', '_symtable', '_sysconfigdata',
|
||||
'_threading_local', '_warnings', '_weakref', '_weakrefset', 'abc',
|
||||
'aifc', 'antigravity', 'anydbm', 'argparse', 'array', 'ast',
|
||||
'asynchat', 'asyncore', 'atexit', 'audiodev', 'audioop', 'base64',
|
||||
'bdb', 'binascii', 'binhex', 'bisect', 'bsddb', 'bsddb.db',
|
||||
'bsddb.dbobj', 'bsddb.dbrecio', 'bsddb.dbshelve', 'bsddb.dbtables',
|
||||
'bsddb.dbutils', 'bz2', 'cPickle', 'cProfile', 'cStringIO',
|
||||
'calendar', 'cgi', 'cgitb', 'chunk', 'cmath', 'cmd', 'code', 'codecs',
|
||||
'codeop', 'collections', 'colorsys', 'commands', 'compileall',
|
||||
'compiler', 'compiler.ast', 'compiler.consts', 'compiler.future',
|
||||
'compiler.misc', 'compiler.pyassem', 'compiler.pycodegen',
|
||||
'compiler.symbols', 'compiler.syntax', 'compiler.transformer',
|
||||
'compiler.visitor', 'contextlib', 'cookielib', 'copy', 'copy_reg',
|
||||
'crypt', 'csv', 'ctypes', 'ctypes._endian', 'ctypes.macholib',
|
||||
'ctypes.macholib.dyld', 'ctypes.macholib.dylib',
|
||||
'ctypes.macholib.framework', 'ctypes.util', 'ctypes.wintypes',
|
||||
'curses', 'curses.ascii', 'curses.has_key', 'curses.panel',
|
||||
'curses.textpad', 'curses.wrapper', 'datetime', 'dbhash', 'dbm',
|
||||
'decimal', 'difflib', 'dircache', 'dis', 'distutils',
|
||||
'distutils.archive_util', 'distutils.bcppcompiler',
|
||||
'distutils.ccompiler', 'distutils.cmd', 'distutils.command',
|
||||
'distutils.command.bdist', 'distutils.command.bdist_dumb',
|
||||
'distutils.command.bdist_msi', 'distutils.command.bdist_rpm',
|
||||
'distutils.command.bdist_wininst', 'distutils.command.build',
|
||||
'distutils.command.build_clib', 'distutils.command.build_ext',
|
||||
'distutils.command.build_py', 'distutils.command.build_scripts',
|
||||
'distutils.command.check', 'distutils.command.clean',
|
||||
'distutils.command.config', 'distutils.command.install',
|
||||
'distutils.command.install_data',
|
||||
'distutils.command.install_egg_info',
|
||||
'distutils.command.install_headers', 'distutils.command.install_lib',
|
||||
'distutils.command.install_scripts', 'distutils.command.register',
|
||||
'distutils.command.sdist', 'distutils.command.upload',
|
||||
'distutils.config', 'distutils.core', 'distutils.cygwinccompiler',
|
||||
'distutils.debug', 'distutils.dep_util', 'distutils.dir_util',
|
||||
'distutils.dist', 'distutils.emxccompiler', 'distutils.errors',
|
||||
'distutils.extension', 'distutils.fancy_getopt',
|
||||
'distutils.file_util', 'distutils.filelist', 'distutils.log',
|
||||
'distutils.msvc9compiler', 'distutils.msvccompiler',
|
||||
'distutils.spawn', 'distutils.sysconfig', 'distutils.text_file',
|
||||
'distutils.unixccompiler', 'distutils.util', 'distutils.version',
|
||||
'distutils.versionpredicate', 'dl', 'doctest', 'dumbdbm',
|
||||
'dummy_thread', 'dummy_threading', 'email', 'email._parseaddr',
|
||||
'email.base64mime', 'email.charset', 'email.encoders', 'email.errors',
|
||||
'email.feedparser', 'email.generator', 'email.header',
|
||||
'email.iterators', 'email.message', 'email.mime',
|
||||
'email.mime.application', 'email.mime.audio', 'email.mime.base',
|
||||
'email.mime.image', 'email.mime.message', 'email.mime.multipart',
|
||||
'email.mime.nonmultipart', 'email.mime.text', 'email.parser',
|
||||
'email.quoprimime', 'email.utils', 'encodings', 'encodings.aliases',
|
||||
'encodings.ascii', 'encodings.base64_codec', 'encodings.big5',
|
||||
'encodings.big5hkscs', 'encodings.bz2_codec', 'encodings.charmap',
|
||||
'encodings.cp037', 'encodings.cp1006', 'encodings.cp1026',
|
||||
'encodings.cp1140', 'encodings.cp1250', 'encodings.cp1251',
|
||||
'encodings.cp1252', 'encodings.cp1253', 'encodings.cp1254',
|
||||
'encodings.cp1255', 'encodings.cp1256', 'encodings.cp1257',
|
||||
'encodings.cp1258', 'encodings.cp424', 'encodings.cp437',
|
||||
'encodings.cp500', 'encodings.cp720', 'encodings.cp737',
|
||||
'encodings.cp775', 'encodings.cp850', 'encodings.cp852',
|
||||
'encodings.cp855', 'encodings.cp856', 'encodings.cp857',
|
||||
'encodings.cp858', 'encodings.cp860', 'encodings.cp861',
|
||||
'encodings.cp862', 'encodings.cp863', 'encodings.cp864',
|
||||
'encodings.cp865', 'encodings.cp866', 'encodings.cp869',
|
||||
'encodings.cp874', 'encodings.cp875', 'encodings.cp932',
|
||||
'encodings.cp949', 'encodings.cp950', 'encodings.euc_jis_2004',
|
||||
'encodings.euc_jisx0213', 'encodings.euc_jp', 'encodings.euc_kr',
|
||||
'encodings.gb18030', 'encodings.gb2312', 'encodings.gbk',
|
||||
'encodings.hex_codec', 'encodings.hp_roman8', 'encodings.hz',
|
||||
'encodings.idna', 'encodings.iso2022_jp', 'encodings.iso2022_jp_1',
|
||||
'encodings.iso2022_jp_2', 'encodings.iso2022_jp_2004',
|
||||
'encodings.iso2022_jp_3', 'encodings.iso2022_jp_ext',
|
||||
'encodings.iso2022_kr', 'encodings.iso8859_1', 'encodings.iso8859_10',
|
||||
'encodings.iso8859_11', 'encodings.iso8859_13',
|
||||
'encodings.iso8859_14', 'encodings.iso8859_15',
|
||||
'encodings.iso8859_16', 'encodings.iso8859_2', 'encodings.iso8859_3',
|
||||
'encodings.iso8859_4', 'encodings.iso8859_5', 'encodings.iso8859_6',
|
||||
'encodings.iso8859_7', 'encodings.iso8859_8', 'encodings.iso8859_9',
|
||||
'encodings.johab', 'encodings.koi8_r', 'encodings.koi8_u',
|
||||
'encodings.latin_1', 'encodings.mac_arabic', 'encodings.mac_centeuro',
|
||||
'encodings.mac_croatian', 'encodings.mac_cyrillic',
|
||||
'encodings.mac_farsi', 'encodings.mac_greek', 'encodings.mac_iceland',
|
||||
'encodings.mac_latin2', 'encodings.mac_roman',
|
||||
'encodings.mac_romanian', 'encodings.mac_turkish', 'encodings.mbcs',
|
||||
'encodings.palmos', 'encodings.ptcp154', 'encodings.punycode',
|
||||
'encodings.quopri_codec', 'encodings.raw_unicode_escape',
|
||||
'encodings.rot_13', 'encodings.shift_jis', 'encodings.shift_jis_2004',
|
||||
'encodings.shift_jisx0213', 'encodings.string_escape',
|
||||
'encodings.tis_620', 'encodings.undefined',
|
||||
'encodings.unicode_escape', 'encodings.unicode_internal',
|
||||
'encodings.utf_16', 'encodings.utf_16_be', 'encodings.utf_16_le',
|
||||
'encodings.utf_32', 'encodings.utf_32_be', 'encodings.utf_32_le',
|
||||
'encodings.utf_7', 'encodings.utf_8', 'encodings.utf_8_sig',
|
||||
'encodings.uu_codec', 'encodings.zlib_codec', 'errno', 'exceptions',
|
||||
'fcntl', 'filecmp', 'fileinput', 'fnmatch', 'formatter', 'fpformat',
|
||||
'fractions', 'ftplib', 'functools', 'future_builtins', 'gc', 'gdbm',
|
||||
'genericpath', 'getopt', 'getpass', 'gettext', 'glob', 'grp', 'gzip',
|
||||
'hashlib', 'heapq', 'hmac', 'hotshot', 'hotshot.log', 'hotshot.stats',
|
||||
'hotshot.stones', 'htmlentitydefs', 'htmllib', 'httplib', 'idlelib',
|
||||
'idlelib.AutoComplete', 'idlelib.AutoCompleteWindow',
|
||||
'idlelib.AutoExpand', 'idlelib.Bindings', 'idlelib.CallTipWindow',
|
||||
'idlelib.CallTips', 'idlelib.ClassBrowser', 'idlelib.CodeContext',
|
||||
'idlelib.ColorDelegator', 'idlelib.Debugger', 'idlelib.Delegator',
|
||||
'idlelib.EditorWindow', 'idlelib.FileList', 'idlelib.FormatParagraph',
|
||||
'idlelib.GrepDialog', 'idlelib.HyperParser', 'idlelib.IOBinding',
|
||||
'idlelib.IdleHistory', 'idlelib.MultiCall', 'idlelib.MultiStatusBar',
|
||||
'idlelib.ObjectBrowser', 'idlelib.OutputWindow', 'idlelib.ParenMatch',
|
||||
'idlelib.PathBrowser', 'idlelib.Percolator', 'idlelib.PyParse',
|
||||
'idlelib.PyShell', 'idlelib.RemoteDebugger',
|
||||
'idlelib.RemoteObjectBrowser', 'idlelib.ReplaceDialog',
|
||||
'idlelib.RstripExtension', 'idlelib.ScriptBinding',
|
||||
'idlelib.ScrolledList', 'idlelib.SearchDialog',
|
||||
'idlelib.SearchDialogBase', 'idlelib.SearchEngine',
|
||||
'idlelib.StackViewer', 'idlelib.ToolTip', 'idlelib.TreeWidget',
|
||||
'idlelib.UndoDelegator', 'idlelib.WidgetRedirector',
|
||||
'idlelib.WindowList', 'idlelib.ZoomHeight', 'idlelib.aboutDialog',
|
||||
'idlelib.configDialog', 'idlelib.configHandler',
|
||||
'idlelib.configHelpSourceEdit', 'idlelib.configSectionNameDialog',
|
||||
'idlelib.dynOptionMenuWidget', 'idlelib.idle', 'idlelib.idlever',
|
||||
'idlelib.keybindingDialog', 'idlelib.macosxSupport', 'idlelib.rpc',
|
||||
'idlelib.run', 'idlelib.tabbedpages', 'idlelib.textView', 'ihooks',
|
||||
'imageop', 'imaplib', 'imghdr', 'imp', 'importlib', 'imputil',
|
||||
'inspect', 'io', 'itertools', 'json', 'json.decoder', 'json.encoder',
|
||||
'json.scanner', 'json.tool', 'keyword', 'lib2to3', 'lib2to3.__main__',
|
||||
'lib2to3.btm_matcher', 'lib2to3.btm_utils', 'lib2to3.fixer_base',
|
||||
'lib2to3.fixer_util', 'lib2to3.fixes', 'lib2to3.fixes.fix_apply',
|
||||
'lib2to3.fixes.fix_basestring', 'lib2to3.fixes.fix_buffer',
|
||||
'lib2to3.fixes.fix_callable', 'lib2to3.fixes.fix_dict',
|
||||
'lib2to3.fixes.fix_except', 'lib2to3.fixes.fix_exec',
|
||||
'lib2to3.fixes.fix_execfile', 'lib2to3.fixes.fix_exitfunc',
|
||||
'lib2to3.fixes.fix_filter', 'lib2to3.fixes.fix_funcattrs',
|
||||
'lib2to3.fixes.fix_future', 'lib2to3.fixes.fix_getcwdu',
|
||||
'lib2to3.fixes.fix_has_key', 'lib2to3.fixes.fix_idioms',
|
||||
'lib2to3.fixes.fix_import', 'lib2to3.fixes.fix_imports',
|
||||
'lib2to3.fixes.fix_imports2', 'lib2to3.fixes.fix_input',
|
||||
'lib2to3.fixes.fix_intern', 'lib2to3.fixes.fix_isinstance',
|
||||
'lib2to3.fixes.fix_itertools', 'lib2to3.fixes.fix_itertools_imports',
|
||||
'lib2to3.fixes.fix_long', 'lib2to3.fixes.fix_map',
|
||||
'lib2to3.fixes.fix_metaclass', 'lib2to3.fixes.fix_methodattrs',
|
||||
'lib2to3.fixes.fix_ne', 'lib2to3.fixes.fix_next',
|
||||
'lib2to3.fixes.fix_nonzero', 'lib2to3.fixes.fix_numliterals',
|
||||
'lib2to3.fixes.fix_operator', 'lib2to3.fixes.fix_paren',
|
||||
'lib2to3.fixes.fix_print', 'lib2to3.fixes.fix_raise',
|
||||
'lib2to3.fixes.fix_raw_input', 'lib2to3.fixes.fix_reduce',
|
||||
'lib2to3.fixes.fix_renames', 'lib2to3.fixes.fix_repr',
|
||||
'lib2to3.fixes.fix_set_literal', 'lib2to3.fixes.fix_standarderror',
|
||||
'lib2to3.fixes.fix_sys_exc', 'lib2to3.fixes.fix_throw',
|
||||
'lib2to3.fixes.fix_tuple_params', 'lib2to3.fixes.fix_types',
|
||||
'lib2to3.fixes.fix_unicode', 'lib2to3.fixes.fix_urllib',
|
||||
'lib2to3.fixes.fix_ws_comma', 'lib2to3.fixes.fix_xrange',
|
||||
'lib2to3.fixes.fix_xreadlines', 'lib2to3.fixes.fix_zip',
|
||||
'lib2to3.main', 'lib2to3.patcomp', 'lib2to3.pgen2',
|
||||
'lib2to3.pgen2.conv', 'lib2to3.pgen2.driver', 'lib2to3.pgen2.grammar',
|
||||
'lib2to3.pgen2.literals', 'lib2to3.pgen2.parse', 'lib2to3.pgen2.pgen',
|
||||
'lib2to3.pgen2.token', 'lib2to3.pgen2.tokenize', 'lib2to3.pygram',
|
||||
'lib2to3.pytree', 'lib2to3.refactor', 'linecache', 'linuxaudiodev',
|
||||
'locale', 'logging', 'logging.config', 'logging.handlers', 'macpath',
|
||||
'macurl2path', 'mailbox', 'mailcap', 'markupbase', 'marshal', 'math',
|
||||
'md5', 'mhlib', 'mimetools', 'mimetypes', 'mimify', 'mmap',
|
||||
'modulefinder', 'multifile', 'multiprocessing',
|
||||
'multiprocessing.connection', 'multiprocessing.dummy',
|
||||
'multiprocessing.dummy.connection', 'multiprocessing.forking',
|
||||
'multiprocessing.heap', 'multiprocessing.managers',
|
||||
'multiprocessing.pool', 'multiprocessing.process',
|
||||
'multiprocessing.queues', 'multiprocessing.reduction',
|
||||
'multiprocessing.sharedctypes', 'multiprocessing.synchronize',
|
||||
'multiprocessing.util', 'mutex', 'netrc', 'new', 'nis', 'nntplib',
|
||||
'ntpath', 'nturl2path', 'numbers', 'opcode', 'operator', 'optparse',
|
||||
'os', 'os2emxpath', 'ossaudiodev', 'parser', 'pdb', 'pickle',
|
||||
'pickletools', 'pipes', 'pkgutil', 'plat-linux2.CDROM',
|
||||
'plat-linux2.DLFCN', 'plat-linux2.IN', 'plat-linux2.TYPES',
|
||||
'platform', 'plistlib', 'popen2', 'poplib', 'posix', 'posixfile',
|
||||
'posixpath', 'pprint', 'profile', 'pstats', 'pty', 'pwd',
|
||||
'py_compile', 'pyclbr', 'pydoc', 'pydoc_data', 'pydoc_data.topics',
|
||||
'pyexpat', 'quopri', 'random', 're', 'readline', 'repr', 'resource',
|
||||
'rexec', 'rfc822', 'rlcompleter', 'robotparser', 'runpy', 'sched',
|
||||
'select', 'sets', 'sgmllib', 'sha', 'shelve', 'shlex', 'shutil',
|
||||
'signal', 'site', 'smtpd', 'smtplib', 'sndhdr', 'socket', 'spwd',
|
||||
'sqlite3', 'sqlite3.dbapi2', 'sqlite3.dump', 'sre', 'sre_compile',
|
||||
'sre_constants', 'sre_parse', 'ssl', 'stat', 'statvfs', 'string',
|
||||
'stringold', 'stringprep', 'strop', 'struct', 'subprocess', 'sunau',
|
||||
'sunaudio', 'symbol', 'symtable', 'sys', 'sysconfig', 'syslog',
|
||||
'tabnanny', 'tarfile', 'telnetlib', 'tempfile', 'termios', 'test',
|
||||
'test.test_support', 'textwrap', 'this', 'thread', 'threading',
|
||||
'time', 'timeit', 'timing', 'toaiff', 'token', 'tokenize', 'trace',
|
||||
'traceback', 'tty', 'types', 'unicodedata', 'unittest',
|
||||
'unittest.__main__', 'unittest.case', 'unittest.loader',
|
||||
'unittest.main', 'unittest.result', 'unittest.runner',
|
||||
'unittest.signals', 'unittest.suite', 'unittest.test',
|
||||
'unittest.test.dummy', 'unittest.test.support',
|
||||
'unittest.test.test_assertions', 'unittest.test.test_break',
|
||||
'unittest.test.test_case', 'unittest.test.test_discovery',
|
||||
'unittest.test.test_functiontestcase', 'unittest.test.test_loader',
|
||||
'unittest.test.test_program', 'unittest.test.test_result',
|
||||
'unittest.test.test_runner', 'unittest.test.test_setups',
|
||||
'unittest.test.test_skipping', 'unittest.test.test_suite',
|
||||
'unittest.util', 'urllib', 'urllib2', 'urlparse', 'user', 'uu',
|
||||
'uuid', 'warnings', 'wave', 'weakref', 'webbrowser', 'whichdb',
|
||||
'wsgiref', 'wsgiref.handlers', 'wsgiref.headers',
|
||||
'wsgiref.simple_server', 'wsgiref.util', 'wsgiref.validate', 'xdrlib',
|
||||
'xml', 'xml.dom', 'xml.dom.NodeFilter', 'xml.dom.domreg',
|
||||
'xml.dom.expatbuilder', 'xml.dom.minicompat', 'xml.dom.minidom',
|
||||
'xml.dom.pulldom', 'xml.dom.xmlbuilder', 'xml.etree',
|
||||
'xml.etree.ElementInclude', 'xml.etree.ElementPath',
|
||||
'xml.etree.ElementTree', 'xml.etree.cElementTree', 'xml.parsers',
|
||||
'xml.parsers.expat', 'xml.sax', 'xml.sax._exceptions',
|
||||
'xml.sax.expatreader', 'xml.sax.handler', 'xml.sax.saxutils',
|
||||
'xml.sax.xmlreader', 'xmllib', 'xmlrpclib', 'xxsubtype', 'zipfile', ]
|
||||
# Dynamic modules that can't be auto-discovered by the script above
|
||||
manual_stdlib = ['os.path', ]
|
||||
py2_stdlib.extend(manual_stdlib)
|
||||
|
||||
|
||||
def _get_import_type(module):
|
||||
mod_base, _, _ = module.partition('.')
|
||||
if mod_base in module_cache:
|
||||
return module_cache[mod_base]
|
||||
if module in module_cache:
|
||||
return module_cache[module]
|
||||
|
||||
def cache_type(module_type):
|
||||
module_cache[mod_base] = module_type
|
||||
module_cache[module] = module_type
|
||||
return module_type
|
||||
|
||||
# First check if the module is local
|
||||
# Check static stdlib list
|
||||
if module in py2_stdlib:
|
||||
return cache_type('stdlib')
|
||||
|
||||
# Check if the module is local
|
||||
try:
|
||||
imp.find_module(mod_base, ['.'])
|
||||
_find_module(module, ['.'])
|
||||
# If the previous line succeeded then it must be a project module
|
||||
return cache_type('project')
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
try:
|
||||
_, path, _ = imp.find_module(mod_base)
|
||||
except ImportError:
|
||||
return cache_type('third-party')
|
||||
|
||||
if path is None:
|
||||
# NOTE(imelnikov): python 3 returns None for path of builtin
|
||||
# modules, like sys or builtin; they are definitely stdlib
|
||||
return cache_type('stdlib')
|
||||
if 'site-packages' in path or 'dist-packages' in path:
|
||||
return cache_type('third-party')
|
||||
std_paths = [stdlib_path_prefix, sys.prefix]
|
||||
|
||||
# NOTE(imelnikov): if we are in virtualenv, we should consider
|
||||
# real prefix too, as python 3 copies some stdlib modules to
|
||||
# virtualenv, but not all of them.
|
||||
real_prefix = getattr(sys, 'real_prefix', None)
|
||||
if real_prefix is not None:
|
||||
std_paths.append(real_prefix)
|
||||
|
||||
if path == module or any(path.startswith(p) for p in std_paths):
|
||||
return cache_type('stdlib')
|
||||
# Otherwise treat it as third-party - this means we may treat some stdlib
|
||||
# modules as third-party, but that's okay because we are allowing
|
||||
# third-party libs in the stdlib section.
|
||||
return cache_type('third-party')
|
||||
|
||||
|
||||
|
||||
57
hacking/tests/checks/test_imports.py
Normal file
57
hacking/tests/checks/test_imports.py
Normal file
@@ -0,0 +1,57 @@
|
||||
# Copyright (c) 2014 Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import mock
|
||||
import six
|
||||
|
||||
from hacking.checks import imports
|
||||
from hacking import tests
|
||||
|
||||
|
||||
def fake_find_module(module, path):
|
||||
# Behave as though we're running in the oslo.db project
|
||||
if path is None and module == 'oslo':
|
||||
# The oslo namespace module won't be found
|
||||
raise ImportError
|
||||
elif module == 'oslo' and path[0] == '.':
|
||||
return (None, './oslo', None)
|
||||
elif module == 'db' and path[0] == './oslo':
|
||||
return (None, './oslo/db', None)
|
||||
elif './oslo/db' in path[0] and module != '_':
|
||||
return (None, '%s/%s' % (path[0], module), None)
|
||||
raise ImportError
|
||||
|
||||
|
||||
if six.PY3:
|
||||
import_name = 'builtins.__import__'
|
||||
else:
|
||||
import_name = '__builtin__.__import__'
|
||||
|
||||
|
||||
class ImportTestCase(tests.TestCase):
|
||||
@mock.patch(import_name)
|
||||
@mock.patch('imp.find_module')
|
||||
def test_namespace_module(self, find_module, mock_import):
|
||||
find_module.side_effect = fake_find_module
|
||||
self.assertEqual('third-party', imports._get_import_type('oslo.i18n'))
|
||||
self.assertEqual('project', imports._get_import_type('oslo.db'))
|
||||
|
||||
@mock.patch(import_name)
|
||||
@mock.patch('imp.find_module')
|
||||
def test_non_module(self, find_module, mock_import):
|
||||
find_module.side_effect = fake_find_module
|
||||
mock_import.side_effect = ImportError
|
||||
mod_name = 'oslo.db.openstack.common.gettextutils._'
|
||||
self.assertEqual('project', imports._get_import_type(mod_name))
|
||||
@@ -1,6 +1,7 @@
|
||||
coverage>=3.6
|
||||
discover
|
||||
fixtures>=0.3.14
|
||||
mock
|
||||
python-subunit>=0.0.18
|
||||
sphinx>=1.1.2,!=1.2.0,<1.3
|
||||
oslosphinx
|
||||
|
||||
Reference in New Issue
Block a user