Index: django/core/management/commands/startapp.py
===================================================================
--- django/core/management/commands/startapp.py	(Revision 6193)
+++ django/core/management/commands/startapp.py	(Arbeitskopie)
@@ -1,7 +1,12 @@
+import os
+from optparse import make_option
 from django.core.management.base import copy_helper, CommandError, LabelCommand
-import os
 
 class Command(LabelCommand):
+    option_list = LabelCommand.option_list + (
+        make_option('--noskeleton', action='store_false', dest='use_skeleton', default=True,
+            help='Tells Django to disable the metadata assistent and skip extended skeleton file creation.'),
+    )
     help = "Creates a Django app directory structure for the given app name in the current directory."
     args = "[appname]"
     label = 'application name'
@@ -12,6 +17,7 @@
     can_import_settings = False
 
     def handle_label(self, app_name, directory=None, **options):
+        use_skeleton = options.get('use_skeleton', True)
         if directory is None:
             directory = os.getcwd()
         # Determine the project_name a bit naively -- by looking at the name of
@@ -19,9 +25,19 @@
         project_dir = os.path.normpath(os.path.join(directory, '..'))
         parent_dir = os.path.basename(project_dir)
         project_name = os.path.basename(directory)
-        if app_name == project_name:
-            raise CommandError("You cannot create an app with the same name (%r) as your project." % app_name)
+        if use_skeleton:
+            directory = os.path.join(directory, app_name)
+            try:
+                os.mkdir(directory)
+            except OSError, e:
+                raise CommandError(e)
+        else:
+            if app_name == project_name:
+                raise CommandError("You cannot create an app with the same name (%r) as your project." % app_name)
         copy_helper(self.style, 'app', app_name, directory, parent_dir)
+        if use_skeleton:
+            from django.utils import package
+            package.standalone_app(directory, app_name)
 
 class ProjectCommand(Command):
     help = "Creates a Django app directory structure for the given app name in this project's directory."
Index: django/core/management/commands/editapp.py
===================================================================
--- django/core/management/commands/editapp.py	(Revision 0)
+++ django/core/management/commands/editapp.py	(Revision 0)
@@ -0,0 +1,31 @@
+from django.core.management.base import LabelCommand, CommandError
+import os
+
+class Command(LabelCommand):
+    help = "Edits releae metadata of the Django app in the current directory."
+    args = "[appname]"
+    label = 'application name'
+    
+    requires_model_validation = False
+    # Can't import settings during this command, because they haven't
+    # necessarily been created.
+    can_import_settings = False
+    
+    def handle_label(self, app_name, directory=None, **options):
+        if directory is None:
+            directory = os.getcwd()
+        release_dir = os.path.join(directory, app_name)
+        release_file = os.path.join(directory, "release.py")
+        
+        if os.path.isdir(release_dir) and os.path.isfile(release_file):
+            from django.utils import package
+            release = package.Release(directory)
+            if release.loaded:
+                package.standalone_app(directory)
+            else:
+                raise CommandError("The release.py file does not contain any data.")
+        elif not os.path.isdir(release_dir) and \
+                os.path.isfile(release_file):
+            raise CommandError("There is a release.py file but the given appname doesn't match.")
+        else:
+            raise CommandError("There is no release.py file in the current directory.")
Index: django/views/debug.py
===================================================================
--- django/views/debug.py	(Revision 6193)
+++ django/views/debug.py	(Arbeitskopie)
@@ -705,7 +705,7 @@
   <p>Of course, you haven't actually done any work yet. Here's what to do next:</p>
   <ul>
     <li>If you plan to use a database, edit the <code>DATABASE_*</code> settings in <code>{{ project_name }}/settings.py</code>.</li>
-    <li>Start your first app by running <code>python {{ project_name }}/manage.py startapp [appname]</code>.</li>
+    <li>Start your first app by running <code>python {{ project_name }}/manage.py startapp --noskeleton [appname]</code>.</li>
   </ul>
 </div>
 
