eggs/djangorecipe-0.20-py2.6.egg/djangorecipe/recipe.py
changeset 307 c6bca38c1cbf
equal deleted inserted replaced
306:5ff1fc726848 307:c6bca38c1cbf
       
     1 from random import choice
       
     2 import os
       
     3 import subprocess
       
     4 import urllib2
       
     5 import shutil
       
     6 import logging
       
     7 import re
       
     8 
       
     9 from zc.buildout import UserError
       
    10 import zc.recipe.egg
       
    11 import setuptools
       
    12 
       
    13 script_template = {
       
    14     'wsgi': '''
       
    15 
       
    16 %(relative_paths_setup)s
       
    17 import sys
       
    18 sys.path[0:0] = [
       
    19   %(path)s,
       
    20   ]
       
    21 %(initialization)s
       
    22 import %(module_name)s
       
    23 
       
    24 application = %(module_name)s.%(attrs)s(%(arguments)s)
       
    25 ''',
       
    26     'fcgi': '''
       
    27 
       
    28 %(relative_paths_setup)s
       
    29 import sys
       
    30 sys.path[0:0] = [
       
    31   %(path)s,
       
    32   ]
       
    33 %(initialization)s
       
    34 import %(module_name)s
       
    35 
       
    36 %(module_name)s.%(attrs)s(%(arguments)s)
       
    37 '''
       
    38 }
       
    39 
       
    40 
       
    41 settings_template = '''
       
    42 import os
       
    43 
       
    44 ADMINS = (
       
    45     # ('Your Name', 'your_email@domain.com'),
       
    46 )
       
    47 
       
    48 MANAGERS = ADMINS
       
    49 
       
    50 DATABASE_ENGINE = 'sqlite3'    # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
       
    51 DATABASE_NAME = '%(project)s.db'
       
    52 DATABASE_USER = ''             # Not used with sqlite3.
       
    53 DATABASE_PASSWORD = ''         # Not used with sqlite3.
       
    54 DATABASE_HOST = ''             # Set to empty string for localhost. Not used with sqlite3.
       
    55 DATABASE_PORT = ''             # Set to empty string for default. Not used with sqlite3.
       
    56 
       
    57 TIME_ZONE = 'America/Chicago'
       
    58 
       
    59 LANGUAGE_CODE = 'en-us'
       
    60 
       
    61 # Absolute path to the directory that holds media.
       
    62 # Example: "/home/media/media.lawrence.com/"
       
    63 MEDIA_ROOT = %(media_root)s
       
    64 
       
    65 # URL that handles the media served from MEDIA_ROOT. Make sure to use a
       
    66 # trailing slash if there is a path component (optional in other cases).
       
    67 # Examples: "http://media.lawrence.com", "http://example.com/media/"
       
    68 MEDIA_URL = '/media/'
       
    69 
       
    70 # URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
       
    71 # trailing slash.
       
    72 # Examples: "http://foo.com/media/", "/media/".
       
    73 ADMIN_MEDIA_PREFIX = '/admin_media/'
       
    74 
       
    75 # Don't share this with anybody.
       
    76 SECRET_KEY = '%(secret)s'
       
    77 
       
    78 MIDDLEWARE_CLASSES = (
       
    79     'django.middleware.common.CommonMiddleware',
       
    80     'django.contrib.sessions.middleware.SessionMiddleware',
       
    81     'django.contrib.auth.middleware.AuthenticationMiddleware',
       
    82     'django.middleware.doc.XViewMiddleware',
       
    83 )
       
    84 
       
    85 ROOT_URLCONF = '%(urlconf)s'
       
    86 
       
    87 
       
    88 INSTALLED_APPS = (
       
    89     'django.contrib.auth',
       
    90     'django.contrib.contenttypes',
       
    91     'django.contrib.sessions',
       
    92     'django.contrib.admin',
       
    93 )
       
    94 
       
    95 TEMPLATE_LOADERS = (
       
    96     'django.template.loaders.filesystem.load_template_source',
       
    97     'django.template.loaders.app_directories.load_template_source',
       
    98 )
       
    99 
       
   100 TEMPLATE_DIRS = (
       
   101     os.path.join(os.path.dirname(__file__), "templates"),
       
   102 )
       
   103 
       
   104 
       
   105 '''
       
   106 
       
   107 production_settings = '''
       
   108 from %(project)s.settings import *
       
   109 '''
       
   110 
       
   111 development_settings = '''
       
   112 from %(project)s.settings import *
       
   113 DEBUG=True
       
   114 TEMPLATE_DEBUG=DEBUG
       
   115 '''
       
   116 
       
   117 urls_template = '''
       
   118 from django.conf.urls.defaults import patterns, include, handler500
       
   119 from django.conf import settings
       
   120 from django.contrib import admin
       
   121 admin.autodiscover()
       
   122 
       
   123 handler500 # Pyflakes
       
   124 
       
   125 urlpatterns = patterns(
       
   126     '',
       
   127     (r'^admin/(.*)', admin.site.root),
       
   128     (r'^accounts/login/$', 'django.contrib.auth.views.login'),
       
   129 )
       
   130 
       
   131 if settings.DEBUG:
       
   132     urlpatterns += patterns('',
       
   133         (r'^media/(?P<path>.*)$', 'django.views.static.serve',
       
   134          {'document_root': settings.MEDIA_ROOT}),
       
   135     )
       
   136 '''
       
   137 
       
   138 class Recipe(object):
       
   139     def __init__(self, buildout, name, options):
       
   140         self.log = logging.getLogger(name)
       
   141         self.egg = zc.recipe.egg.Egg(buildout, options['recipe'], options)
       
   142 
       
   143         self.buildout, self.name, self.options = buildout, name, options
       
   144         options['location'] = os.path.join(
       
   145             buildout['buildout']['parts-directory'], name)
       
   146         options['bin-directory'] = buildout['buildout']['bin-directory']
       
   147 
       
   148         options.setdefault('project', 'project')
       
   149         options.setdefault('settings', 'development')
       
   150 
       
   151         options.setdefault('urlconf', options['project'] + '.urls')
       
   152         options.setdefault(
       
   153             'media_root',
       
   154             "os.path.join(os.path.dirname(__file__), 'media')")
       
   155         # Set this so the rest of the recipe can expect the values to be
       
   156         # there. We need to make sure that both pythonpath and extra-paths are
       
   157         # set for BBB.
       
   158         if 'extra-paths' in options:
       
   159             options['pythonpath'] = options['extra-paths']
       
   160         else:
       
   161             options.setdefault('extra-paths', options.get('pythonpath', ''))
       
   162 
       
   163         # Usefull when using archived versions
       
   164         buildout['buildout'].setdefault(
       
   165             'download-cache',
       
   166             os.path.join(buildout['buildout']['directory'],
       
   167                          'downloads'))
       
   168 
       
   169         # mod_wsgi support script
       
   170         options.setdefault('wsgi', 'false')
       
   171         options.setdefault('fcgi', 'false')
       
   172         options.setdefault('wsgilog', '')
       
   173         options.setdefault('logfile', '')
       
   174 
       
   175         # only try to download stuff if we aren't asked to install from cache
       
   176         self.install_from_cache = self.buildout['buildout'].get(
       
   177             'install-from-cache', '').strip() == 'true'
       
   178 
       
   179 
       
   180     def install(self):
       
   181         location = self.options['location']
       
   182         base_dir = self.buildout['buildout']['directory']
       
   183 
       
   184         project_dir = os.path.join(base_dir, self.options['project'])
       
   185 
       
   186         download_dir = self.buildout['buildout']['download-cache']
       
   187         if not os.path.exists(download_dir):
       
   188             os.mkdir(download_dir)
       
   189 
       
   190         version = self.options['version']
       
   191         # Remove a pre-existing installation if it is there
       
   192         if os.path.exists(location):
       
   193             shutil.rmtree(location)
       
   194 
       
   195         if self.is_svn_url(version):
       
   196             self.install_svn_version(version, download_dir, location,
       
   197                                      self.install_from_cache)
       
   198         else:
       
   199             tarball = self.get_release(version, download_dir)
       
   200             # Extract and put the dir in its proper place
       
   201             self.install_release(version, download_dir, tarball, location)
       
   202 
       
   203         self.options['setup'] = location
       
   204         development = zc.recipe.egg.Develop(self.buildout,
       
   205                                             self.options['recipe'],
       
   206                                             self.options)
       
   207         development.install()
       
   208         del self.options['setup']
       
   209 
       
   210         extra_paths = self.get_extra_paths()
       
   211         requirements, ws = self.egg.working_set(['djangorecipe'])
       
   212 
       
   213         script_paths = []
       
   214 
       
   215         # Create the Django management script
       
   216         script_paths.extend(self.create_manage_script(extra_paths, ws))
       
   217 
       
   218         # Create the test runner
       
   219         script_paths.extend(self.create_test_runner(extra_paths, ws))
       
   220 
       
   221         # Make the wsgi and fastcgi scripts if enabled
       
   222         script_paths.extend(self.make_scripts(extra_paths, ws))
       
   223 
       
   224         # Create default settings if we haven't got a project
       
   225         # egg specified, and if it doesn't already exist
       
   226         if not self.options.get('projectegg'):
       
   227             if not os.path.exists(project_dir):
       
   228                 self.create_project(project_dir)
       
   229             else:
       
   230                 self.log.info(
       
   231                     'Skipping creating of project: %(project)s since '
       
   232                     'it exists' % self.options)
       
   233 
       
   234         return script_paths + [location]
       
   235 
       
   236     def install_svn_version(self, version, download_dir, location,
       
   237                             install_from_cache):
       
   238         svn_url = self.version_to_svn(version)
       
   239         download_location = os.path.join(
       
   240             download_dir, 'django-' +
       
   241             self.version_to_download_suffix(version))
       
   242         if not install_from_cache:
       
   243             if os.path.exists(download_location):
       
   244                 if self.svn_update(download_location, version):
       
   245                     raise UserError(
       
   246                         "Failed to update Django; %s. "
       
   247                         "Please check your internet connection." % (
       
   248                             download_location))
       
   249             else:
       
   250                 self.log.info("Checking out Django from svn: %s" % svn_url)
       
   251                 cmd = 'svn co %s %s' % (svn_url, download_location)
       
   252                 if not self.buildout['buildout'].get('verbosity'):
       
   253                     cmd += ' -q'
       
   254                 if self.command(cmd):
       
   255                     raise UserError("Failed to checkout Django. "
       
   256                                     "Please check your internet connection.")
       
   257         else:
       
   258             self.log.info("Installing Django from cache: " + download_location)
       
   259 
       
   260         shutil.copytree(download_location, location)
       
   261 
       
   262 
       
   263     def install_release(self, version, download_dir, tarball, destination):
       
   264         extraction_dir = os.path.join(download_dir, 'django-archive')
       
   265         setuptools.archive_util.unpack_archive(tarball, extraction_dir)
       
   266         # Lookup the resulting extraction dir instead of guessing it
       
   267         # (Django releases have a tendency not to be consistend here)
       
   268         untarred_dir = os.path.join(extraction_dir,
       
   269                                     os.listdir(extraction_dir)[0])
       
   270         shutil.move(untarred_dir, destination)
       
   271         shutil.rmtree(extraction_dir)
       
   272 
       
   273     def get_release(self, version, download_dir):
       
   274         tarball = os.path.join(download_dir, 'django-%s.tar.gz' % version)
       
   275 
       
   276         # Only download when we don't yet have an archive
       
   277         if not os.path.exists(tarball):
       
   278             download_url = 'http://www.djangoproject.com/download/%s/tarball/'
       
   279             self.log.info("Downloading Django from: %s" % (
       
   280                     download_url % version))
       
   281 
       
   282             tarball_f = open(tarball, 'wb')
       
   283             f = urllib2.urlopen(download_url % version)
       
   284             tarball_f.write(f.read())
       
   285             tarball_f.close()
       
   286             f.close()
       
   287         return tarball
       
   288 
       
   289     def create_manage_script(self, extra_paths, ws):
       
   290         project = self.options.get('projectegg', self.options['project'])
       
   291         return zc.buildout.easy_install.scripts(
       
   292             [(self.options.get('control-script', self.name),
       
   293               'djangorecipe.manage', 'main')],
       
   294             ws, self.options['executable'], self.options['bin-directory'],
       
   295             extra_paths = extra_paths,
       
   296             arguments= "'%s.%s'" % (project,
       
   297                                     self.options['settings']))
       
   298 
       
   299 
       
   300 
       
   301     def create_test_runner(self, extra_paths, working_set):
       
   302         apps = self.options.get('test', '').split()
       
   303         # Only create the testrunner if the user requests it
       
   304         if apps:
       
   305             return zc.buildout.easy_install.scripts(
       
   306                 [(self.options.get('testrunner', 'test'),
       
   307                   'djangorecipe.test', 'main')],
       
   308                 working_set, self.options['executable'],
       
   309                 self.options['bin-directory'],
       
   310                 extra_paths = extra_paths,
       
   311                 arguments= "'%s.%s', %s" % (
       
   312                     self.options['project'],
       
   313                     self.options['settings'],
       
   314                     ', '.join(["'%s'" % app for app in apps])))
       
   315         else:
       
   316             return []
       
   317 
       
   318 
       
   319     def create_project(self, project_dir):
       
   320         os.makedirs(project_dir)
       
   321 
       
   322         template_vars = {'secret': self.generate_secret()}
       
   323         template_vars.update(self.options)
       
   324 
       
   325         self.create_file(
       
   326             os.path.join(project_dir, 'development.py'),
       
   327             development_settings, template_vars)
       
   328 
       
   329         self.create_file(
       
   330             os.path.join(project_dir, 'production.py'),
       
   331             production_settings, template_vars)
       
   332 
       
   333         self.create_file(
       
   334             os.path.join(project_dir, 'urls.py'),
       
   335             urls_template, template_vars)
       
   336 
       
   337         self.create_file(
       
   338             os.path.join(project_dir, 'settings.py'),
       
   339             settings_template, template_vars)
       
   340 
       
   341         # Create the media and templates directories for our
       
   342         # project
       
   343         os.mkdir(os.path.join(project_dir, 'media'))
       
   344         os.mkdir(os.path.join(project_dir, 'templates'))
       
   345 
       
   346         # Make the settings dir a Python package so that Django
       
   347         # can load the settings from it. It will act like the
       
   348         # project dir.
       
   349         open(os.path.join(project_dir, '__init__.py'), 'w').close()
       
   350 
       
   351     def make_scripts(self, extra_paths, ws):
       
   352         scripts = []
       
   353         _script_template = zc.buildout.easy_install.script_template
       
   354         for protocol in ('wsgi', 'fcgi'):
       
   355             zc.buildout.easy_install.script_template = \
       
   356                 zc.buildout.easy_install.script_header + \
       
   357                     script_template[protocol]
       
   358             if self.options.get(protocol, '').lower() == 'true':
       
   359                 project = self.options.get('projectegg',
       
   360                                            self.options['project'])
       
   361                 scripts.extend(
       
   362                     zc.buildout.easy_install.scripts(
       
   363                         [('%s.%s' % (self.options.get('control-script',
       
   364                                                       self.name),
       
   365                                      protocol),
       
   366                           'djangorecipe.%s' % protocol, 'main')],
       
   367                         ws,
       
   368                         self.options['executable'],
       
   369                         self.options['bin-directory'],
       
   370                         extra_paths=extra_paths,
       
   371                         arguments= "'%s.%s', logfile='%s'" % (
       
   372                             project, self.options['settings'],
       
   373                             self.options.get('logfile'))))
       
   374         zc.buildout.easy_install.script_template = _script_template
       
   375         return scripts
       
   376 
       
   377     def is_svn_url(self, version):
       
   378         # Search if there is http/https/svn or svn+[a tunnel identifier] in the
       
   379         # url or if the trunk marker is used, all indicating the use of svn
       
   380         svn_version_search = re.compile(
       
   381             r'^(http|https|svn|svn\+[a-zA-Z-_]+)://|^(trunk)$').search(version)
       
   382         return svn_version_search is not None
       
   383 
       
   384     def version_to_svn(self, version):
       
   385         if version == 'trunk':
       
   386             return 'http://code.djangoproject.com/svn/django/trunk/'
       
   387         else:
       
   388             return version
       
   389 
       
   390     def version_to_download_suffix(self, version):
       
   391         if version == 'trunk':
       
   392             return 'svn'
       
   393         return [p for p in version.split('/') if p][-1]
       
   394 
       
   395     def svn_update(self, path, version):
       
   396         command = 'svn up'
       
   397         revision_search = re.compile(r'@([0-9]*)$').search(
       
   398             self.options['version'])
       
   399 
       
   400         if revision_search is not None:
       
   401             command += ' -r ' + revision_search.group(1)
       
   402         self.log.info("Updating Django from svn")
       
   403         if not self.buildout['buildout'].get('verbosity'):
       
   404             command += ' -q'
       
   405         return self.command(command, cwd=path)
       
   406 
       
   407     def get_extra_paths(self):
       
   408         extra_paths = [self.options['location'],
       
   409                        self.buildout['buildout']['directory']
       
   410                        ]
       
   411 
       
   412         # Add libraries found by a site .pth files to our extra-paths.
       
   413         if 'pth-files' in self.options:
       
   414             import site
       
   415             for pth_file in self.options['pth-files'].splitlines():
       
   416                 pth_libs = site.addsitedir(pth_file, set())
       
   417                 if not pth_libs:
       
   418                     self.log.warning(
       
   419                         "No site *.pth libraries found for pth_file=%s" % (
       
   420                          pth_file,))
       
   421                 else:
       
   422                     self.log.info("Adding *.pth libraries=%s" % pth_libs)
       
   423                     self.options['extra-paths'] += '\n' + '\n'.join(pth_libs)
       
   424 
       
   425         pythonpath = [p.replace('/', os.path.sep) for p in
       
   426                       self.options['extra-paths'].splitlines() if p.strip()]
       
   427 
       
   428         extra_paths.extend(pythonpath)
       
   429         return extra_paths
       
   430 
       
   431     def update(self):
       
   432         newest = self.buildout['buildout'].get('newest') != 'false'
       
   433         if newest and not self.install_from_cache and \
       
   434                 self.is_svn_url(self.options['version']):
       
   435             self.svn_update(self.options['location'], self.options['version'])
       
   436 
       
   437         extra_paths = self.get_extra_paths()
       
   438         requirements, ws = self.egg.working_set(['djangorecipe'])
       
   439         # Create the Django management script
       
   440         self.create_manage_script(extra_paths, ws)
       
   441 
       
   442         # Create the test runner
       
   443         self.create_test_runner(extra_paths, ws)
       
   444 
       
   445         # Make the wsgi and fastcgi scripts if enabled
       
   446         self.make_scripts(extra_paths, ws)
       
   447 
       
   448     def command(self, cmd, **kwargs):
       
   449         output = subprocess.PIPE
       
   450         if self.buildout['buildout'].get('verbosity'):
       
   451             output = None
       
   452         command = subprocess.Popen(
       
   453             cmd, shell=True, stdout=output, **kwargs)
       
   454         return command.wait()
       
   455 
       
   456     def create_file(self, file, template, options):
       
   457         if os.path.exists(file):
       
   458             return
       
   459 
       
   460         f = open(file, 'w')
       
   461         f.write(template % options)
       
   462         f.close()
       
   463 
       
   464     def generate_secret(self):
       
   465         chars = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)'
       
   466         return ''.join([choice(chars) for i in range(50)])