From: Thomas V. S. <tho...@us...> - 2011-01-21 11:00:27
|
Update of /cvsroot/pychecker/pychecker/pychecker In directory sfp-cvsdas-2.v30.ch3.sourceforge.com:/tmp/cvs-serv20149/pychecker Modified Files: CodeChecks.py check.py checker.py Log Message: * pychecker/CodeChecks.py: Handle sibling imports, where a module could be importing another living in the same directory. When handling IMPORT_NAME, pass the current's module moduleDir as the sibling module dir so we can load the ones that are as such, and tag them properly in the PCModules. * pychecker/check.py: Track both PC Modules and normal python modules added/loaded after importing all files to be checked. Delete the modules that are likely to be a sibling module from sys.modules, because they pollute that namespace by pretending they are an importable module from sys.path when they were a side effect of loading the files we were interested in. * pychecker/checker.py: Find a good moduleDir for the candidate files. * test/test_internal.py: Allow PYCHECKER_DEBUG as an env var so runs of trial can be done with debugging. Fix variables assert. Test now passes. Index: checker.py =================================================================== RCS file: /cvsroot/pychecker/pychecker/pychecker/checker.py,v retrieving revision 1.135 retrieving revision 1.136 diff -u -d -r1.135 -r1.136 --- checker.py 13 Jan 2011 12:25:08 -0000 1.135 +++ checker.py 21 Jan 2011 11:00:18 -0000 1.136 @@ -189,7 +189,18 @@ if check : try : # FIXME: can we find a good moduleDir ? - module = pcmodules.PyCheckerModule(pymodule.__name__) + # based on possible module.__file__, check if it's from + # sys.path, and if not, extract moduleDir + moduleDir = os.path.dirname(pymodule.__file__) + for path in sys.path: + if os.path.abspath(moduleDir) == os.path.abspath(path): + moduleDir = None + break + + # FIXME: could it possibly be from a higher-level package, + # instead of the current dir ? Loop up with __init__.py ? + module = pcmodules.PyCheckerModule(pymodule.__name__, + moduleDir=moduleDir) if module.initModule(pymodule): warnings = warn.find([module], _cfg, _suppressions) _printWarnings(_get_unique_warnings(warnings)) Index: CodeChecks.py =================================================================== RCS file: /cvsroot/pychecker/pychecker/pychecker/CodeChecks.py,v retrieving revision 1.225 retrieving revision 1.226 diff -u -d -r1.225 -r1.226 --- CodeChecks.py 21 Jan 2011 10:33:32 -0000 1.225 +++ CodeChecks.py 21 Jan 2011 11:00:18 -0000 1.226 @@ -735,10 +735,13 @@ else: fullName = "%s.%s" % (moduleName, operand) - try: - pcmodule = _getOrLoadPCModule(code, fullName, moduleDir) - code.pushStack(Stack.Item(pcmodule, types.ModuleType)) - except ImportError: + # see if the module we are importing from has the operand as a module + siblingModuleDir = module.moduleDir + pcmodule = _getOrLoadPCModule(code, moduleName, moduleDir, + siblingModuleDir=siblingModuleDir) + if operand in pcmodule.modules: + code.pushStack(Stack.Item(pcmodule.modules[operand], types.ModuleType)) + else: # FIXME: so what is it ? what do we push ? # For now, emulate the old code code.pushStack(Stack.Item(operand, types.ModuleType)) @@ -1693,7 +1696,7 @@ _checkNoEffect(code) -def _getOrLoadPCModule(code, name, moduleDir=None): +def _getOrLoadPCModule(code, name, moduleDir=None, siblingModuleDir=None): """ Retrieve a previously loaded PyChecker module by name, or load it. @@ -1705,23 +1708,40 @@ @rtype: L{pcmodules.PyCheckerModule} """ assert '*' not in name, "Name %r contains an asterisk" % name + + # make system imports take precedence over siblings + # FIXME: not sure what Python actually does here + # but reversing breaks the test_zope_interface test + pcmodule = pcmodules.getPCModule(name, moduleDir=moduleDir) + + if not pcmodule: + pcmodule = pcmodules.getPCModule(name, moduleDir=siblingModuleDir) + if not pcmodule: pcmodule = pcmodules.PyCheckerModule(name, moduleDir=moduleDir) try: pcmodule.load(allowImportError=True) except ImportError, e: - if not _handleDeprecated(code, name): - raise e + pcmodule = pcmodules.PyCheckerModule(name, + moduleDir=siblingModuleDir) + try: + pcmodule.load(allowImportError=True) + except ImportError, e: + if not _handleDeprecated(code, name): + raise e else: pass return pcmodule - def _IMPORT_NAME(oparg, operand, codeSource, code): try: - pcmodule = _getOrLoadPCModule(code, operand) + # FIXME: if it is a sibling import, we should reuse moduleDir + # FIXME: but should only be a candidate, and here we pass it always + siblingModuleDir = codeSource.module.moduleDir + pcmodule = _getOrLoadPCModule(code, operand, + siblingModuleDir=siblingModuleDir) code.pushStack(Stack.Item(pcmodule, types.ModuleType)) except ImportError: # TODO: a submodule could import a same-level module Index: check.py =================================================================== RCS file: /cvsroot/pychecker/pychecker/pychecker/check.py,v retrieving revision 1.2 retrieving revision 1.3 diff -u -d -r1.2 -r1.3 --- check.py 13 Jan 2011 22:38:19 -0000 1.2 +++ check.py 21 Jan 2011 11:00:18 -0000 1.3 @@ -96,7 +96,7 @@ return modules -def getAllModules(): +def getAllPCModules(): """ Returns a list of all modules that should be checked. @@ -242,20 +242,42 @@ def getWarnings(files, cfg = None, suppressions = None): warnings = processFiles(files, cfg) fixupBuiltinModules() - return warnings + warn.find(getAllModules(), _cfg, suppressions) + return warnings + warn.find(getAllPCModules(), _cfg, suppressions) def _print_processing(name) : if not _cfg.quiet : sys.stderr.write("Processing %s...\n" % name) +def _mightBeSiblingModule(module): + # check if the given module might be a sibling module + # return True if it's likely it is + + # if we can't check the file we cannot now + if not hasattr(module, '__file__'): + return False + + # if it's an absolute path then it was probably a system import + if module.__file__.startswith(os.path.sep): + return False + + # if the package name matches the dir, it was an import from ''/cwd + package = module.__name__.split('.')[0] + directory = module.__file__.split(os.path.sep)[0] + + if package == directory: + return False + + return True + # grooming this to be public API to use pychecker as a module def _check(files, cfg=None, suppressions=None, printProcessing=False): # snapshot modules before and after processing, so that we only warn # about the modules loaded because of these files. # preferable to clearing the loaded modules because we don't have to # reprocess previously handled modules - beforeModules = getAllModules() + beforePCModules = getAllPCModules() + beforeModules = dict(sys.modules.items()) utils.initConfig(cfg) utils.debug('main: Checking %d files', len(files)) @@ -263,26 +285,41 @@ importWarnings = processFiles(files, cfg, printProcessing and _print_processing or None) utils.debug('main: Found %d import warnings' % len(importWarnings)) + utils.debug('main: %d modules in sys.modules' % len(sys.modules.keys())) fixupBuiltinModules() - afterModules = getAllModules() + afterPCModules = getAllPCModules() - newModules = afterModules[:] - for m in beforeModules: - if m in newModules: - newModules.remove(m) + newPCModules = afterPCModules[:] + for m in beforePCModules: + if m in newPCModules: + newPCModules.remove(m) + + newModules = dict(sys.modules.items()) + for k, v in beforeModules.items(): + if k in newModules: + del newModules[k] if cfg.printParse : - for module in newModules: + for module in newPCModules: printer.module(module) + utils.debug('main: %d Pychecker modules and %d python modules loaded', + len(newPCModules), len(newModules)) + + # remove all sys.modules suspected of being sibling imports; they now + # pollute the global namespace of sys.modules + for k, v in newModules.items(): + if v and _mightBeSiblingModule(v): + utils.debug('main: unloading python module %s', v) + del sys.modules[k] utils.debug('main: Finding warnings') # suppressions is a tuple of suppressions, suppressionRegexs dicts - warnings = warn.find(newModules, cfg, suppressions) + warnings = warn.find(newPCModules, cfg, suppressions) utils.debug('main: Found %d warnings in %d files and %d modules', - len(importWarnings) + len(warnings), len(files), len(newModules)) + len(importWarnings) + len(warnings), len(files), len(newPCModules)) # FIXME: any way to assert we are popping the one we pushed ? utils.popConfig() |