Index: django/utils/package/validators.py
===================================================================
--- django/utils/package/validators.py	(Revision 0)
+++ django/utils/package/validators.py	(Revision 0)
@@ -0,0 +1,49 @@
+"Validators used for package management"
+import os, sys, re
+from pkg_resources import safe_version, safe_name
+from django.core import validators
+
+INVALID_APP_NAMES = ('django', 'site', 'test', 'tests', 'release', 'doc', 'docs')
+find_keywords_re = re.compile(r'[-\w\.]+')
+
+def isvalidname(value):
+    if value.lower() in INVALID_APP_NAMES:
+        sys.stderr.write("Error: That app name is invalid. Please use another name.\n")
+        return False
+    return True
+
+def isvalidversion(value):
+    if not value or value.startswith("-"):
+        sys.stderr.write("Error: That version number is invalid. Use only letters and digits.\n")
+        return False
+    return True
+
+def isalnum(value):
+    if not value.isalnum():
+        sys.stderr.write("Error: That input is invalid. Use only letters, digits and underscores.\n")
+        return False
+    return True
+
+def isgiven(value):
+    if not value:
+        sys.stderr.write("Error: This field is required.\n")
+        return False
+    return True
+
+def iskeywordlist(value):
+    if not find_keywords_re.search(value):
+        sys.stderr.write("Error: This keyword list is invalid.\n")
+        return False
+    return True
+
+def isemailadress(value):
+    if not validators.email_re.search(value):
+        sys.stderr.write("Error: That e-mail address is invalid.\n")
+        return False
+    return True
+
+def isyesorno(value):
+    if not value.lower() in ('yes', 'no'):
+        sys.stderr.write("Error: Please enter yes or no.\n")
+        return False
+    return True
Index: django/utils/package/ez_setup.py
===================================================================
--- django/utils/package/ez_setup.py	(Revision 0)
+++ django/utils/package/ez_setup.py	(Revision 0)
@@ -0,0 +1,230 @@
+#!python
+"""Bootstrap setuptools installation
+
+If you want to use setuptools in your package's setup.py, just include this
+file in the same directory with it, and add this to the top of your setup.py::
+
+    from ez_setup import use_setuptools
+    use_setuptools()
+
+If you want to require a specific version of setuptools, set a download
+mirror, or use an alternate download directory, you can do so by supplying
+the appropriate options to ``use_setuptools()``.
+
+This file can also be run as a script to install or upgrade setuptools.
+"""
+import sys
+DEFAULT_VERSION = "0.6c6"
+DEFAULT_URL     = "http://cheeseshop.python.org/packages/%s/s/setuptools/" % sys.version[:3]
+
+md5_data = {
+    'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
+    'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
+    'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
+    'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
+    'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
+    'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
+    'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
+    'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
+    'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
+    'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
+    'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
+    'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
+    'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
+    'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
+    'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
+    'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
+    'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
+    'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
+    'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
+    'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
+    'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
+    'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
+    'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
+    'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
+}
+
+import sys, os
+
+def _validate_md5(egg_name, data):
+    if egg_name in md5_data:
+        from md5 import md5
+        digest = md5(data).hexdigest()
+        if digest != md5_data[egg_name]:
+            print >>sys.stderr, (
+                "md5 validation of %s failed!  (Possible download problem?)"
+                % egg_name
+            )
+            sys.exit(2)
+    return data
+
+
+def use_setuptools(
+    version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
+    download_delay=15
+):
+    """Automatically find/download setuptools and make it available on sys.path
+
+    `version` should be a valid setuptools version number that is available
+    as an egg for download under the `download_base` URL (which should end with
+    a '/').  `to_dir` is the directory where setuptools will be downloaded, if
+    it is not already available.  If `download_delay` is specified, it should
+    be the number of seconds that will be paused before initiating a download,
+    should one be required.  If an older version of setuptools is installed,
+    this routine will print a message to ``sys.stderr`` and raise SystemExit in
+    an attempt to abort the calling script.
+    """
+    try:
+        import setuptools
+        if setuptools.__version__ == '0.0.1':
+            print >>sys.stderr, (
+            "You have an obsolete version of setuptools installed.  Please\n"
+            "remove it from your system entirely before rerunning this script."
+            )
+            sys.exit(2)
+    except ImportError:
+        egg = download_setuptools(version, download_base, to_dir, download_delay)
+        sys.path.insert(0, egg)
+        import setuptools; setuptools.bootstrap_install_from = egg
+
+    import pkg_resources
+    try:
+        pkg_resources.require("setuptools>="+version)
+
+    except pkg_resources.VersionConflict, e:
+        # XXX could we install in a subprocess here?
+        print >>sys.stderr, (
+            "The required version of setuptools (>=%s) is not available, and\n"
+            "can't be installed while this script is running. Please install\n"
+            " a more recent version first.\n\n(Currently using %r)"
+        ) % (version, e.args[0])
+        sys.exit(2)
+
+def download_setuptools(
+    version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
+    delay = 15
+):
+    """Download setuptools from a specified location and return its filename
+
+    `version` should be a valid setuptools version number that is available
+    as an egg for download under the `download_base` URL (which should end
+    with a '/'). `to_dir` is the directory where the egg will be downloaded.
+    `delay` is the number of seconds to pause before an actual download attempt.
+    """
+    import urllib2, shutil
+    egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
+    url = download_base + egg_name
+    saveto = os.path.join(to_dir, egg_name)
+    src = dst = None
+    if not os.path.exists(saveto):  # Avoid repeated downloads
+        try:
+            from distutils import log
+            if delay:
+                log.warn("""
+---------------------------------------------------------------------------
+This script requires setuptools version %s to run (even to display
+help).  I will attempt to download it for you (from
+%s), but
+you may need to enable firewall access for this script first.
+I will start the download in %d seconds.
+
+(Note: if this machine does not have network access, please obtain the file
+
+   %s
+
+and place it in this directory before rerunning this script.)
+---------------------------------------------------------------------------""",
+                    version, download_base, delay, url
+                ); from time import sleep; sleep(delay)
+            log.warn("Downloading %s", url)
+            src = urllib2.urlopen(url)
+            # Read/write all in one block, so we don't create a corrupt file
+            # if the download is interrupted.
+            data = _validate_md5(egg_name, src.read())
+            dst = open(saveto,"wb"); dst.write(data)
+        finally:
+            if src: src.close()
+            if dst: dst.close()
+    return os.path.realpath(saveto)
+
+def main(argv, version=DEFAULT_VERSION):
+    """Install or upgrade setuptools and EasyInstall"""
+
+    try:
+        import setuptools
+    except ImportError:
+        egg = None
+        try:
+            egg = download_setuptools(version, delay=0)
+            sys.path.insert(0,egg)
+            from setuptools.command.easy_install import main
+            return main(list(argv)+[egg])   # we're done here
+        finally:
+            if egg and os.path.exists(egg):
+                os.unlink(egg)
+    else:
+        if setuptools.__version__ == '0.0.1':
+            # tell the user to uninstall obsolete version
+            use_setuptools(version)
+
+    req = "setuptools>="+version
+    import pkg_resources
+    try:
+        pkg_resources.require(req)
+    except pkg_resources.VersionConflict:
+        try:
+            from setuptools.command.easy_install import main
+        except ImportError:
+            from easy_install import main
+        main(list(argv)+[download_setuptools(delay=0)])
+        sys.exit(0) # try to force an exit
+    else:
+        if argv:
+            from setuptools.command.easy_install import main
+            main(argv)
+        else:
+            print "Setuptools version",version,"or greater has been installed."
+            print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
+
+
+
+def update_md5(filenames):
+    """Update our built-in md5 registry"""
+
+    import re
+    from md5 import md5
+
+    for name in filenames:
+        base = os.path.basename(name)
+        f = open(name,'rb')
+        md5_data[base] = md5(f.read()).hexdigest()
+        f.close()
+
+    data = ["    %r: %r,\n" % it for it in md5_data.items()]
+    data.sort()
+    repl = "".join(data)
+
+    import inspect
+    srcfile = inspect.getsourcefile(sys.modules[__name__])
+    f = open(srcfile, 'rb'); src = f.read(); f.close()
+
+    match = re.search("\nmd5_data = {\n([^}]+)}", src)
+    if not match:
+        print >>sys.stderr, "Internal error!"
+        sys.exit(2)
+
+    src = src[:match.start(1)] + repl + src[match.end(1):]
+    f = open(srcfile,'w')
+    f.write(src)
+    f.close()
+
+
+if __name__=='__main__':
+    if len(sys.argv)>2 and sys.argv[1]=='--md5update':
+        update_md5(sys.argv[2:])
+    else:
+        main(sys.argv[1:])
+
+
+
+
Index: django/utils/package/__init__.py
===================================================================
--- django/utils/package/__init__.py	(Revision 0)
+++ django/utils/package/__init__.py	(Revision 0)
@@ -0,0 +1,209 @@
+from ez_setup import use_setuptools
+use_setuptools()
+
+import os, sys, urlparse, shutil
+from setuptools import setup as original_setup
+from pkg_resources import safe_version, safe_name
+
+from django.core.management.base import CommandError
+from django.utils.package.prompts import *
+
+def setup(**args):
+    """
+    Hacky wrapper to make sure that the default PyPI keyword is included when
+    the setup.py of an app is used
+    """
+    keywords = args.get('keyword', PYPI_KEYWORD)
+    if PYPI_KEYWORD not in keywords:
+        args['keyword'] = "%s %s" % (PYPI_KEYWORD, args['keyword'])
+    original_setup(args)
+
+def generate_default_files(template_dir):
+    """
+    Generates a list of filepaths and appendant content.
+    """
+    for d, subdirs, files in os.walk(template_dir):
+        for f in files:
+            parent = os.path.basename(d)
+            if f.startswith('.') or f.endswith('.pyc') or parent.startswith('.'):
+                continue
+            filepath = os.path.join(d, f)
+            filecontent = open(filepath, 'r').read()
+            yield (os.path.basename(filepath), filecontent)
+
+# could also be combined with skeleton files in django.conf (?)s
+TEMPLATE_DIR =  os.path.join(os.path.abspath(os.path.dirname(__file__)), "templates")
+DEFAULT_FILES = generate_default_files(TEMPLATE_DIR)
+DEFAULT_DIRECTORIES = ('docs', 'tests', '%(NAME)s/templates/%(NAME)s')
+APP_REPOSITORY_URL = "http://www.djangoproject.com/apps/"
+
+class Release(dict):
+    """
+    Wrapper to handle release metadata from release.py and make it available
+    as items and attributes of a dict object. Setting attributes will actually
+    set the dict items.
+    """
+    def __init__(self, directory, internal=None):
+        self.directory = directory
+        self.renamed_name = False
+        self.loaded = False
+        self.manifest = ""
+        
+        if internal is None:
+            internal = {}
+        self.load()
+        dict.__init__(self, internal)
+        self.__initialised = True
+    
+    def __getattr__(self, item):
+        """
+        Simulates getting attributes
+        """
+        try:
+            return self.__getitem__(item)
+        except KeyError:
+            return False
+    
+    def __setattr__(self, item, value):
+        """
+        Simulates setting attributes
+        """
+        if not self.__dict__.has_key('_Release__initialised'):
+            return dict.__setattr__(self, item, value)
+        elif self.__dict__.has_key(item):
+            dict.__setattr__(self, item, value)
+        else:
+            self.__setitem__(item, value)
+    
+    def exists(self):
+        """
+        Returns true if the release already exists and could be loaded
+        """
+        return len(self)>0
+    
+    def load(self):
+        """
+        Loads metadata from release.py into itself
+        """
+        sys.path.insert(0, self.directory)
+        os.chdir(self.directory)
+        try:
+            rel = __import__("release", {}, {}, [''])
+            for info in dir(rel):
+                if info == info.upper():
+                    self[info] = str(getattr(rel, info))
+        except (ImportError, AttributeError):
+            return
+        self.loaded = True
+    
+    def save(self, path, filename, data):
+        """
+        Saves given data to a file
+        """
+        filepath = os.path.join(path, filename)
+        try:
+            if os.path.isfile(filepath):
+                filecontent = open(filepath, 'r').read()
+                if filecontent == data:
+                    return
+            f = open(filepath, 'w')
+            f.write(data)
+            f.close()
+        except OSError, e:
+            raise CommandError(e)
+        print "Written: %s" % filepath
+    
+    def check_path(self):
+        """
+        Moves an app directory if the app was renamed (except templates dir).
+        """
+        if self.renamed_name:
+            old_path = os.path.join(self.directory, self.renamed_name)
+            new_path = os.path.join(self.directory, self.NAME)
+            try:
+                shutil.move(old_path, new_path)
+            except (IOError, OSError), e:
+                raise CommandError(e)
+            else:
+                print "Moved: %r to %r" % (old_path, new_path)
+                print "If necessary please move the templates manually."
+            self.renamed_name = False
+    
+    def update(self, directories=DEFAULT_DIRECTORIES, files=DEFAULT_FILES):
+        """
+        Creates default directories and default files and should be called at
+        the end of the assistent.
+        """
+        # check if the app was renamed and move it if yes
+        self.check_path()
+        # create default directories
+        
+        for newdir in directories:
+            try:
+                newpath = os.path.join(self.directory, newdir % self)
+                if not os.path.exists(newpath):
+                    os.makedirs(newpath)
+                    print "Created: %s" % newpath
+                elif not os.access(newpath, os.W_OK):
+                    raise CommandError("You have no write access to the directory.")
+            except OSError, e:
+                raise CommandError(e)
+            self.manifest += "recursive-include %s *\n" % newdir % self
+        
+        # write default files
+        for newfile in files:
+            if self.loaded and newfile[0].endswith(".txt"):
+                continue
+            self.save(self.directory, newfile[0], newfile[1] % self)
+            self.manifest += "include %s\n" % newfile[0] % self
+        
+        # save manifest to file
+        self.save(self.directory, "MANIFEST.in", self.manifest)
+        self.loaded = True
+
+    def clean_up(self):
+        """
+        Helper function for file clean-up after interruption
+        """
+        try:
+            shutil.rmtree(self.directory, True)
+        except:
+            pass
+
+def ask(prompt):
+    return prompt.ask()
+
+def standalone_app(directory, default_name=None):
+    """
+    Helper function for creating a setup.py file for modules under the current
+    workding dir. All arguments are required and will be prompted for if
+    invalid or not given.
+    """
+    release = Release(directory)
+    if default_name is not None and default_name.lower() in INVALID_APP_NAMES:
+        release.clean_up(directory)
+        raise CommandError("%r conflicts with the name of some package files and cannot be used as an app name. Please try another name." % default_name)
+    if release.loaded:
+        print "Django app found: %s (%s)." % (release.NAME, release.VERSION)
+    try:
+        name = Name(default_name or release.NAME or '').ask()
+        # what if the name was changed during editapp?
+        if name not in (release.NAME, default_name):
+            release.renamed_name = release.NAME or default_name
+        release.NAME = name
+        release.URL = urlparse.urljoin(APP_REPOSITORY_URL, name)
+        release.VERSION = Version(release.VERSION or '').ask()
+        release.SUMMARY = Summary(release.SUMMARY or '').ask()
+        release.KEYWORDS = Keywords(release.KEYWORDS or '').ask()
+        release.AUTHOR = Author(release.AUTHOR or '').ask()
+        release.AUTHOR_EMAIL = Email(release.AUTHOR_EMAIL or '').ask()
+        release.ZIP_SAFE = ZipSafe(release.ZIP_SAFE or '').ask()
+        
+    except (KeyboardInterrupt, EOFError):
+        # Delete directory if canceled
+        if not release.loaded:
+            clean_up(directory)
+        print "\n"
+        raise CommandError("Operation cancelled.")
+    # Update the release with the new meta data
+    release.update()
Index: django/utils/package/prompts.py
===================================================================
--- django/utils/package/prompts.py	(Revision 0)
+++ django/utils/package/prompts.py	(Revision 0)
@@ -0,0 +1,92 @@
+from django.utils.package.validators import *
+PYPI_KEYWORD = "django.apps"
+
+class Prompt(object):
+    """Simple base class for prompting"""
+    def __init__(self, default=None):
+        self.default = default
+        self.title = self.__class__.__name__
+        self.preflight()
+        self.setup()
+    
+    def setup(self):
+        "sets up the user dialog"
+        self.question = self.title
+        if self.default is not None:
+            self.question += ' (%r)' % self.default
+        self.question += ': '
+    
+    def ask(self):
+        "main loop for user input"
+        while 1:
+            response = raw_input(self.question).strip()
+            if not response:
+                if self.default is not None:
+                    response = self.default
+            if self.validate(response):
+                return self.postflight(response)
+    
+    def preflight(self):
+        "can be used to manipulate the default data"
+        pass
+    
+    def validate(self, response):
+        "validates user response against validators"
+        return isgiven(response)
+    
+    def postflight(self, response):
+        "can be used to manipulate the response"
+        return response
+
+class Name(Prompt):
+    "the application and directory name"
+    def validate(self, response):
+        response = safe_name(response)
+        return isalnum(response) and isvalidname(response)
+
+class Version(Prompt):
+    "the version, validated against pkg_resources"
+    def validate(self, response):
+        response = safe_version(response)
+        return isvalidversion(response)
+        
+class Keywords(Prompt):
+    "the keywords, includes the default PyPI keyword"
+    def preflight(self):
+        keywordlist = find_keywords_re.findall(self.default)
+        if PYPI_KEYWORD in keywordlist:
+            keywordlist.remove(PYPI_KEYWORD)
+            self.default = " ".join(keywordlist).strip()
+    
+    def validate(self, response):
+        return isgiven(response) and iskeywordlist(response)
+    
+    def postflight(self, response):
+        keywordlist = find_keywords_re.findall(response)
+        keywordlist.insert(0,PYPI_KEYWORD)
+        return " ".join(keywordlist).strip()
+
+class Email(Prompt):
+    "e-mail adress of the application author"
+    def validate(self, response):
+        return isgiven(response) and isemailadress(response)
+
+class ZipSafe(Prompt):
+    "defines if the egg distribution can be a zipped file while installation"
+    def preflight(self):
+        if self.default.lower() in ("true", "false"):
+            self.default = {'true':'yes','false':'no'}[self.default.lower()]
+    
+    def validate(self, response):
+        return isyesorno(response)
+    
+    def postflight(self, response):
+        return {'yes':True,'no':False}[response]
+
+class Author(Prompt):
+    "name of the app author and owner"
+    pass
+
+class Summary(Prompt):
+    "short summary about the app"
+    pass
Index: django/utils/package/templates/INSTALL.txt
===================================================================
--- django/utils/package/templates/INSTALL.txt	(Revision 0)
+++ django/utils/package/templates/INSTALL.txt	(Revision 0)
@@ -0,0 +1,12 @@
+Thanks for downloading "%(NAME)s".
+
+To install it, run the following command inside this directory:
+
+    python setup.py install
+
+Or if you'd prefer you can use any other setuptools function, or simply
+place the included ``%(NAME)s`` directory somewhere on your Python path.
+
+Note that this application requires Python 2.3 or later, and Django
+0.97 or later. You can obtain Python from http://www.python.org/ and
+Django from http://www.djangoproject.com/.
Index: django/utils/package/templates/release.py
===================================================================
--- django/utils/package/templates/release.py	(Revision 0)
+++ django/utils/package/templates/release.py	(Revision 0)
@@ -0,0 +1,9 @@
+# Metadata used for packaging, handle with care
+NAME = "%(NAME)s"
+VERSION = "%(VERSION)s"
+URL = "%(URL)s"
+AUTHOR = "%(AUTHOR)s"
+AUTHOR_EMAIL = "%(AUTHOR_EMAIL)s"
+SUMMARY = "%(SUMMARY)s"
+KEYWORDS = "%(KEYWORDS)s"
+ZIP_SAFE = %(ZIP_SAFE)s
Index: django/utils/package/templates/setup.py
===================================================================
--- django/utils/package/templates/setup.py	(Revision 0)
+++ django/utils/package/templates/setup.py	(Revision 0)
@@ -0,0 +1,20 @@
+from setuptools import find_packages
+from django.utils.package import setup
+
+try:
+    import release
+except ImportError:
+    raise "Please add a release.py file to the application directory."
+
+setup(
+    name = release.NAME,
+    version = release.VERSION,
+    url = release.URL,
+    author = release.AUTHOR,
+    author_email = release.AUTHOR_EMAIL,
+    summary = release.SUMMARY,
+    zip_safe = release.ZIP_SAFE,
+    keywords = release.KEYWORDS,
+    packages = find_packages(),
+    include_package_data = True,
+)
Index: django/utils/package/templates/README.txt
===================================================================
--- django/utils/package/templates/README.txt	(Revision 0)
+++ django/utils/package/templates/README.txt	(Revision 0)
@@ -0,0 +1,4 @@
+%(NAME)s
+==============================================================================
+
+TODO
Index: extras/django_bash_completion
===================================================================
--- extras/django_bash_completion	(Revision 6193)
+++ extras/django_bash_completion	(Arbeitskopie)
@@ -42,7 +42,7 @@
     prev="${COMP_WORDS[COMP_CWORD-1]}"
 
     # Standalone options
