[#2465] Workaround pour le ticket Django #14087
authorEric Mc Sween <eric.mcsween@auf.org>
Thu, 5 Jan 2012 21:58:56 +0000 (16:58 -0500)
committerEric Mc Sween <eric.mcsween@auf.org>
Thu, 5 Jan 2012 21:58:56 +0000 (16:58 -0500)
Django avait de la difficulté à trouver les management commands dans des
"namespaced packages". J'ai appliqué la dernière patch suggérée dans le ticket
(https://code.djangoproject.com/ticket/14087) par monkey patching.

CHANGES
auf/recipe/django/manage.py [new file with mode: 0644]
auf/recipe/django/recipe.py
setup.py

diff --git a/CHANGES b/CHANGES
index 6ef9933..d13badb 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,10 @@
+0.20
+====
+
+* Patch pour permettre au script bin/django de trouver les management commands
+  dans des apps contenues dans des "namespaced packages". Voir le ticket Django
+  #14087 (https://code.djangoproject.com/ticket/14087)
+
 0.19
 ====
 
diff --git a/auf/recipe/django/manage.py b/auf/recipe/django/manage.py
new file mode 100644 (file)
index 0000000..74af2d6
--- /dev/null
@@ -0,0 +1,95 @@
+import imp
+import sys
+
+from django.core import management
+
+
+def main(settings_file):
+    try:
+        mod = __import__(settings_file)
+        components = settings_file.split('.')
+        for comp in components[1:]:
+            mod = getattr(mod, comp)
+
+    except ImportError, e:
+        import sys
+        sys.stderr.write("Error loading the settings module '%s': %s"
+                            % (settings_file, e))
+        return sys.exit(1)
+
+    management.execute_manager(mod)
+
+# Fix pour le ticket Django #14087 (https://code.djangoproject.com/ticket/14087)
+# On applique le patch https://code.djangoproject.com/attachment/ticket/14087/namespace_package_pth.diff
+# par monkey patching.
+
+def find_modules(name, path=None):
+    """Find all modules with name 'name'
+
+    Unlike find_module in the imp package this returns a list of all
+    matched modules.
+    """
+    results = []
+    if path is None: path = sys.path
+    for p in path:
+        importer = sys.path_importer_cache.get(p, None)
+        if importer is None:
+            find_module = imp.find_module
+        else:
+            find_module = importer.find_module
+
+        try:
+            result = find_module(name, [p])
+            if result is not None:
+                results.append(result)
+        except ImportError:
+            pass
+    if not results:
+        raise ImportError("No module named %.200s" % name)
+    return results
+
+def find_management_module(app_name):
+    """
+    Determines the path to the management module for the given app_name,
+    without actually importing the application or the management module.
+
+    Raises ImportError if the management module cannot be found for any reason.
+    """
+    parts = app_name.split('.')
+
+    for i in range(len(parts), 0, -1):
+        try:
+            paths = sys.modules['.'.join(parts[:i])].__path__
+        except KeyError:
+            continue
+
+        parts = parts[i:] + ['management']
+        parts.reverse()
+        break
+    else:
+        parts.append('management')
+        parts.reverse()
+        part = parts.pop()
+        paths = None
+
+        # When using manage.py, the project module is added to the path,
+        # loaded, then removed from the path. This means that
+        # testproject.testapp.models can be loaded in future, even if
+        # testproject isn't in the path. When looking for the management
+        # module, we need look for the case where the project name is part
+        # of the app_name but the project directory itself isn't on the path.
+        try:
+            modules = find_modules(part, paths)
+            paths = [m[1] for m in modules]
+        except ImportError,e:
+            if os.path.basename(os.getcwd()) != part:
+                raise e
+
+    while parts:
+        part = parts.pop()
+        modules = find_modules(part, paths)
+        paths = [m[1] for m in modules]
+    return paths[0]
+
+# Patch!
+management.find_management_module = find_management_module
index 5e90a90..75aae68 100644 (file)
@@ -4,6 +4,7 @@ import os
 import shutil
 import pkg_resources
 import djangorecipe
+import zc.buildout
 from djangorecipe.boilerplate import versions
 from djangorecipe.recipe import Recipe as OriginalDjangoRecipe
 from boilerplate import *
@@ -82,5 +83,12 @@ class Recipe(OriginalDjangoRecipe):
         dst = os.path.join(project_dir, 'media', 'django')
         shutil.copytree(src, dst)
 
-
-
+    def create_manage_script(self, extra_paths, ws):
+        project = self.options.get('projectegg', self.options['project'])
+        return zc.buildout.easy_install.scripts(
+            [(self.options.get('control-script', self.name),
+              'auf.recipe.django.manage', 'main')],
+            ws, self.options['executable'], self.options['bin-directory'],
+            extra_paths=extra_paths,
+            arguments="'%s.%s'" % (project,
+                                   self.options['settings']))
index 79e1958..134c697 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
 import sys, os
 
 name = 'auf.recipe.django'
-version = '0.19'
+version = '0.20'
 
 setup(name=name,
       version=version,
@@ -20,9 +20,10 @@ setup(name=name,
       include_package_data=True,
       zip_safe=False,
       install_requires=[
-        'zc.buildout',
-        'zc.recipe.egg',
-        'djangorecipe',
+          'setuptools',
+          'zc.buildout',
+          'zc.recipe.egg',
+          'djangorecipe',
       ],
       entry_points="""
       # -*- Entry points: -*-