Commits

Anthony Tuininga committed caf2df8 Merge

Merged in takluyver/cx_freeze/issue22 (pull request #22)

Fix for issue 22

Comments (0)

Files changed (4)

cx_Freeze/finder.py

 import opcode
 import os
 import pkgutil
+import re
 import sys
 import types
 import zipfile
 
 __all__ = [ "Module", "ModuleFinder" ]
 
+try:
+    isidentifier = str.isidentifier  # Built in method in Python 3
+except AttributeError:
+    # Check with regex for Python 2
+    _identifier_re = re.compile(r'^[a-z_]\w*$', re.I)
+    def isidentifier(s):
+        return bool(_identifier_re.match(s))
+
 class ModuleFinder(object):
 
     def __init__(self, includeFiles = None, excludes = [], path = None,
                     for suffix in suffixes:
                         if fileName.endswith(suffix):
                             name = fileName[:-len(suffix)]
-                            break
+                            # Only modules with valid Python names are importable
+                            if isidentifier(name):
+                                break
                     else:
                         continue
                     if name == "__init__":
                         continue
+                    
                 subModuleName = "%s.%s" % (module.name, name)
-                subModule, returnError = \
-                        self._InternalImportModule(subModuleName,
+                subModule = self._InternalImportModule(subModuleName,
                                 deferredImports)
-                if returnError and subModule is None:
+                if subModule is None:
                     raise ImportError("No module named %r" % subModuleName)
                 module.globalNames[name] = None
                 if subModule.path and recursive:
         # absolute import (available in Python 2.5 and up)
         # the name given is the only name that will be searched
         if relativeImportIndex == 0:
-            module, returnError = self._InternalImportModule(name,
+            module = self._InternalImportModule(name,
                     deferredImports, namespace = namespace)
 
         # old style relative import (only possibility in Python 2.4 and prior)
             parent = self._DetermineParent(caller)
             while parent is not None:
                 fullName = "%s.%s" % (parent.name, name)
-                module, returnError = self._InternalImportModule(fullName,
+                module = self._InternalImportModule(fullName,
                         deferredImports, namespace = namespace)
                 if module is not None:
                     parent.globalNames[name] = None
                     return module
                 parent = self._GetParentByName(parent.name)
-            module, returnError = self._InternalImportModule(name,
+            module = self._InternalImportModule(name,
                     deferredImports, namespace = namespace)
 
         # new style relative import (available in Python 2.5 and up)
                 relativeImportIndex -= 1
             if parent is None:
                 module = None
-                returnError = True
             elif not name:
                 module = parent
             else:
                 name = "%s.%s" % (parent.name, name)
-                module, returnError = self._InternalImportModule(name,
+                module = self._InternalImportModule(name,
                         deferredImports, namespace = namespace)
 
         # if module not found, track that fact
             if caller is None:
                 raise ImportError("No module named %r" % name)
             self._RunHook("missing", name, caller)
-            if returnError and name not in caller.ignoreNames:
+            if name not in caller.ignoreNames:
                 callers = self._badModules.setdefault(name, {})
                 callers[caller.name] = None
 
            name given is an absolute name. None is returned if the module
            cannot be found."""
         try:
-            return self._modules[name], False
+            # Check in module cache before trying to import it again.
+            return self._modules[name]
         except KeyError:
             pass
         
             module = self._AddModule(name)
             self._RunHook("load", module.name, module)
             module.inImport = False
-            return module, False
+            return module
         
         pos = name.rfind(".")
         if pos < 0:  # Top-level module
             parentModule = None
         else:        # Dotted module name - look up the parent module
             parentName = name[:pos]
-            parentModule, returnError = \
+            parentModule = \
                     self._InternalImportModule(parentName, deferredImports,
                             namespace = namespace)
             if parentModule is None:
-                return None, returnError
+                return None
             if namespace:
                 parentModule.ExtendPath()
             path = parentModule.path
         
         if name in self.aliases:
             actualName = self.aliases[name]
-            module, returnError = \
-                    self._InternalImportModule(actualName, deferredImports)
+            module = self._InternalImportModule(actualName, deferredImports)
             self._modules[name] = module
-            return module, returnError
+            return module
         
         try:
             fp, path, info = self._FindModule(searchName, path, namespace)
                     parentModule, namespace)
         except ImportError:
             self._modules[name] = None
-            return None, True
-        return module, False
+            return None
+        return module
 
     def _LoadModule(self, name, fp, path, info, deferredImports,
             parent = None, namespace = False):

test/samples/testpkg1/__init__.py

Empty file added.

test/samples/testpkg1/not.importable.py

+# The . in the filename means this file cannot be imported as a module.

test/test_finder.py

 
 from cx_Freeze.finder import ModuleFinder
 
+any3 = (mock.ANY,)*3
+
 def test_ScanCode():
     mf = ModuleFinder()
     with mock.patch.object(mf, '_ImportModule') as _ImportModule_mock:
         _ImportModule_mock.return_value = None
         mf.IncludeFile(os.path.join(test_dir, 'imports_sample.py'))
-        any3 = (mock.ANY,)*3
         _ImportModule_mock.assert_has_calls([mock.call('moda', *any3),
                                              mock.call('modb', *any3),
                                              mock.call('', *any3),
                                              mock.call('modg.submod', *any3),
                                              mock.call('modh', *any3),
                                             ])
-    
+
+def test_not_import_invalid_module_name():
+    """testpkg1 contains not.importable.py, which shouldn't be included."""
+    mf = ModuleFinder()
+    mf.path.insert(0, os.path.join(test_dir, 'samples'))
+    mf.IncludePackage('testpkg1')  # Threw ImportError before the bug was fixed
+