-    opts="--help --settings --pythonpath --noinput --noreload --format --indent --verbosity --adminmedia --version"
+    opts="--help --settings --pythonpath --noinput --noreload --noskeleton --format --indent --verbosity --adminmedia --version"
     # Actions
     actions="adminindex createcachetable dbshell diffsettings \
              dumpdata flush inspectdb loaddata reset runfcgi runserver \
Index: docs/tutorial01.txt
===================================================================
--- docs/tutorial01.txt	(Revision 6193)
+++ docs/tutorial01.txt	(Arbeitskopie)
@@ -220,7 +220,7 @@
 To create your app, make sure you're in the ``mysite`` directory and type
 this command::
 
-    python manage.py startapp polls
+    python manage.py startapp --noskeleton polls
 
 That'll create a directory ``polls``, which is laid out like this::
 
Index: docs/tutorial03.txt
===================================================================
--- docs/tutorial03.txt	(Revision 6193)
+++ docs/tutorial03.txt	(Arbeitskopie)
@@ -418,8 +418,8 @@
 with minimal fuss.
 
 Our poll app is pretty decoupled at this point, thanks to the strict directory
-structure that ``python manage.py startapp`` created, but one part of it is
-coupled to the Django settings: The URLconf.
+structure that ``python manage.py startapp --noskeleton`` created, but one
+part of it is coupled to the Django settings: The URLconf.
 
 We've been editing the URLs in ``mysite/urls.py``, but the URL design of an
 app is specific to the app, not to the Django installation -- so let's move the
Index: docs/man/django-admin.1
===================================================================
--- docs/man/django-admin.1	(Revision 6193)
+++ docs/man/django-admin.1	(Arbeitskopie)
@@ -87,9 +87,11 @@
 Prints the SQL statements for resetting PostgreSQL sequences for the
 given app name(s).
 .TP
-.BI "startapp [" "appname" "]"
+.BI "startapp [" "\-\-noskeleton" "] [" "appname" "]"
 Creates a Django app directory structure for the given app name in
 the current directory.
+.BI \-\-noskeleton
+option disables the assistent for creating reusable Django apps.
 .TP
 .BI "startproject [" "projectname" "]"
 Creates a Django project directory structure for the given project name
Index: docs/django-admin.txt
===================================================================
--- docs/django-admin.txt	(Revision 6193)
+++ docs/django-admin.txt	(Arbeitskopie)
@@ -505,6 +505,14 @@
 Creates a Django app directory structure for the given app name in the current
 directory.
 
+Turning off the metadata assistent
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Use the ``--noskeleton`` option to disable the assistent which asks for
+metadata to create reusable Django apps. This means you won't be asked for
+detailed information about the app you are going to create and no additional
+files will be created.
+
 startproject <projectname>
 --------------------------
 
