[Checkins] SVN: z3c.feature.core/trunk/ initial import

Paul Carduner paulcarduner at gmail.com
Fri Mar 27 04:52:36 EDT 2009


Log message for revision 98393:
  initial import

Changed:
  A   z3c.feature.core/trunk/
  A   z3c.feature.core/trunk/bootstrap.py
  A   z3c.feature.core/trunk/buildout.cfg
  A   z3c.feature.core/trunk/setup.py
  A   z3c.feature.core/trunk/src/
  A   z3c.feature.core/trunk/src/z3c/
  A   z3c.feature.core/trunk/src/z3c/__init__.py
  A   z3c.feature.core/trunk/src/z3c/feature/
  A   z3c.feature.core/trunk/src/z3c/feature/__init__.py
  A   z3c.feature.core/trunk/src/z3c/feature/core/
  A   z3c.feature.core/trunk/src/z3c/feature/core/README.txt
  A   z3c.feature.core/trunk/src/z3c/feature/core/__init__.py
  A   z3c.feature.core/trunk/src/z3c/feature/core/base.py
  A   z3c.feature.core/trunk/src/z3c/feature/core/base.txt
  A   z3c.feature.core/trunk/src/z3c/feature/core/command-line-project.xml
  A   z3c.feature.core/trunk/src/z3c/feature/core/example.txt
  A   z3c.feature.core/trunk/src/z3c/feature/core/file-templates/
  A   z3c.feature.core/trunk/src/z3c/feature/core/file-templates/README.txt
  A   z3c.feature.core/trunk/src/z3c/feature/core/file-templates/index-template.txt
  A   z3c.feature.core/trunk/src/z3c/feature/core/file-templates/test_doc.py
  A   z3c.feature.core/trunk/src/z3c/feature/core/index.txt
  A   z3c.feature.core/trunk/src/z3c/feature/core/interfaces.py
  A   z3c.feature.core/trunk/src/z3c/feature/core/metadata.py
  A   z3c.feature.core/trunk/src/z3c/feature/core/metadata.txt
  A   z3c.feature.core/trunk/src/z3c/feature/core/python-package-project.xml
  A   z3c.feature.core/trunk/src/z3c/feature/core/python.py
  A   z3c.feature.core/trunk/src/z3c/feature/core/python.txt
  A   z3c.feature.core/trunk/src/z3c/feature/core/template.py
  A   z3c.feature.core/trunk/src/z3c/feature/core/testing.py
  A   z3c.feature.core/trunk/src/z3c/feature/core/tests/
  A   z3c.feature.core/trunk/src/z3c/feature/core/tests/__init__.py
  A   z3c.feature.core/trunk/src/z3c/feature/core/tests/test_doc.py
  A   z3c.feature.core/trunk/src/z3c/feature/core/unittest.py
  A   z3c.feature.core/trunk/src/z3c/feature/core/unittest.txt
  A   z3c.feature.core/trunk/src/z3c/feature/core/web/
  A   z3c.feature.core/trunk/src/z3c/feature/core/web/__init__.py
  A   z3c.feature.core/trunk/src/z3c/feature/core/web/metadata.py
  A   z3c.feature.core/trunk/src/z3c/feature/core/web/python.py
  A   z3c.feature.core/trunk/src/z3c/feature/core/web/templates/
  A   z3c.feature.core/trunk/src/z3c/feature/core/web/templates/comment-header-zpl.pt
  A   z3c.feature.core/trunk/src/z3c/feature/core/web/templates/metadata.js
  A   z3c.feature.core/trunk/src/z3c/feature/core/web/templates/metadata.pt
  A   z3c.feature.core/trunk/src/z3c/feature/core/web/templates/proprietary-header.pt
  A   z3c.feature.core/trunk/src/z3c/feature/core/web/unittest.py
  A   z3c.feature.core/trunk/src/z3c/feature/core/xml.py
  A   z3c.feature.core/trunk/src/z3c/feature/core/xml.txt

-=-
Added: z3c.feature.core/trunk/bootstrap.py
===================================================================
--- z3c.feature.core/trunk/bootstrap.py	                        (rev 0)
+++ z3c.feature.core/trunk/bootstrap.py	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id$
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+ez = {}
+exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+                     ).read() in ez
+ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+import pkg_resources
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+    cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+    os.P_WAIT, sys.executable, sys.executable,
+    '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
+    dict(os.environ,
+         PYTHONPATH=
+         ws.find(pkg_resources.Requirement.parse('setuptools')).location
+         ),
+    ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)


Property changes on: z3c.feature.core/trunk/bootstrap.py
___________________________________________________________________
Added: svn:keywords
   + Id

Added: z3c.feature.core/trunk/buildout.cfg
===================================================================
--- z3c.feature.core/trunk/buildout.cfg	                        (rev 0)
+++ z3c.feature.core/trunk/buildout.cfg	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,32 @@
+[buildout]
+extends = http://download.zope.org/zope3.4/3.4.0/versions.cfg
+develop = .
+          ../z3c.builder.core
+parts = python test coverage-test coverage-report
+versions = versions
+
+[python]
+recipe = zc.recipe.egg
+interpreter = py
+eggs = z3c.feature.core
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = z3c.feature.core [test]
+
+[coverage-test]
+recipe = zc.recipe.testrunner
+eggs = z3c.feature.core [test]
+defaults = ['--coverage', '../../coverage']
+
+[coverage-report]
+recipe = zc.recipe.egg
+eggs = z3c.coverage
+scripts = coverage=coverage-report
+arguments = ('coverage', 'coverage/report')
+
+[gtkeggdeps]
+recipe = zc.recipe.egg
+scripts = gtkeggdeps
+eggs = gtkeggdeps
+       z3c.feature.core

Added: z3c.feature.core/trunk/setup.py
===================================================================
--- z3c.feature.core/trunk/setup.py	                        (rev 0)
+++ z3c.feature.core/trunk/setup.py	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,65 @@
+##############################################################################
+#
+# Copyright (c) 2009 Paul Carduner and Stephan Richter.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+
+"""Setup"""
+from setuptools import setup, find_packages
+
+setup (
+    name = 'z3c.feature.core',
+    version = '0.1.0',
+    author = u"Paul Carduner and Stephan Richter",
+    author_email = u"zope-dev at zope.org",
+    description = u"Core Features to use with z3c.builder.core",
+    license = "ZPL",
+    keywords = u"zope3 project builder feature",
+    url = "http://pypi.python.org/pypi/z3c.feature.core",
+    packages = find_packages('src'),
+    include_package_data = True,
+    package_dir = {'':'src'},
+    namespace_packages = ['z3c','z3c.feature'],
+    extras_require = {
+        'test':[
+            'zope.testing',
+            'z3c.coverage',
+            ],
+        },
+    install_requires = [
+        'setuptools',
+        'z3c.builder.core',
+        ],
+    zip_safe = False,
+    entry_points = """
+    [z3c.boiler.template]
+    command-line = z3c.feature.core.template:CommandLineProjectTemplate
+    python-package = z3c.feature.core.template:PythonPackageProjectTemplate
+
+    [z3c.feature]
+    meta-data = z3c.feature.core.metadata:MetaDataFeature
+    comment-header-ZPL = z3c.feature.core.metadata:CommentHeaderZPLFeature
+    proprietary-header = z3c.feature.core.metadata:ProprietaryHeaderFeature
+    python-interpreter = z3c.feature.core.python:PythonInterpreterFeature
+    script = z3c.feature.core.python:ScriptFeature
+    unit-testing = z3c.feature.core.unittest:TestingFeature
+    documentation = z3c.feature.core.metadata:DocumentationFeature
+
+    [z3c.builderweb]
+    meta-data = z3c.feature.core.web.metadata:MetaDataWebFeature
+    comment-header-ZPL = z3c.feature.core.web.metadata:CommentHeaderZPLWebFeature
+    proprietary-header = z3c.feature.core.web.metadata:ProprietaryHeaderWebFeature
+    python-interpreter = z3c.feature.core.web.python:PythonInterpreterWebFeature
+    script = z3c.feature.core.web.python:ScriptWebFeature
+    unit-testing = z3c.feature.core.web.unittest:TestingWebFeature
+    documentation = z3c.feature.core.web.metadata:DocumentationWebFeature
+    """,
+    )


Property changes on: z3c.feature.core/trunk/setup.py
___________________________________________________________________
Added: svn:keywords
   + Id

Added: z3c.feature.core/trunk/src/z3c/__init__.py
===================================================================
--- z3c.feature.core/trunk/src/z3c/__init__.py	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/__init__.py	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,7 @@
+try:
+    # Declare this a namespace package if pkg_resources is available.
+    import pkg_resources
+    pkg_resources.declare_namespace(__name__)
+except ImportError:
+    pass
+


Property changes on: z3c.feature.core/trunk/src/z3c/__init__.py
___________________________________________________________________
Added: svn:keywords
   + Id

Added: z3c.feature.core/trunk/src/z3c/feature/__init__.py
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/__init__.py	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/__init__.py	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,6 @@
+try:
+    # Declare this a namespace package if pkg_resources is available.
+    import pkg_resources
+    pkg_resources.declare_namespace(__name__)
+except ImportError:
+    pass


Property changes on: z3c.feature.core/trunk/src/z3c/feature/__init__.py
___________________________________________________________________
Added: svn:keywords
   + Id

Added: z3c.feature.core/trunk/src/z3c/feature/core/README.txt
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/README.txt	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/README.txt	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,34 @@
+===============
+Boiler Features
+===============
+
+This package provides high-level components to build a project. The intend is
+that a user can select a set of features that make up a project and the system
+figures out how to create a coherent project from that set of features.
+
+- `base.txt`
+
+  This document provides a tutorial about how to write features based on the
+  base feature provided by this package.
+
+- `metadata.txt`
+
+  This file explains the metadata feature, which is used to create the
+  package's setup information.
+
+- `python.txt`
+
+  This file documents the basic Python features provided by the module.
+
+- `unittest.txt`
+
+  This document provides detailed documentation obout the testing feature.
+
+- `xml.txt`
+
+  This document describes how XML is converted into features and then applied
+  to a project.
+
+- `example.txt`
+
+  A quick example of producing a project from XML feature descriptions.


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/README.txt
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.feature.core/trunk/src/z3c/feature/core/__init__.py
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/__init__.py	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/__init__.py	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1 @@
+#module


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/__init__.py
___________________________________________________________________
Added: svn:keywords
   + Id

Added: z3c.feature.core/trunk/src/z3c/feature/core/base.py
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/base.py	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/base.py	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,203 @@
+##############################################################################
+#
+# Copyright (c) 2009 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Base Classes Feature
+
+$Id$
+"""
+import lxml.etree
+import pkg_resources
+import zope.interface
+import zope.schema
+from zope.schema.fieldproperty import FieldProperty
+from z3c.feature.core import interfaces, xml
+
+MISSING_DOCUMENTATION = """
+Sorry, but the authors of the \"%s\" feature are
+big meanies who don't like to make software
+accessible."""
+
+class BaseFeature(object):
+    zope.interface.implements(
+        interfaces.IFeature, interfaces.IBaseFeatureSchema)
+
+    featureSingleton = FieldProperty(interfaces.IFeature['featureSingleton'])
+    featureDependencies = ()
+
+    @property
+    def featureTitle(self):
+        """See ``z3c.feature.core.interfaces.IFeature``"""
+        return self.__class__.__name__
+
+    @property
+    def featureDocumentation(self):
+        """See ``z3c.feature.core.interfaces.IFeature``"""
+        return MISSING_DOCUMENTATION  % self.featureTitle
+
+    @classmethod
+    def fromXMLNode(cls, node, omit=()):
+        feature = cls()
+        schema = getFeatureSchema(feature)
+        data = xml.extractData(node, schema)
+        for fieldName in schema:
+            if fieldName in omit:
+                continue
+            value = data.get(fieldName)
+            if value:
+                setattr(feature, fieldName, value)
+        return feature
+
+    @classmethod
+    def fromXML(cls, xml):
+        tree = lxml.etree.fromstring(xml)
+        return cls.fromXMLNode(tree)
+
+    def findEntryPoint(self):
+        set = pkg_resources.working_set
+        for entryPoint in set.iter_entry_points(interfaces.FEATURE_GROUP):
+            if entryPoint.load() == self.__class__:
+                return entryPoint.dist.project_name, entryPoint.name
+        return None, None
+
+    def toXML(self, asString=False, prettyPrint=False):
+        feature = lxml.etree.Element('feature')
+        egg, name = self.findEntryPoint()
+        feature.set('type', egg + ':' + name if egg and name else 'unknown')
+        schema = getFeatureSchema(self)
+        for fieldName in zope.schema.getFields(schema):
+            if fieldName in interfaces.IFeature:
+                continue
+
+            value = getattr(self, fieldName)
+            if value != schema[fieldName].default:
+                featureOption = lxml.etree.SubElement(feature, fieldName)
+                if isinstance(value, (list, tuple, set)):
+                    for item in value:
+                        itemElem = lxml.etree.SubElement(featureOption, 'item')
+                        itemElem.text = str(item)
+                else:
+                    featureOption.text = str(value)
+        if asString:
+            return lxml.etree.tostring(feature, pretty_print=prettyPrint)
+        return feature
+
+    def update(self, features=None):
+        """See ``z3c.feature.core.interfaces.IFeature``"""
+
+    def _applyTo(self, context):
+        pass
+
+    def applyTo(self, context):
+        """See ``z3c.feature.core.interfaces.IFeature``"""
+        if interfaces.IHaveAppliedFeatures.providedBy(context):
+            context.appliedFeatures.append(self)
+        self._applyTo(context)
+
+    def __repr__(self):
+        return "<%s %r>" % (self.__class__.__name__, self.featureTitle)
+
+
+def getFeatureSchema(feature):
+    for iface in zope.interface.providedBy(feature).flattened():
+        if interfaces.IFeatureSchema.providedBy(iface):
+            return iface
+    return None
+
+
+def getFeatureTypes(feature):
+    return [iface
+            for iface in zope.interface.providedBy(feature).flattened()
+            if interfaces.IFeatureType.providedBy(iface)]
+
+
+def resolveDependencies(features, resolution=None, seen=None, types=None,
+                        all=None):
+    # List of all features.
+    if all is None:
+        all = features
+    # List of all feature types that have been found on singletons.
+    if types is None:
+        types = []
+    # List of features that have been resolved in the correct order.
+    if resolution is None:
+        resolution = []
+    # A list of seen features for a particular dependency sub-path.
+    if seen is None:
+        seen = []
+    # Loop through all features and resolve them.
+    for name, feature in features.items():
+        # If the feature is a singleton record its types.
+        if feature.featureSingleton:
+            for type in getFeatureTypes(feature):
+                if type in types:
+                    raise interfaces.DuplicateFeatureConflictError(type)
+                types.append(type)
+        # If a feature is already resolved, skip the feature.
+        if feature in resolution:
+            continue
+        # If we have seen the feature already, we have a cycle.
+        if feature in seen:
+            raise interfaces.CyclicDependencyError(seen[seen.index(feature):])
+        # If we do not have a cycle, add the feature to the list of seen ones
+        seen.append(feature)
+        # Resolve the dependencies of all children.
+        if feature.featureDependencies:
+            try:
+                deps = dict(
+                    [(name, all[name]) for name in feature.featureDependencies])
+            except KeyError:
+                raise interfaces.MissingFeatureDependencyError(feature, name)
+            resolveDependencies(deps, resolution, seen, types, all)
+        # Add the feature to the resolution.
+        if  feature not in resolution:
+            resolution.append(feature)
+        # Remove the feature from the current dependency path.
+        seen.pop()
+
+    return resolution
+
+def applyFeatures(features, project):
+    """Apply a list of features to the project."""
+    # Make sure the project collects all applied features
+    zope.interface.directlyProvides(project, interfaces.IHaveAppliedFeatures)
+    project.appliedFeatures = []
+    # Apply features one by one.
+    featureDict = dict(
+        [(feature.findEntryPoint()[1] or 'unknwon-%i' %idx, feature)
+         for idx, feature in enumerate(features)])
+    for feature in resolveDependencies(featureDict):
+        feature.update(featureDict)
+        feature.applyTo(project)
+
+
+class FileBasedTemplateBase(object):
+
+    _filename = None
+
+    def getFeatures(self):
+        if self._filename is None:
+            raise ValueError("Missing filename attribute.")
+        return xml.getFeatures(open(self._filename))
+
+
+def FileBasedTemplate(filename, title, description):
+    FileBasedTemplate = type(
+        '<FileBasedTemplate for %s' % filename,
+        (FileBasedTemplateBase,),
+        dict(title=title,
+             description=description,
+             _filename=filename))
+
+    zope.interface.classImplements(
+        FileBasedTemplate, interfaces.IProjectTemplateProvider)
+    return FileBasedTemplate


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/base.py
___________________________________________________________________
Added: svn:keywords
   + Id

Added: z3c.feature.core/trunk/src/z3c/feature/core/base.txt
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/base.txt	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/base.txt	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,413 @@
+=================
+Building Features
+=================
+
+Features are the high-level concept that is used to build projects. This
+document provides developers with a brief introduction on developing features
+for ZBoiler.
+
+Let's use an example. Let's say that we would like to implement a a feature
+called "Project Management" that creates a few text files -- `CHANGES.txt`,
+`TODO.txt`, and `AUTHORS.txt` -- in the project root.
+
+
+Getting Started
+---------------
+
+While not necessary, it is highly recommended to use the `BaseFeature` class
+as the base implementation, as it fulfills the entire feature API:
+
+  >>> from z3c.feature.core import base
+
+  >>> class ProjectManagementFeature(base.BaseFeature):
+  ...     pass
+
+Let's now instantiate the feature and ensure that the interface is fulfilled.
+
+  >>> pm = ProjectManagementFeature()
+
+  >>> from zope.interface.verify import verifyObject
+  >>> from z3c.feature.core import interfaces
+
+  >>> verifyObject(interfaces.IFeature, pm)
+  True
+
+Every feature must provide an interface that describes its configuration
+parameters. This interface is extracted using a small helper function:
+
+  >>> base.getFeatureSchema(pm)
+  <InterfaceClass z3c.feature.core.interfaces.IBaseFeatureSchema>
+
+In this case, the schema is trivial and we will later see how it can be
+changed. If no schema is found, `None` is returned:
+
+  >>> base.getFeatureSchema(object())
+
+A feature also provides a title and some documentation about itself.
+
+    >>> print pm.featureTitle
+    ProjectManagementFeature
+
+    >>> print pm.featureDocumentation
+    <BLANKLINE>
+    Sorry, but the authors of the "ProjectManagementFeature" feature are big
+    meanies who don't like to make software accessible.
+
+Clearly the default values are not desirable and we will change them in the
+next iteration of our project management feature.
+
+Since pluggability is an important aspect of the system, each feature should
+be registered as an entry point for the group:
+
+  >>> interfaces.FEATURE_GROUP
+  'z3c.feature'
+
+Since we have not yet registered the new feature as an entry point, finding
+the entry point results in nothing:
+
+  >>> pm.findEntryPoint()
+  (None, None)
+
+Usually, the first part of the result is the egg name and the latter is the
+entry point name within the group.
+
+Once the feature is setup, we can apply it to a project:
+
+  >>> from z3c.builder.core import project
+  >>> prj = project.ProjectBuilder(u'test')
+
+Before the feature can be applied, it is updated, so that it has the chance to
+fill in missing values. The `update()` method takes an optional argument that
+is a list of all features to be applied. This allows the feature to access
+other features data.
+
+  >>> pm.update({})
+
+Now we can apply the feature to the project:
+
+  >>> pm.applyTo(prj)
+
+Since we have not implemented any behavior, applying the project management
+feature has no visible effect:
+
+  >>> sorted(prj.keys())
+  []
+
+However, there is also a helper function that applies a list of features to a
+project:
+
+  >>> base.applyFeatures([pm], prj)
+
+A nice side effect of this function is that it tells the project the features
+it has applied:
+
+  >>> interfaces.IHaveAppliedFeatures.providedBy(prj)
+  True
+  >>> prj.appliedFeatures
+  [<ProjectManagementFeature 'ProjectManagementFeature'>]
+
+Another big part of the story is the ability to import from and export to XML,
+so that project definitions can be easily shared. Let's export our project
+management feature first:
+
+  >>> print pm.toXML(asString=True, prettyPrint=True)
+  <feature type="unknown"/>
+
+Pretty boring, simply because our feature dows not have a schema yet and has
+not been registered as an entry point. Let's register our feature as an entry
+point now:
+
+  >>> import pkg_resources
+  >>> from z3c.feature.core import testing
+
+  >>> dist = testing.FeatureDistribution()
+  >>> pkg_resources.working_set.add(dist)
+
+  >>> dist.addEntryPoint(
+  ...   interfaces.FEATURE_GROUP, 'ProjectManagement', ProjectManagementFeature)
+
+Now that the entry point is defined, the feature also finds it easily:
+
+  >>> pm.findEntryPoint()
+  ('z3c.feature.testing', 'ProjectManagement')
+
+Let's render the XML again:
+
+  >>> print pm.toXML(asString=True, prettyPrint=True)
+  <feature type="z3c.feature.testing:ProjectManagement"/>
+
+Of course we can also load a feature from XML. This is realized via a static
+method.
+
+  >>> ProjectManagementFeature.fromXML(
+  ...     '<feature type="z3c.feature.testing:ProjectManagement"/>')
+  <ProjectManagementFeature 'ProjectManagementFeature'>
+
+Again, since no feature schema is defined, importing is pretty simple as
+well. Let's now look into developing a more interesting feature.
+
+
+A Complete Feature
+------------------
+
+Let's now implement a complete feature that actually generates the promised
+files and provides better documentation as well.
+
+  >>> from z3c.builder.core.base import FileBuilder
+  >>> class HeaderFileBuilder(FileBuilder):
+  ...     def __init__(self, name, title=''):
+  ...         super(HeaderFileBuilder, self).__init__(name)
+  ...         self.title = title
+  ...     def render(self):
+  ...         cols = len(self.title)
+  ...         return '='*cols + '\n'+self.title+'\n' + '='*cols if cols else ''
+
+  >>> import zope.interface
+  >>> class IProjectManagementSchema(zope.interface.Interface):
+  ...     includeHeaders = zope.schema.Bool(
+  ...         title=u'Include Headers',
+  ...         description=u'If set, include headers in generated files.',
+  ...         default=False)
+  >>> zope.interface.alsoProvides(
+  ...     IProjectManagementSchema, interfaces.IFeatureSchema)
+
+  >>> class ProjectManagementFeature(base.BaseFeature):
+  ...     zope.interface.implements(IProjectManagementSchema)
+  ...
+  ...     featureTitle = u'Project Management'
+  ...     featureDocumentation = (
+  ...         u'Adds a CHANGES.txt, TODO.txt, and AUTHORS.txt to the '
+  ...         u'project root.')
+  ...
+  ...     includeHeaders = False
+  ...
+  ...     def _applyTo(self, context):
+  ...         for name in (u'TODO.txt', u'CHANGES.txt', u'AUTHORS.txt'):
+  ...             title = name[:-4] if self.includeHeaders else ''
+  ...             builder = HeaderFileBuilder(name, title)
+  ...             context.add(builder)
+
+Everything is pretty straightforward. The only part to look out for is that
+instead of implementing ``applyTo()``, you should implement the ``_applyTo()``
+method. Let's now try it out.
+
+  >>> pm = ProjectManagementFeature()
+  >>> pm
+  <ProjectManagementFeature u'Project Management'>
+  >>> pm.featureTitle
+  u'Project Management'
+  >>> pm.featureDocumentation
+  u'Adds a CHANGES.txt, TODO.txt, and AUTHORS.txt to the project root.'
+
+Now we are ready to apply the feature on the project.
+
+  >>> pm.update()
+  >>> pm.applyTo(prj)
+
+  >>> sorted(prj)
+  [u'AUTHORS.txt', u'CHANGES.txt', u'TODO.txt']
+
+Since we did not set the `includeHeader` flag, all files should be empty:
+
+  >>> prj['AUTHORS.txt'].render()
+  ''
+
+Let's now try with the flag turned on:
+
+  >>> pm.includeHeaders = True
+  >>> prj = project.ProjectBuilder(u'test')
+
+  >>> pm.update()
+  >>> pm.applyTo(prj)
+
+  >>> print prj['AUTHORS.txt'].render()
+  =======
+  AUTHORS
+  =======
+
+Let's also look at XML serialization again. First the serialization to XML:
+
+  >>> dist.addEntryPoint(
+  ...   interfaces.FEATURE_GROUP, 'ProjectManagement', ProjectManagementFeature)
+
+  >>> print pm.toXML(True, True)
+  <feature type="z3c.feature.testing:ProjectManagement">
+    <includeHeaders>True</includeHeaders>
+  </feature>
+
+Let's now take that output and generate the feature again:
+
+  >>> pm2 = ProjectManagementFeature.fromXML('''\
+  ... <feature type="z3c.feature.testing:ProjectManagement">
+  ...   <includeHeaders>True</includeHeaders>
+  ... </feature>
+  ... ''')
+  >>> pm2
+  <ProjectManagementFeature u'Project Management'>
+  >>> pm2.includeHeaders
+  True
+
+  >>> pm2 = ProjectManagementFeature.fromXML('''\
+  ... <feature type="z3c.feature.testing:ProjectManagement">
+  ...   <includeHeaders>False</includeHeaders>
+  ... </feature>
+  ... ''')
+  >>> pm2.includeHeaders
+  False
+
+And that's it! Let's now talk about some advanced features.
+
+
+Project Singletons
+------------------
+
+Features can be used in two ways: singletons and multiples. A singleton
+feature instance can only be applied once to the project. Examples include
+the meta-data, project setup, documentation features. Multiples, on the other
+hand, can have multiple instances applied to a project. Good examples include
+interface, class and HTML page features. By default, the singleton flag is
+set.
+
+  >>> pm.featureSingleton
+  True
+
+
+Dependencies
+------------
+
+In some cases, features depend on other features. Those dependencies come in
+two flavors:
+
+(1) A particular feature must be applied before another feature can be
+    applied.
+
+(2) A particular feature needs to access information of another feature.
+
+Dependencies are specified as part of the feature and are referenced by entry
+point name.
+
+Let's first look at case (1). As an example, we create a feature that creates
+a directory and one that puts a file in that directory.
+
+  >>> from z3c.builder.core.base import DirectoryBuilder
+  >>> class DocsDirectoryFeature(base.BaseFeature):
+  ...     name = u'docs'
+  ...     def _applyTo(self, context):
+  ...         builder = DirectoryBuilder(self.name)
+  ...         context.add(builder)
+  >>> docs = DocsDirectoryFeature()
+
+  >>> dist.addEntryPoint(
+  ...   interfaces.FEATURE_GROUP, 'DocsDirectoryFeature', DocsDirectoryFeature)
+
+  >>> class IndexFileFeature(base.BaseFeature):
+  ...     featureDependencies = ('DocsDirectoryFeature',)
+  ...
+  ...     def _applyTo(self, context):
+  ...         builder = HeaderFileBuilder(u'index.rst', u'Index')
+  ...         context['docs'].add(builder)
+  >>> index = IndexFileFeature()
+
+Let's now apply the two features to a newly created project:
+
+  >>> prj = project.ProjectBuilder(u'test')
+  >>> base.applyFeatures([index, docs], prj)
+
+  >>> prj.appliedFeatures
+  [<DocsDirectoryFeature 'DocsDirectoryFeature'>,
+   <IndexFileFeature 'IndexFileFeature'>]
+
+  >>> sorted(prj)
+  [u'docs']
+  >>> sorted(prj['docs'])
+  [u'index.rst']
+
+It worked. Note that the applied features list even provides the correct order
+in which the features were applied.
+
+Let's now look at case (2). Here the file builder simply wants to access the
+information of the directory builder. In this case dependency resolution is
+not necessary, though having it does not provide a problem either.
+
+To access a feature, we need to implement the `update()` method. So in this
+case, we would like to reuse the name of the docs directory feature.
+
+  >>> class IndexFileFeature(base.BaseFeature):
+  ...     featureDependencies = ('DocsDirectoryFeature',)
+  ...
+  ...     def update(self, features):
+  ...         self.dirName = features['DocsDirectoryFeature'].name
+  ...
+  ...     def _applyTo(self, context):
+  ...         builder = HeaderFileBuilder(
+  ...             u'index.rst', u'Index for `%s`' %self.dirName)
+  ...         context.add(builder)
+  >>> index = IndexFileFeature()
+
+Let's now apply the two features to a newly created project:
+
+  >>> prj = project.ProjectBuilder(u'test')
+  >>> base.applyFeatures([index, docs], prj)
+
+  >>> prj.appliedFeatures
+  [<DocsDirectoryFeature 'DocsDirectoryFeature'>,
+   <IndexFileFeature 'IndexFileFeature'>]
+
+  >>> sorted(prj)
+  [u'docs', u'index.rst']
+  >>> print prj['index.rst'].render()
+  ================
+  Index for `docs`
+  ================
+
+
+Enforcing Uniqueness by Feature Type
+------------------------------------
+
+It is sometimes necessary to enforce that only one feature of a particular
+*type* of feature is applied. For example, it makes little sense to apply a
+Google App Engine and Zope 3 project feature in the same run.
+
+You can get a list of all feature types using a simple function call:
+
+  >>> single = base.BaseFeature()
+  >>> base.getFeatureTypes(single)
+  []
+
+So our little single feature does not have any types. Let's now create a
+feature type:
+
+  >>> class ISingle(zope.interface.Interface):
+  ...     pass
+  >>> zope.interface.alsoProvides(ISingle, interfaces.IFeatureType)
+
+Once the new type is applied to the single feature, it is found via the query:
+
+  >>> zope.interface.alsoProvides(single, ISingle)
+  >>> base.getFeatureTypes(single)
+  [<InterfaceClass __builtin__.ISingle>]
+
+Let's new create a second feature:
+
+  >>> double = base.BaseFeature()
+  >>> zope.interface.alsoProvides(double, ISingle)
+
+Since both features support the `ISingle` feature type, they cannot be applied
+to the sam eproject:
+
+  >>> prj = project.ProjectBuilder(u'test')
+  >>> base.applyFeatures([single, double], prj)
+  Traceback (most recent call last):
+  ...
+  DuplicateFeatureConflictError: <InterfaceClass __builtin__.ISingle>
+
+However, when the features are not singletons, they can be applied together:
+
+  >>> single.featureSingleton = False
+  >>> double.featureSingleton = False
+
+  >>> base.applyFeatures([single, double], prj)
+  >>> prj.appliedFeatures
+  [<BaseFeature 'BaseFeature'>, <BaseFeature 'BaseFeature'>]
+
+And that's it.


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/base.txt
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.feature.core/trunk/src/z3c/feature/core/command-line-project.xml
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/command-line-project.xml	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/command-line-project.xml	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,17 @@
+<project name="helloworld">
+  <feature type="z3c.feature.core:meta-data">
+    <author>?</author>
+    <author-email>?</author-email>
+    <description>?</description>
+    <version>?</version>
+    <license>?</license>
+    <url>?</url>
+    <keywords>?</keywords>
+    <namespace-packages>?</namespace-packages>
+    <install-requires>?</install-requires>
+  </feature>
+  <feature type="z3c.feature.core:python-interpreter" />
+  <feature type="z3c.feature.core:unit-testing" />
+  <feature type="z3c.feature.core:script" />
+  <feature type="z3c.feature.core:documentation" />
+</project>


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/command-line-project.xml
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.feature.core/trunk/src/z3c/feature/core/example.txt
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/example.txt	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/example.txt	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,312 @@
+============================
+XML Configuration Processing
+============================
+
+We have an xml configuration layer for the project builder.
+
+  >>> from z3c.feature.core import xml
+
+Let's start by defining the simplest possible project definition in
+xml:
+
+  >>> hw = xml.xmlToProject('<project name="HelloWorld"/>')
+
+This provides you with a full project using default settings, once
+you've run the update method.
+
+  >>> hw.setup.update()
+  >>> hw.name
+  u'HelloWorld'
+  >>> hw.setup.version
+  u'0.1.0'
+  >>> hw.setup.license
+  u'GPLv3'
+  >>> hw.setup.url
+  u'http://pypi.python.org/pypi/HelloWorld'
+  >>> hw.setup.keywords
+  []
+  >>> hw.setup.author
+  u''
+  >>> hw.setup.author_email
+  u''
+  >>> hw.setup.description
+  u''
+  >>> print hw.commentHeader
+  ##############################################################################
+  #
+  # This file is part of %(name)s.
+  #
+  # %(name)s is free software: you can redistribute it and/or modify
+  # it under the terms of the GNU General Public License as published by
+  # the Free Software Foundation, either version 3 of the License, or
+  # (at your option) any later version.
+  #
+  # %(name)s is distributed in the hope that it will be useful,
+  # but WITHOUT ANY WARRANTY; without even the implied warranty of
+  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  # GNU General Public License for more details.
+  #
+  # You should have received a copy of the GNU General Public License
+  # along with %(name)s.  If not, see <http://www.gnu.org/licenses/>.
+  #
+  ##############################################################################
+
+
+Project Metadata
+----------------
+
+We can customize this project by adding some features.  The simplest
+feature is the meta-data feature which lets you define some typical
+project meta-data:
+
+  >>> hw = xml.xmlToProject('''
+  ... <project name="HelloWorld">
+  ...   <feature type="z3c.feature.core:meta-data">
+  ...     <license>Zope Public License</license>
+  ...     <version>0.5.0</version>
+  ...     <author>Paul Carduner and Stephan Richter</author>
+  ...     <author-email>zope-dev at zope.org</author-email>
+  ...     <description>A simple Zope 3 Application</description>
+  ...     <license>ZPL 2.1</license>
+  ...     <keywords>
+  ...       <item>zope3</item>
+  ...       <item>HelloWorld</item>
+  ...       <item>project</item>
+  ...     </keywords>
+  ...     <url>http://svn.zope.org/HelloWorld/trunk</url>
+  ...     <commentHeader># Copyright 2009 Cool People.</commentHeader>
+  ...   </feature>
+  ... </project>''')
+
+  >>> hw.name
+  u'HelloWorld'
+  >>> hw.setup.version
+  u'0.5.0'
+  >>> hw.setup.license
+  u'Zope Public License'
+  >>> hw.setup.url
+  u'http://svn.zope.org/HelloWorld/trunk'
+  >>> hw.setup.keywords
+  [u'zope3', u'HelloWorld', u'project']
+  >>> hw.setup.author
+  u'Paul Carduner and Stephan Richter'
+  >>> hw.setup.author_email
+  u'zope-dev at zope.org'
+  >>> hw.setup.description
+  u'A simple Zope 3 Application'
+  >>> hw.commentHeader
+  u'# Copyright 2009 Cool People.'
+
+We can also use a custom comment header feature.  Since it apepars
+after the meta data feature, it can overwrite anything done by the
+meta data feature.
+
+  >>> hw = xml.xmlToProject('''
+  ... <project name="HelloWorld">
+  ...   <feature type="z3c.feature.core:meta-data" />
+  ...   <feature type="z3c.feature.core:comment-header-ZPL">
+  ...     <year>2009</year>
+  ...     <author>Paul Carduner</author>
+  ...   </feature>
+  ... </project>''')
+
+  >>> hw
+  <BuildoutProjectBuilder u'HelloWorld'>
+
+  >>> print hw.commentHeader
+  ##############################################################################
+  #
+  # Copyright (c) 2009 Paul Carduner.
+  # All Rights Reserved.
+  #
+  # This software is subject to the provisions of the Zope Public License,
+  # Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+  # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+  # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+  # FOR A PARTICULAR PURPOSE.
+  #
+  ##############################################################################
+
+Here is another example of a nest feature that adds a proprietary
+copyright header.
+
+  >>> hw = xml.xmlToProject('''
+  ... <project name="HelloWorld">
+  ...   <feature type="z3c.feature.core:proprietary-header">
+  ...     <year>2009</year>
+  ...     <company>Awesomeness LLC.</company>
+  ...     <location>Heaven on Earth</location>
+  ...   </feature>
+  ... </project>''')
+  >>> print hw.commentHeader
+  ###############################################################################
+  #
+  # Copyright 2009 by Awesomeness LLC., Heaven on Earth
+  #
+  ###############################################################################
+
+
+Buildout
+--------
+
+We can also configure buildout related features that go beyond the
+defaults.  Let's first look at the defaults.
+
+  >>> hw = xml.xmlToProject('<project name="HelloWorld"/>')
+  >>> hw.buildout
+  <BuildoutConfigBuilder for u'HelloWorld'>
+  >>> hw.buildout.extends
+  [u'http://download.zope.org/zope3.4/3.4.0/versions.cfg']
+
+Lets add a python interpreter feature
+
+  >>> hw = xml.xmlToProject('''
+  ... <project name="HelloWorld">
+  ...   <feature type="z3c.feature.core:python-interpreter"/>
+  ... </project>''')
+
+  >>> hw.buildout
+  <BuildoutConfigBuilder for u'HelloWorld'>
+
+
+Testing
+-------
+
+Let's add some unit testing features:
+
+  >>> hw = xml.xmlToProject('''
+  ... <project name="HelloWorld">
+  ...   <feature type="z3c.feature.core:unit-testing"/>
+  ... </project>''')
+
+  >>> hw.update()
+  >>> hw.buildout.names
+  [u'test', u'coverage-test', u'coverage-report']
+
+  >>> hw.buildout['test']
+  <PartBuilder u'test'>
+
+  >>> hw.buildout['coverage-test']
+  <PartBuilder u'coverage-test'>
+
+  >>> hw.buildout['coverage-report']
+  <PartBuilder u'coverage-report'>
+
+  >>> hw.setup.extras_requires
+  {'test': ['z3c.coverage', 'zope.testing']}
+
+  >>> hw.package['tests']['test_doc.py']
+  <SimpleFileBuilder u'test_doc.py'>
+
+  >>> hw.package['README.txt']
+  <SimpleFileBuilder u'README.txt'>
+
+
+Python Shell Script
+-------------------
+
+Lets add a python shell script feature:
+
+  >>> hw = xml.xmlToProject('''
+  ... <project name="HelloWorld">
+  ...   <feature type="z3c.feature.core:script"/>
+  ... </project>''')
+
+  >>> hw.update()
+  >>> hw.setup.entry_points
+  {'console_scripts': [u'HelloWorld = HelloWorld.script:main']}
+  >>> hw.package['script.py']
+  <ModuleBuilder u'script.py'>
+
+  >>> print hw.package['script.py'].render()
+  ##############################################################################
+  #
+  # This file is part of HelloWorld....
+  #
+  ##############################################################################
+  """Module Documentation"""
+  <BLANKLINE>
+  def main(args=None):
+      """Runs the HelloWorld script from the HelloWorld project"""
+      print "Successfully ran the HelloWorld script"
+
+
+Documentation Generation
+------------------------
+
+Lets add some documentation generation.
+
+  >>> hw = xml.xmlToProject('''
+  ... <project name="HelloWorld">
+  ...   <feature type="z3c.feature.core:documentation"/>
+  ... </project>''')
+
+  >>> hw.update()
+
+We now have a docs buildout section.
+
+  >>> print hw.buildout['docs'].render()
+  [docs]
+  recipe = z3c.recipe.sphinxdoc
+  eggs = z3c.recipe.sphinxdoc
+         HelloWorld [docs]
+  layout.html =
+  default.css =
+
+and a docs extras_require section:
+
+  >>> print hw.setup.extras_requires
+  {'docs': ['Sphinx']}
+
+and an index.txt template file:
+
+  >>> hw.package['index.txt']
+  <DocumentationFileBuilder u'index.txt'>
+
+  >>> print hw.package['index.txt'].render()
+  ========================
+  HelloWorld Documentation
+  ========================
+  <BLANKLINE>
+  Contents:
+  <BLANKLINE>
+  .. toctree::
+     :maxdepth: 2
+  <BLANKLINE>
+  Indices and tables
+  ==================
+  <BLANKLINE>
+  * :ref:`genindex`
+  * :ref:`modindex`
+  * :ref:`search`
+
+If we do this in combination with other builders that produce *.txt
+files (like the doctest builder), then those get added to the
+contents.
+
+  >>> hw = xml.xmlToProject('''
+  ... <project name="HelloWorld">
+  ...   <feature type="z3c.feature.core:documentation"/>
+  ...   <feature type="z3c.feature.core:unit-testing"/>
+  ... </project>''')
+
+  >>> hw.update()
+  >>> print hw.package['index.txt'].render()
+  ========================
+  HelloWorld Documentation
+  ========================
+  <BLANKLINE>
+  Contents:
+  <BLANKLINE>
+  .. toctree::
+     :maxdepth: 2
+     README
+  <BLANKLINE>
+  Indices and tables
+  ==================
+  <BLANKLINE>
+  * :ref:`genindex`
+  * :ref:`modindex`
+  * :ref:`search`
+  <BLANKLINE>


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/example.txt
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.feature.core/trunk/src/z3c/feature/core/file-templates/README.txt
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/file-templates/README.txt	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/file-templates/README.txt	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,14 @@
+================
+A Simple DocTest
+================
+
+Doctests work by evaluating txt documents that look like python
+interpreter sessions.  This test will pass:
+
+  >>> 1+1
+  2
+
+The following test will not pass (that's why it is commented out):
+
+#  >>> print 'apples and bananas'
+#  oranges
\ No newline at end of file


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/file-templates/README.txt
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.feature.core/trunk/src/z3c/feature/core/file-templates/index-template.txt
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/file-templates/index-template.txt	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/file-templates/index-template.txt	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,15 @@
+%(heading)s
+
+Contents:
+
+.. toctree::
+   :maxdepth: 2
+
+   %(TOC)s
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/file-templates/index-template.txt
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.feature.core/trunk/src/z3c/feature/core/file-templates/test_doc.py
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/file-templates/test_doc.py	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/file-templates/test_doc.py	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,13 @@
+import unittest
+import zope.testing.doctest
+
+def test_suite():
+    return unittest.TestSuite((
+
+        zope.testing.doctest.DocFileSuite(
+            '../README.txt',
+            optionflags=zope.testing.doctest.NORMALIZE_WHITESPACE |
+                        zope.testing.doctest.ELLIPSIS),
+
+
+        ))


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/file-templates/test_doc.py
___________________________________________________________________
Added: svn:keywords
   + Id

Added: z3c.feature.core/trunk/src/z3c/feature/core/index.txt
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/index.txt	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/index.txt	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,14 @@
+====================
+Core Boiler Features
+====================
+
+.. toctree::
+   :maxdepth: 2
+
+   README
+   base
+   metadata
+   python
+   unittest
+   xml
+   example


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/index.txt
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.feature.core/trunk/src/z3c/feature/core/interfaces.py
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/interfaces.py	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/interfaces.py	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,322 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Feature interfaces
+
+$Id$
+"""
+import datetime
+import os
+
+import zope.interface
+from zope.interface.interfaces import IInterface
+import zope.schema
+
+from z3c.builder.core.interfaces import troveClassiferVocabulary
+
+FEATURE_GROUP = 'z3c.feature'
+
+class IFeatureType(IInterface):
+    """An interface that describes a particular type of feature."""
+
+class DuplicateFeatureConflictError(ValueError):
+
+    def __init__(self, type):
+        self.type = type
+        ValueError.__init__(self, type)
+
+class CyclicDependencyError(ValueError):
+
+    def __init__(self, cycle):
+        self.cycle = cycle
+        ValueError.__init__(self, cycle)
+
+class MissingFeatureDependencyError(KeyError):
+    def __init__(self, feature, dependencyName):
+        self.feature = feature
+        self.dependencyName = dependencyName
+        KeyError.__init__(self, dependencyName)
+
+    def __str__(self):
+        return ('Feature "%s" depends on "%s" but no "%s" '
+                'feature was specified') % (self.feature.featureTitle,
+                                            self.dependencyName,
+                                            self.dependencyName)
+
+class IFeatureSchema(IInterface):
+    """A schema describing the configuration parameters of a feature."""
+
+class IBaseFeatureSchema(zope.interface.Interface):
+    """A trivial feature schema."""
+zope.interface.directlyProvides(IBaseFeatureSchema, IFeatureSchema)
+
+
+class IFeature(zope.interface.Interface):
+    """An object representing a possible feature."""
+
+    featureTitle = zope.schema.TextLine(
+        title=u"Feature Title",
+        description=u"A user readable title for this feature.")
+
+    featureDocumentation = zope.schema.Text(
+        title=u"Feature Documentation",
+        description=u"""ReSTructured text documentation on the feature.
+
+        This should explain what files were modified/created by the
+        feature and what each piece does, with tips on modifying them
+        later.  Documentation for all the features are combined into
+        a single README.txt in the project Root.
+        """)
+
+    featureSingleton = zope.schema.Bool(
+        title=u"Feature Sigelton",
+        description=(u"When set, only one instance of this feature can "
+                     u"be applied to the context."),
+        default=True)
+
+    featureDependencies = zope.schema.Tuple(
+        title=u"Feature Dependencies",
+        description=(u"A list of names of features that this feature "
+                     u"depends on."),
+        value_type=zope.schema.ASCIILine())
+
+    def update(features=()):
+        """Update the feature.
+
+        Optionally, all other features that are applied are passed into the
+        method as an argument.
+        """
+
+    def applyTo(context):
+        """Apply the given feature to the context."""
+
+    def findEntryPoint():
+        """Returns the egg name and entry point name.
+
+        If no entry point is found, `(None, None)` is returned.
+        """
+
+    def toXML(asString=False, prettyPrint=False):
+        """Returns an etree node object representing the feature in xml.
+
+        Optional keyword arguments include asString and prettyPrint
+        which if set to true will return an xml string that is pretty
+        printed.
+        """
+
+class IHaveAppliedFeatures(zope.interface.Interface):
+    """A component that keeps track of the features applied to it."""
+
+    appliedFeatures = zope.schema.List(
+        title=u'Applied Features',
+        description=u'A list of features that were applied on the builder.')
+
+
+class IProjectTemplateProvider(zope.interface.Interface):
+    """Object that provides a projecte template."""
+
+    title = zope.schema.TextLine(
+        title=u"Title",
+        description=u"A title that can be provided to the user.")
+
+    description = zope.schema.TextLine(
+        title=u"Description",
+        description=u"A description that can be provided to the user.")
+
+    def getFeatures():
+        """Returns a dictionary of entrypoint name to features that
+           are part of this template."""
+
+
+class IFileBasedTemplateProvider(IProjectTemplateProvider):
+
+    filename = zope.schema.TextLine(
+        title=u"Filename")
+
+
+class IMetaDataFeature(zope.interface.Interface):
+
+    license = zope.schema.TextLine(
+        title=u'License',
+        default=u'GNU General Public License (GPL)',
+        required=False)
+
+    version = zope.schema.TextLine(
+        title=u'Version',
+        description=u'An initial version number for the project',
+        default=u'0.1.0',
+        required=False)
+
+    description = zope.schema.TextLine(
+        title=u'Project Description',
+        description=u'Short Description of the project',
+        required=False)
+
+    author = zope.schema.TextLine(
+        title=u'Author(s)',
+        description=u'Name of the project author(s)',
+        required=False)
+
+    author_email = zope.schema.TextLine(
+        title=u'Author Email',
+        description=u'Email address for the project author(s)',
+        required=False)
+
+    keywords = zope.schema.List(
+        title=u'Keywords',
+        description=u'A list of keywords related to the project, one per line.',
+        value_type=zope.schema.TextLine(),
+        required=False)
+
+    url = zope.schema.TextLine(
+        title=u'URL',
+        description=(u'The url for the project. Defaults to '
+                     u'http://pypi.python.org/pypi/[project-name]'),
+        required=False)
+
+    classifiers = zope.schema.Set(
+        title=u"Trove Classifiers",
+        value_type=zope.schema.Choice(
+            vocabulary=troveClassiferVocabulary),
+        required=False)
+
+    commentHeader = zope.schema.Text(
+        title=u'Comment Header',
+        description=(u'A comment header that should appear at the top of every '
+                     u'python file (like a license header).'),
+        required=False)
+
+    namespace_packages = zope.schema.List(
+        title=u'Namespace Packages',
+        description=(u'A list of namespace packages that should be created, one '
+                     u'per line (i.e. zope or zc or z3c or collective)'),
+        value_type=zope.schema.TextLine(),
+        required=False)
+
+    install_requires = zope.schema.List(
+        title=u"Install Requires",
+        description=(u"A list of additional dependencies, one per line "
+                     u"(i.e. lxml)"),
+        value_type=zope.schema.TextLine(),
+        required=False)
+
+    extras_require = zope.schema.Dict(
+        title=u"Extras Require",
+        key_type=zope.schema.TextLine(),
+        value_type=zope.schema.List(value_type=zope.schema.TextLine()))
+
+zope.interface.directlyProvides(IMetaDataFeature, IFeatureSchema)
+
+
+class ICommentHeaderZPLFeature(zope.interface.Interface):
+
+    author = zope.schema.TextLine(
+        title=u'Author(s)',
+        description=u'Name of the project author(s)',
+        required=False)
+
+    year = zope.schema.Int(
+        title=u"Year",
+        description=u'The copyright year',
+        default=datetime.date.today().year)
+
+zope.interface.directlyProvides(ICommentHeaderZPLFeature, IFeatureSchema)
+
+
+class IProprietaryHeaderFeature(zope.interface.Interface):
+
+    year = zope.schema.Int(
+        title=u"Year",
+        description=u'The copyright year',
+        default=datetime.date.today().year)
+
+    company = zope.schema.TextLine(
+        title=u'Company',
+        description=u'The name of your company, (i.e. Foo Inc.)')
+
+    location = zope.schema.TextLine(
+        title=u'Location',
+        description=u'Location of your company (i.e. San Francisco, USA)')
+
+zope.interface.directlyProvides(IProprietaryHeaderFeature, IFeatureSchema)
+
+
+class IPythonInterpreterFeature(zope.interface.Interface):
+
+    executableName = zope.schema.ASCIILine(
+        title=u'Executable',
+        description=(u'The path to the python executable. '
+                     u'(i.e. /opt/python2.6/bin/python or just python)'),
+        default='python')
+
+zope.interface.directlyProvides(IPythonInterpreterFeature, IFeatureSchema)
+
+
+class ITestingFeature(zope.interface.Interface):
+
+    coverageDirectory = zope.schema.TextLine(
+        title=u"Coverage Directory",
+        description=u"Directory where test coverage data should be placed.",
+        default=u"${buildout:directory}/coverage")
+
+    coverageReportDirectory = zope.schema.TextLine(
+        title=u'Coverage Report Directory',
+        description=u'Directory where coverage reports should be generated',
+        default=u'${buildout:directory}/coverage/report')
+
+zope.interface.directlyProvides(ITestingFeature, IFeatureSchema)
+
+
+class IScriptFeature(zope.interface.Interface):
+
+    scriptName = zope.schema.TextLine(
+        title=u'Script Name',
+        description=(u'The name of the script that will '
+                     u'be made available on the command line. '
+                     u'Defaults to the name of the project.'),
+        required=False)
+
+    scriptFile = zope.schema.TextLine(
+        title=u'Script File',
+        description=u'The file where the script will be located',
+        default=u"script.py")
+
+    scriptFunction = zope.schema.TextLine(
+        title=u'Script Function',
+        description=u'The function in the script file that runs the script',
+        default=u"main")
+
+zope.interface.directlyProvides(IScriptFeature, IFeatureSchema)
+
+
+class IDocumentationFeature(zope.interface.Interface):
+
+    layoutTemplatePath = zope.schema.URI(
+        title=u'Layout Template',
+        description=u"URL for a layout template.  Leave blank for default",
+        required=False)
+
+    cssPath = zope.schema.URI(
+        title=u'CSS url',
+        description=(u'URL for a css file that should be used.  Leave blank '
+                     u'for default.'),
+        required=False)
+
+    additionalEggs = zope.schema.List(
+        title=u'Additional Packages',
+        description=(u'Additional packages for which documentation should '
+                     u'be built'),
+        value_type=zope.schema.TextLine(),
+        required=False)
+
+zope.interface.directlyProvides(IDocumentationFeature, IFeatureSchema)


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/interfaces.py
___________________________________________________________________
Added: svn:keywords
   + Id

Added: z3c.feature.core/trunk/src/z3c/feature/core/metadata.py
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/metadata.py	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/metadata.py	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,245 @@
+##############################################################################
+#
+# Copyright (c) 2009 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Meta Data Feature
+
+$Id$
+"""
+import os
+import types
+import zope.interface
+from zope.schema.fieldproperty import FieldProperty
+
+from z3c.builder.core import project, buildout
+from z3c.builder.core.base import getTemplatePath, FileBuilder
+from z3c.builder.core.interfaces import IFileBuilder
+from z3c.feature.core import base, interfaces, xml
+
+
+METADATA_FEATURE_DOCUMENTATION = """
+The Metadata feature sets up the setup.py file which is what
+setuptools uses to generate distributable python eggs.
+"""
+
+COMMENT_HEADER_ZPL_DOCUMENTATION = """
+The Zope Public License file header looks something like this::
+
+%(header)s
+"""
+
+PROPRIETARY_HEADER_DOCUMENTATION = """
+The proprietary header looks something like this::
+
+%(header)s
+"""
+
+DOCUMENTATION_FEATURE_DOCUMENTATION = """
+The ReSTructured Text Documentation feature hooks up scripts
+for generating html (or latex for that matter) documentation
+from ReSTructured text files using Sphinx.  There are a few
+pieces involved in this hookup:
+
+  #. ``buildout.cfg`` **part section**
+
+       This looks something like::
+
+         [docs]
+         recipe = z3c.recipe.sphinxdoc
+         eggs = yourproject [docs]
+                z3c.recipe.sphinxdoc
+         default.css =
+         layout.html =
+
+       This buildout part section will generate a script in the
+       bin directory called ``docs`` which you can run liket his::
+
+         $ ./bin/docs
+
+       Documentation will be put into the ``parts/docs/`` directory, with
+       one directory for each package specified in the eggs parameter of
+       the docs section.  See the documentation for z3c.recipe.sphinxdoc
+       for more information.  It can be found at
+
+         http://pypi.python.org/pypi/z3c.recipe.sphinxdoc
+
+  #. ``setup.py extras_require`` **section**
+
+       For the docs to build correctly, there must be a ``docs`` section in
+       ``extras_require`` that pulls in the Sphinx dependencies.
+
+  #. ``index.txt`` **file in project src**
+
+       An ``index.txt`` file will be added to the src directory.  This
+       serves as the root document used by Sphinx to generate all the
+       documentation.  See the Sphinx documentation for what sort of things
+       you can put in here at
+
+         http://sphinx.pocoo.org/
+"""
+
+class MetaDataFeature(base.BaseFeature):
+    zope.interface.implements(interfaces.IMetaDataFeature)
+
+    version = FieldProperty(interfaces.IMetaDataFeature['version'])
+    license = FieldProperty(interfaces.IMetaDataFeature['license'])
+    url = FieldProperty(interfaces.IMetaDataFeature['url'])
+    classifiers = FieldProperty(interfaces.IMetaDataFeature['classifiers'])
+    keywords = FieldProperty(interfaces.IMetaDataFeature['keywords'])
+    author = FieldProperty(interfaces.IMetaDataFeature['author'])
+    author_email = FieldProperty(interfaces.IMetaDataFeature['author_email'])
+    description = FieldProperty(interfaces.IMetaDataFeature['description'])
+    commentHeader = FieldProperty(interfaces.IMetaDataFeature['commentHeader'])
+    namespace_packages = FieldProperty(
+        interfaces.IMetaDataFeature['namespace_packages'])
+    install_requires = FieldProperty(
+        interfaces.IMetaDataFeature['install_requires'])
+    extras_require = FieldProperty(interfaces.IMetaDataFeature['extras_require'])
+
+    featureTitle = u'Metadata'
+    featureDocumentation = METADATA_FEATURE_DOCUMENTATION
+
+    @classmethod
+    def fromXMLNode(cls, node, omit=()):
+        feature = super(MetaDataFeature, cls).fromXMLNode(
+            node,
+            omit=('classifiers', 'extras_require'))
+
+        schema = base.getFeatureSchema(feature)
+        data = xml.extractData(node, schema)
+        classifiers = data.get('classifiers')
+
+        feature.classifiers = set()
+        if classifiers:
+            feature.classifiers = [c.strip() for c in classifiers.split('\n')]
+        return feature
+
+    def _applyTo(self, context):
+        """See ``z3c.feature.core.interfaces.IFeature``"""
+        # Assign all meta data to setup.
+        for name in ('version', 'license', 'url', 'keywords', 'author',
+                     'author_email', 'description', 'namespace_packages',
+                     'install_requires','extras_require'):
+            value = getattr(self, name)
+            if value is not None:
+                setattr(context.setup, name, value)
+        # Set the comment header.
+        if self.commentHeader is not None:
+            context.commentHeader = self.commentHeader
+
+
+class CommentHeaderZPLFeature(base.BaseFeature):
+    zope.interface.implements(interfaces.ICommentHeaderZPLFeature)
+
+    _template = unicode(open(getTemplatePath('zpl-header.py')).read())
+
+    year = FieldProperty(interfaces.ICommentHeaderZPLFeature['year'])
+    author = FieldProperty(interfaces.ICommentHeaderZPLFeature['author'])
+
+    featureTitle = u'ZPL Header'
+
+    @property
+    def featureDocumentation(self):
+        """See ``z3c.feature.core.interfaces.IFeature``"""
+        header = '  ' + self.renderCommentHeader().replace('\n', '\n  ')
+        return COMMENT_HEADER_ZPL_DOCUMENTATION % {'header':header}
+
+    def renderCommentHeader(self):
+        return self._template % dict(year=self.year,
+                                     author=self.author)
+
+    def _applyTo(self, context):
+        """See ``z3c.feature.core.interfaces.IFeature``"""
+        context.commentHeader = self.renderCommentHeader()
+
+
+class ProprietaryHeaderFeature(base.BaseFeature):
+    zope.interface.implements(interfaces.IProprietaryHeaderFeature)
+
+    _template = unicode(open(getTemplatePath('proprietary-header.py')).read())
+
+    year = FieldProperty(interfaces.IProprietaryHeaderFeature['year'])
+    company = FieldProperty(interfaces.IProprietaryHeaderFeature['company'])
+    location = FieldProperty(interfaces.IProprietaryHeaderFeature['location'])
+
+    featureTitle = u'Proprietary Header'
+
+    @property
+    def featureDocumentation(self):
+        """See ``z3c.feature.core.interfaces.IFeature``"""
+        header = '  ' + self.renderCommentHeader().replace('\n', '\n  ')
+        return PROPRIETARY_HEADER_DOCUMENTATION % {'header':header}
+
+    def renderCommentHeader(self):
+        return self._template % dict(year=self.year,
+                                     company=self.company,
+                                     location=self.location)
+
+    def _applyTo(self, context):
+        """See ``z3c.feature.core.interfaces.IFeature``"""
+        context.commentHeader = self.renderCommentHeader()
+
+
+class DocumentationFileBuilder(FileBuilder):
+
+    template = os.path.join(
+        os.path.dirname(__file__), 'file-templates', 'index-template.txt')
+
+    def update(self):
+        project = self.getProject()
+        self.title = project.name + ' Documentation'
+        self.TOC = []
+        for builder in self.__parent__.values():
+            if (IFileBuilder.providedBy(builder) and
+                builder.filename.endswith('.txt') and
+                builder is not self):
+
+                self.TOC.append(builder.filename[:-4])
+
+    def render(self):
+        heading = '%(border)s\n%(title)s\n%(border)s' % dict(
+            border='='*len(self.title),
+            title=self.title)
+        TOC = '\n   '.join(self.TOC)
+        return open(self.template).read() % dict(heading=heading,
+                                                 TOC=TOC)
+
+class DocumentationFeature(base.BaseFeature):
+    zope.interface.implements(interfaces.IDocumentationFeature)
+
+    layoutTemplatePath = FieldProperty(
+        interfaces.IDocumentationFeature['layoutTemplatePath'])
+    cssPath = FieldProperty(
+        interfaces.IDocumentationFeature['cssPath'])
+    additionalEggs = FieldProperty(
+        interfaces.IDocumentationFeature['additionalEggs'])
+
+    featureTitle = u'Restructured Text Documentation'
+    featureDocumentation = DOCUMENTATION_FEATURE_DOCUMENTATION
+
+    def _applyTo(self, context):
+        """See ``z3c.feature.core.interfaces.IFeature``"""
+        docsPart = buildout.PartBuilder(u'docs')
+        docsPart.addValue('recipe', 'z3c.recipe.sphinxdoc')
+        eggs = [context.name+' [docs]'] + (self.additionalEggs or [])
+        eggsString = 'z3c.recipe.sphinxdoc'
+        for egg in eggs:
+            eggsString += '\n       '+egg
+        docsPart.addValue('eggs', eggsString)
+        docsPart.addValue('layout.html', self.layoutTemplatePath or '')
+        docsPart.addValue('default.css', self.cssPath or '')
+        context.buildout.add(docsPart)
+
+        context.setup.addExtrasRequires('docs', ['Sphinx'])
+
+        indexBuilder = DocumentationFileBuilder(u'index.txt')
+        context.package.add(indexBuilder)


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/metadata.py
___________________________________________________________________
Added: svn:keywords
   + Id

Added: z3c.feature.core/trunk/src/z3c/feature/core/metadata.txt
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/metadata.txt	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/metadata.txt	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,393 @@
+=================
+Metadata Features
+=================
+
+The `metadata` module provides several features related to the overall setup
+of a package.
+
+  >>> from z3c.feature.core import metadata, interfaces
+
+Meta-Data Feature
+-----------------
+
+The meta-data feature maintains all attributes that are needed for the
+`setup.py` file.
+
+  >>> feature = metadata.MetaDataFeature()
+  >>> feature
+  <MetaDataFeature u'Metadata'>
+
+  >>> feature.version = u'0.0.1'
+  >>> feature.author = u'Paul Carduner'
+  >>> feature.keywords = [u'test', u'some', u'more']
+
+Let's verify that both, the `IFeature` and `IMetaDataFeature` interface, are
+provided:
+
+  >>> from zope.interface.verify import verifyObject
+  >>> verifyObject(interfaces.IFeature, feature)
+  True
+  >>> verifyObject(interfaces.IMetaDataFeature, feature)
+  True
+
+The feature schema is:
+
+  >>> from z3c.feature.core import base
+  >>> base.getFeatureSchema(feature)
+  <InterfaceClass z3c.feature.core.interfaces.IMetaDataFeature>
+
+Good documentation is also provided:
+
+  >>> print feature.featureTitle
+  Metadata
+  >>> print feature.featureDocumentation
+  The Metadata feature sets up the setup.py file which is what
+  setuptools uses to generate distributable python eggs.
+
+This is a singleton feature:
+
+  >>> feature.featureSingleton
+  True
+
+Serialization to XML works:
+
+  >>> print feature.toXML(True, True)
+  <feature type="z3c.feature.core:meta-data">
+    <author>Paul Carduner</author>
+    <version>0.0.1</version>
+    <keywords>
+      <item>test</item>
+      <item>some</item>
+      <item>more</item>
+    </keywords>
+  </feature>
+
+Deserialization from XML works:
+
+  >>> feature = metadata.MetaDataFeature.fromXML('''\
+  ...     <feature type="z3c.feature.core:meta-data">
+  ...       <author>Paul Carduner</author>
+  ...       <version>0.0.1</version>
+  ...       <keywords>
+  ...         <item>test</item>
+  ...         <item>some</item>
+  ...         <item>more</item>
+  ...       </keywords>
+  ...     </feature>
+  ... ''')
+  >>> feature
+  <MetaDataFeature u'Metadata'>
+
+  >>> feature.author
+  u'Paul Carduner'
+  >>> feature.version
+  u'0.0.1'
+  >>> feature.keywords
+  [u'test', u'some', u'more']
+
+Let's now apply the feature to a project:
+
+  >>> from z3c.builder.core import project
+  >>> prj = project.BuildoutProjectBuilder(u'test')
+
+  >>> feature.update()
+  >>> feature.applyTo(prj)
+
+And the data is applied:
+
+  >>> prj.setup.author
+  u'Paul Carduner'
+  >>> prj.setup.version
+  u'0.0.1'
+  >>> prj.setup.keywords
+  [u'test', u'some', u'more']
+
+Okay, we applied the feature successfully by updating the necessary meta-data.
+
+
+ZPL Comment Header
+------------------
+
+This feature sets the comment header for a given project. That header will be
+applied to all Python code files.
+
+  >>> feature = metadata.CommentHeaderZPLFeature()
+  >>> feature
+  <CommentHeaderZPLFeature u'ZPL Header'>
+
+  >>> feature.year = 2008
+  >>> feature.author = u'Paul Carduner'
+
+Let's verify that both, the `IFeature` and `ICommentHeaderZPLFeature`
+interface, are provided:
+
+  >>> verifyObject(interfaces.IFeature, feature)
+  True
+  >>> verifyObject(interfaces.ICommentHeaderZPLFeature, feature)
+  True
+
+The feature schema is:
+
+  >>> base.getFeatureSchema(feature)
+  <InterfaceClass z3c.feature.core.interfaces.ICommentHeaderZPLFeature>
+
+Good documentation is also provided:
+
+  >>> print feature.featureTitle
+  ZPL Header
+  >>> print feature.featureDocumentation
+  <BLANKLINE>
+  The Zope Public License file header looks something like this::
+  <BLANKLINE>
+    ##############################################################################
+    #
+    # Copyright (c) 2008 Paul Carduner.
+    # All Rights Reserved.
+    #
+    # This software is subject to the provisions of the Zope Public License,
+    # Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+    # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+    # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+    # FOR A PARTICULAR PURPOSE.
+    #
+    ##############################################################################
+
+This is a singleton feature:
+
+  >>> feature.featureSingleton
+  True
+
+Serialization to XML works:
+
+  >>> print feature.toXML(True, True)
+  <feature type="z3c.feature.core:comment-header-ZPL">
+    <year>2008</year>
+    <author>Paul Carduner</author>
+  </feature>
+
+Deserialization from XML works:
+
+  >>> feature = metadata.CommentHeaderZPLFeature.fromXML('''\
+  ...     <feature type="z3c.feature.core:comment-header-ZPL">
+  ...       <year>2008</year>
+  ...       <author>Paul Carduner</author>
+  ...     </feature>
+  ... ''')
+  >>> feature
+  <CommentHeaderZPLFeature u'ZPL Header'>
+
+  >>> feature.author
+  u'Paul Carduner'
+  >>> feature.year
+  2008
+
+Let's now apply the feature to a project:
+
+  >>> from z3c.builder.core import project
+  >>> prj = project.BuildoutProjectBuilder(u'test')
+
+  >>> feature.update()
+  >>> feature.applyTo(prj)
+
+And the data is applied:
+
+  >>> print prj.commentHeader
+  ##############################################################################
+  #
+  # Copyright (c) 2008 Paul Carduner.
+  # All Rights Reserved.
+  #
+  # This software is subject to the provisions of the Zope Public License,
+  # Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+  # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+  # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+  # FOR A PARTICULAR PURPOSE.
+  #
+  ##############################################################################
+
+
+Proprietary Comment Header
+--------------------------
+
+This feature sets the proprietary comment header for a given project. That
+header will be applied to all Python code files.
+
+  >>> feature = metadata.ProprietaryHeaderFeature()
+  >>> feature
+  <ProprietaryHeaderFeature u'Proprietary Header'>
+
+  >>> feature.year = 2008
+  >>> feature.company = u'Paulo, Inc.'
+  >>> feature.location = u'Berkeley, CA, USA'
+
+Let's verify that both, the `IFeature` and `IProprietaryHeaderFeature`
+interface, are provided:
+
+  >>> verifyObject(interfaces.IFeature, feature)
+  True
+  >>> verifyObject(interfaces.IProprietaryHeaderFeature, feature)
+  True
+
+The feature schema is:
+
+  >>> base.getFeatureSchema(feature)
+  <InterfaceClass z3c.feature.core.interfaces.IProprietaryHeaderFeature>
+
+Good documentation is also provided:
+
+  >>> print feature.featureTitle
+  Proprietary Header
+  >>> print feature.featureDocumentation
+  <BLANKLINE>
+  The proprietary header looks something like this::
+  <BLANKLINE>
+    ###############################################################################
+    #
+    # Copyright 2008 by Paulo, Inc., Berkeley, CA, USA
+    #
+    ###############################################################################
+
+This is a singleton feature:
+
+  >>> feature.featureSingleton
+  True
+
+Serialization to XML works:
+
+  >>> print feature.toXML(True, True)
+  <feature type="z3c.feature.core:proprietary-header">
+    <company>Paulo, Inc.</company>
+    <location>Berkeley, CA, USA</location>
+    <year>2008</year>
+  </feature>
+
+Deserialization from XML works:
+
+  >>> feature = metadata.ProprietaryHeaderFeature.fromXML('''\
+  ...     <feature type="z3c.feature.core:proprietary-header">
+  ...       <company>Paulo, Inc.</company>
+  ...       <location>Berkeley, CA, USA</location>
+  ...       <year>2008</year>
+  ...     </feature>
+  ... ''')
+  >>> feature
+  <ProprietaryHeaderFeature u'Proprietary Header'>
+
+  >>> feature.year
+  2008
+  >>> feature.company
+  u'Paulo, Inc.'
+  >>> feature.location
+  u'Berkeley, CA, USA'
+
+Let's now apply the feature to a project:
+
+  >>> from z3c.builder.core import project
+  >>> prj = project.BuildoutProjectBuilder(u'test')
+
+  >>> feature.update()
+  >>> feature.applyTo(prj)
+
+And the data is applied:
+
+  >>> print prj.commentHeader
+  ###############################################################################
+  #
+  # Copyright 2008 by Paulo, Inc., Berkeley, CA, USA
+  #
+  ###############################################################################
+
+
+Documentation Feature
+---------------------
+
+This feature installs a buildout part that creates a script for a
+documentation generator of the package.
+
+  >>> feature = metadata.DocumentationFeature()
+  >>> feature
+  <DocumentationFeature u'Restructured Text Documentation'>
+
+  >>> feature.additionalEggs = [u'package2']
+
+Let's verify that both, the `IFeature` and `IDocumentationFeature`
+interface, are provided:
+
+  >>> verifyObject(interfaces.IFeature, feature)
+  True
+  >>> verifyObject(interfaces.IDocumentationFeature, feature)
+  True
+
+The feature schema is:
+
+  >>> base.getFeatureSchema(feature)
+  <InterfaceClass z3c.feature.core.interfaces.IDocumentationFeature>
+
+Good documentation is also provided:
+
+  >>> print feature.featureTitle
+  Restructured Text Documentation
+  >>> print feature.featureDocumentation
+  <BLANKLINE>
+  The ReSTructured Text Documentation feature hooks up scripts
+  for generating html (or latex for that matter) documentation
+  from ReSTructured text files using Sphinx.  There are a few
+  pieces involved in this hookup:
+  ...
+
+This is a singleton feature:
+
+  >>> feature.featureSingleton
+  True
+
+Serialization to XML works:
+
+  >>> print feature.toXML(True, True)
+  <feature type="z3c.feature.core:documentation">
+    <additionalEggs>
+      <item>package2</item>
+    </additionalEggs>
+  </feature>
+
+Deserialization from XML works:
+
+  >>> feature = metadata.DocumentationFeature.fromXML('''\
+  ...     <feature type="z3c.feature.core:documentation">
+  ...       <additionalEggs>
+  ...         <item>package2</item>
+  ...       </additionalEggs>
+  ...     </feature>
+  ... ''')
+  >>> feature
+  <DocumentationFeature u'Restructured Text Documentation'>
+
+  >>> feature.additionalEggs
+  [u'package2']
+
+Let's now apply the feature to a project:
+
+  >>> from z3c.builder.core import project
+  >>> prj = project.BuildoutProjectBuilder(u'test')
+
+  >>> feature.update()
+  >>> feature.applyTo(prj)
+
+And the data is applied:
+
+  >>> prj.update()
+  >>> print prj.buildout.render()
+  [buildout]
+  extends = http://download.zope.org/zope3.4/3.4.0/versions.cfg
+  develop = .
+  parts = docs
+  versions = versions
+  <BLANKLINE>
+  [docs]
+  recipe = z3c.recipe.sphinxdoc
+  eggs = z3c.recipe.sphinxdoc
+         test [docs]
+         package2
+  layout.html =
+  default.css =


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/metadata.txt
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.feature.core/trunk/src/z3c/feature/core/python-package-project.xml
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/python-package-project.xml	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/python-package-project.xml	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,16 @@
+<project name="helloworld">
+  <feature type="z3c.feature.core:meta-data">
+    <author>?</author>
+    <author-email>?</author-email>
+    <description>?</description>
+    <version>?</version>
+    <license>?</license>
+    <url>?</url>
+    <keywords>?</keywords>
+    <namespace-packages>?</namespace-packages>
+    <install-requires>?</install-requires>
+  </feature>
+  <feature type="z3c.feature.core:python-interpreter" />
+  <feature type="z3c.feature.core:unit-testing" />
+  <feature type="z3c.feature.core:documentation" />
+</project>


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/python-package-project.xml
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.feature.core/trunk/src/z3c/feature/core/python.py
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/python.py	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/python.py	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,126 @@
+##############################################################################
+#
+# Copyright (c) 2009 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Core Python Features
+
+$Id$
+"""
+import zope.interface
+from zope.schema.fieldproperty import FieldProperty
+
+from z3c.builder.core import project, buildout
+from z3c.builder.core.python import ModuleBuilder, FunctionBuilder
+from z3c.feature.core import base, interfaces, xml
+
+
+PYTHON_INTERPRETER_DOCUMENTATION = """
+The Python Interpreter feature creates an alias to whatever python
+interpreter you want inside your project's bin/ directory.
+Furthermore, it adds your project and all its egg dependencies to the
+python path.  After running buildout from your project directory you
+can start up the interpreter as normal::
+
+  $ ./bin/python
+
+  >>> from myproject import mymodule
+  >>> form a.dependency import something
+"""
+
+SCRIPT_FEATURE_DOCUMENTATION = """
+The Command Line Script Feature exposes a python function in your
+project as a command line script.  There are several pieces to this:
+
+  #. **The script file.**
+
+        There is a script file located at %(scriptFile)s with a function
+        in it called %(scriptFunction)s.  This is what you should modify to
+        make your script actually do something.
+
+  #. **Setuptools entry point.**
+
+       When someone installs your project using setuptools, for example
+       with the command::
+
+         $ easy_install yourproject
+
+       any entry points in the console_script group will turn into
+       executable scripts that the end user can just run.  This make your
+       project into an application and not just a set of python
+       libraries.
+
+       The entry point is created by modifying the ``setup.py`` file.
+       Look for the keyword parameter called entry_points.
+
+  #. ``scripts`` **buildout part.**
+
+       Finally there is also a part added to buildout.cfg that makes the
+       script defined in the entry point available in the projects bin/
+       directory.  This is only for development purposes.
+"""
+
+class PythonInterpreterFeature(base.BaseFeature):
+    zope.interface.implements(interfaces.IPythonInterpreterFeature)
+
+    executableName = FieldProperty(
+        interfaces.IPythonInterpreterFeature['executableName'])
+
+    featureTitle = u'Python Interpreter'
+    featureDocumentation = PYTHON_INTERPRETER_DOCUMENTATION
+
+    def _applyTo(self, context):
+        pythonPartBuilder = buildout.PartBuilder(u'python')
+        pythonPartBuilder.addValue('recipe', 'zc.recipe.egg')
+        pythonPartBuilder.addValue('interpreter', self.executableName)
+        pythonPartBuilder.addValue('eggs', context.name)
+        context.buildout.add(pythonPartBuilder)
+
+
+class ScriptFeature(base.BaseFeature):
+    zope.interface.implements(interfaces.IScriptFeature)
+
+    scriptName = FieldProperty(interfaces.IScriptFeature['scriptName'])
+    scriptFile = FieldProperty(interfaces.IScriptFeature['scriptFile'])
+    scriptFunction = FieldProperty(interfaces.IScriptFeature['scriptFunction'])
+
+    featureTitle = u'Command Line Script'
+
+    @property
+    def featureDocumentation(self):
+        return SCRIPT_FEATURE_DOCUMENTATION % dict(
+            scriptFunction=self.scriptFunction,
+            scriptFile=self.scriptFile)
+
+    def _applyTo(self, context):
+        scriptName = self.scriptName or context.name
+        moduleBuilder = ModuleBuilder(self.scriptFile)
+        moduleBuilder.add(FunctionBuilder(
+            self.scriptFunction,
+            kwargs={'args':None},
+            docstring=('Runs the %s script '
+                       'from the %s project') % (scriptName, context.name),
+            code='print "Successfully ran the %s script"' % scriptName
+            ))
+
+        context.package.add(moduleBuilder)
+
+        context.setup.addEntryPoints(
+            'console_scripts', ['%s = %s.%s:%s' % (scriptName,
+                                                   context.name,
+                                                   self.scriptFile[:-3],
+                                                   self.scriptFunction)])
+
+        scriptsPart = buildout.PartBuilder(u'scripts')
+        scriptsPart.addValue('recipe','zc.recipe.egg:scripts')
+        scriptsPart.addValue('eggs', context.name)
+        scriptsPart.addValue('script', scriptName)
+        context.buildout.add(scriptsPart)


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/python.py
___________________________________________________________________
Added: svn:keywords
   + Id

Added: z3c.feature.core/trunk/src/z3c/feature/core/python.txt
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/python.txt	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/python.txt	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,230 @@
+===============
+Python Features
+===============
+
+The Python features are designed to write Python code and setup the Python
+interpreter.
+
+  >>> from z3c.feature.core import python, interfaces
+
+
+Python Interpreter
+------------------
+
+This feature installs the buildout recipe to create a Python interpreter with
+the context of the package.
+
+  >>> feature = python.PythonInterpreterFeature()
+  >>> feature
+  <PythonInterpreterFeature u'Python Interpreter'>
+
+  >>> feature.executableName = 'py'
+
+Let's verify that both, the `IFeature` and `IPythonInterpreterFeature`
+interface, are provided:
+
+  >>> from zope.interface.verify import verifyObject
+  >>> verifyObject(interfaces.IFeature, feature)
+  True
+  >>> verifyObject(interfaces.IPythonInterpreterFeature, feature)
+  True
+
+The feature schema is:
+
+  >>> from z3c.feature.core import base
+  >>> base.getFeatureSchema(feature)
+  <InterfaceClass z3c.feature.core.interfaces.IPythonInterpreterFeature>
+
+Good documentation is also provided:
+
+  >>> print feature.featureTitle
+  Python Interpreter
+  >>> print feature.featureDocumentation
+  <BLANKLINE>
+  The Python Interpreter feature creates an alias to whatever python
+  interpreter you want inside your project's bin/ directory.
+  Furthermore, it adds your project and all its egg dependencies to the
+  python path.  After running buildout from your project directory you
+  can start up the interpreter as normal::
+  <BLANKLINE>
+    $ ./bin/python
+  ...
+
+This is a singleton feature:
+
+  >>> feature.featureSingleton
+  True
+
+Serialization to XML works:
+
+  >>> print feature.toXML(True, True)
+  <feature type="z3c.feature.core:python-interpreter">
+    <executableName>py</executableName>
+  </feature>
+
+Deserialization from XML works:
+
+  >>> feature = python.PythonInterpreterFeature.fromXML('''\
+  ...     <feature type="z3c.feature.core:python-interpreter">
+  ...       <executableName>py</executableName>
+  ...     </feature>
+  ... ''')
+  >>> feature
+  <PythonInterpreterFeature u'Python Interpreter'>
+
+  >>> feature.executableName
+  'py'
+
+Let's now apply the feature to a project:
+
+  >>> from z3c.builder.core import project
+  >>> prj = project.BuildoutProjectBuilder(u'test')
+
+  >>> feature.update()
+  >>> feature.applyTo(prj)
+
+And the data is applied:
+
+  >>> prj.buildout.update()
+  >>> print prj.buildout.render()
+  [buildout]
+  extends = http://download.zope.org/zope3.4/3.4.0/versions.cfg
+  develop = .
+  parts = python
+  versions = versions
+  <BLANKLINE>
+  [python]
+  recipe = zc.recipe.egg
+  interpreter = py
+  eggs = test
+
+Okay, we applied the feature successfully by creating an interpreter part.
+
+
+Script Feature
+--------------
+
+This feature installs the buildout recipe to create a Python interpreter with
+the context of the package.
+
+  >>> feature = python.ScriptFeature()
+  >>> feature
+  <ScriptFeature u'Command Line Script'>
+
+  >>> feature.scriptName = u'demo'
+  >>> feature.scriptFile = u'demo.py'
+  >>> feature.scriptFunction = u'script'
+
+Let's verify that both, the `IFeature` and `IScriptFeature` interface, are
+provided:
+
+  >>> verifyObject(interfaces.IFeature, feature)
+  True
+  >>> verifyObject(interfaces.IScriptFeature, feature)
+  True
+
+The feature schema is:
+
+  >>> base.getFeatureSchema(feature)
+  <InterfaceClass z3c.feature.core.interfaces.IScriptFeature>
+
+Good documentation is also provided:
+
+  >>> print feature.featureTitle
+  Command Line Script
+  >>> print feature.featureDocumentation
+  The Command Line Script Feature exposes a python function in your
+  project as a command line script.  There are several pieces to this...
+
+This is a singleton feature:
+
+  >>> feature.featureSingleton
+  True
+
+Serialization to XML works:
+
+  >>> print feature.toXML(True, True)
+  <feature type="z3c.feature.core:script">
+    <scriptName>demo</scriptName>
+    <scriptFile>demo.py</scriptFile>
+    <scriptFunction>script</scriptFunction>
+  </feature>
+
+Deserialization from XML works:
+
+  >>> feature = python.ScriptFeature.fromXML('''\
+  ...     <feature type="z3c.feature.core:script">
+  ...       <scriptName>demo</scriptName>
+  ...       <scriptFile>demo.py</scriptFile>
+  ...       <scriptFunction>script</scriptFunction>
+  ...     </feature>
+  ... ''')
+  >>> feature
+  <ScriptFeature u'Command Line Script'>
+
+  >>> feature.scriptName
+  u'demo'
+  >>> feature.scriptFile
+  u'demo.py'
+  >>> feature.scriptFunction
+  u'script'
+
+Let's now apply the feature to a project:
+
+  >>> from z3c.builder.core import project
+  >>> prj = project.BuildoutProjectBuilder(u'test')
+
+  >>> feature.update()
+  >>> feature.applyTo(prj)
+
+And the data is applied:
+
+  >>> prj.update()
+
+  >>> print prj.buildout.render()
+  [buildout]
+  extends = http://download.zope.org/zope3.4/3.4.0/versions.cfg
+  develop = .
+  parts = scripts
+  versions = versions
+  <BLANKLINE>
+  [scripts]
+  recipe = zc.recipe.egg:scripts
+  eggs = test
+  script = demo
+
+  >>> print prj.setup.render()
+  #...
+  setup (
+      name = 'test',
+      version = '0.1.0',
+      author = u"",
+      author_email = u"",
+      description = u"",
+      license = "GPLv3",
+      keywords = u"",
+      url = "http://pypi.python.org/pypi/test",
+      classifiers = [],
+      packages = find_packages('src'),
+      include_package_data = True,
+      package_dir = {'':'src'},
+      namespace_packages = [],
+      extras_require = {},
+      install_requires = [
+          'setuptools',
+          ],
+      zip_safe = False,
+      entry_points = {'console_scripts': [u'demo = test.demo:script']},
+      )
+
+  >>> print prj.package['demo.py'].render()
+  ##############################################################################
+  #
+  # This file is part of test...
+  #
+  ##############################################################################
+  """Module Documentation"""
+  <BLANKLINE>
+  def script(args=None):
+      """Runs the demo script from the test project"""
+      print "Successfully ran the demo script"


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/python.txt
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.feature.core/trunk/src/z3c/feature/core/template.py
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/template.py	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/template.py	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,69 @@
+##############################################################################
+#
+# Copyright (c) 2009 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Core Python Features
+
+$Id$
+"""
+import os
+import pkg_resources
+from zope.interface import classImplements
+from z3c.feature.core import interfaces, xml
+
+
+class FileBasedTemplateBase(object):
+
+    filename = None
+
+    def getFeatures(self):
+        if self.filename is None:
+            raise ValueError("Missing filename attribute.")
+        return xml.getFeaturesDict(open(self.filename))
+
+
+def FileBasedTemplate(filename, title, description):
+    FileBasedTemplate = type(
+        '<FileBasedTemplate for %s' % filename,
+        (FileBasedTemplateBase,), dict(title=title,
+                                   description=description,
+                                   filename=filename))
+
+    classImplements(FileBasedTemplate, interfaces.IFileBasedTemplateProvider)
+    return FileBasedTemplate
+
+
+def getTemplateList():
+    names = set()
+    templates = {}
+    for entryPoint in pkg_resources.working_set.iter_entry_points('z3c.boiler.template'):
+        template = entryPoint.load()()
+        if entryPoint.name in names:
+            name = entryPoint.dist.project_name+':'+entryPoint.name
+        else:
+            name = entryPoint.name
+        templates[name] = template
+    return templates
+
+
+def getTemplate(name):
+    return getTemplateList()[name]
+
+CommandLineProjectTemplate = FileBasedTemplate(
+    os.path.join(os.path.dirname(__file__),"command-line-project.xml"),
+    u"Command Line Program",
+    u"Includes all the features you would want for a command line program.")
+
+PythonPackageProjectTemplate = FileBasedTemplate(
+    os.path.join(os.path.dirname(__file__),"python-package-project.xml"),
+    u"Python Package",
+    u"Just a simple python package with few bells and whistles.")


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/template.py
___________________________________________________________________
Added: svn:keywords
   + Id

Added: z3c.feature.core/trunk/src/z3c/feature/core/testing.py
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/testing.py	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/testing.py	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,51 @@
+##############################################################################
+#
+# Copyright (c) 2009 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Testing Support
+
+$Id$
+"""
+
+
+class EntryPoint(object):
+
+    def __init__(self, group, name, klass, dist):
+        self.group = group
+        self.name = name
+        self.klass = klass
+        self.dist = dist
+
+    def load(self):
+        return self.klass
+
+class FeatureDistribution(object):
+
+    project_name = 'z3c.feature.testing'
+    key = 'z3c.feature.testing'
+    location = 'z3c.feature.testing'
+
+    def __init__(self):
+        self.entryPoints = {}
+
+    def addEntryPoint(self, group, name, klass):
+        eps = self.entryPoints.setdefault(group, {})
+        eps[name] = EntryPoint(group, name, klass, self)
+
+    def get_entry_map(self, group):
+        return self.entryPoints[group]
+
+    def insert_on(self, entries, entry):
+        entries.append(self.location)
+
+    def activate(self):
+        pass


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/testing.py
___________________________________________________________________
Added: svn:keywords
   + Id

Added: z3c.feature.core/trunk/src/z3c/feature/core/tests/__init__.py
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/tests/__init__.py	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/tests/__init__.py	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1 @@
+#package


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/tests/__init__.py
___________________________________________________________________
Added: svn:keywords
   + Id

Added: z3c.feature.core/trunk/src/z3c/feature/core/tests/test_doc.py
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/tests/test_doc.py	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/tests/test_doc.py	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,67 @@
+##############################################################################
+#
+# Copyright (c) 2007 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Test Setup.
+
+$Id$
+"""
+import os
+import shutil
+import unittest
+import tempfile
+import logging
+import zope.testing.doctest
+import zope.component
+from zope.configuration import xmlconfig
+from z3c.builder.core import testing
+
+def test_suite():
+    return unittest.TestSuite((
+
+        zope.testing.doctest.DocFileSuite(
+            '../README.txt',
+            optionflags=zope.testing.doctest.NORMALIZE_WHITESPACE |
+                        zope.testing.doctest.ELLIPSIS),
+
+        zope.testing.doctest.DocFileSuite(
+            '../example.txt',
+            optionflags=zope.testing.doctest.NORMALIZE_WHITESPACE |
+                        zope.testing.doctest.ELLIPSIS),
+
+        zope.testing.doctest.DocFileSuite(
+            '../base.txt',
+            optionflags=zope.testing.doctest.NORMALIZE_WHITESPACE |
+                        zope.testing.doctest.ELLIPSIS),
+
+        zope.testing.doctest.DocFileSuite(
+            '../metadata.txt',
+            optionflags=zope.testing.doctest.NORMALIZE_WHITESPACE |
+                        zope.testing.doctest.ELLIPSIS),
+
+        zope.testing.doctest.DocFileSuite(
+            '../python.txt',
+            optionflags=zope.testing.doctest.NORMALIZE_WHITESPACE |
+                        zope.testing.doctest.ELLIPSIS),
+
+        zope.testing.doctest.DocFileSuite(
+            '../unittest.txt',
+            optionflags=zope.testing.doctest.NORMALIZE_WHITESPACE |
+                        zope.testing.doctest.ELLIPSIS),
+
+        zope.testing.doctest.DocFileSuite(
+            '../xml.txt',
+            setUp=testing.buildSetUp, tearDown=testing.buildTearDown,
+            optionflags=zope.testing.doctest.NORMALIZE_WHITESPACE |
+                        zope.testing.doctest.ELLIPSIS),
+
+        ))


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/tests/test_doc.py
___________________________________________________________________
Added: svn:keywords
   + Id

Added: z3c.feature.core/trunk/src/z3c/feature/core/unittest.py
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/unittest.py	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/unittest.py	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,129 @@
+##############################################################################
+#
+# Copyright (c) 2009 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Unit Testing Feature
+
+$Id$
+"""
+import os
+import zope.interface
+from zope.schema.fieldproperty import FieldProperty
+
+from z3c.builder.core import buildout, python
+from z3c.builder.core.base import SimpleFileBuilder
+from z3c.feature.core import base, interfaces
+
+TESTING_DOCUMENTATION = """
+The Automated Testing feature generates boiler plate code for running
+unittests, specifically in the form of DocTests.  Here are the various
+pieces generated by the Automated Testing Feature:
+
+  1. **new buildout.cfg sections**
+
+       Three new sections are added to the buildout.cfg file. Each
+       correspond to executable scripts in the bin/ directory.  There is
+       a test runner which you can run with this command::
+
+         $ ./bin/test
+
+       There are many command line options that can be used by the test
+       runner.  See them all by running::
+
+         $ ./bin/test --help
+
+       You can also generate coverage reports for the tests with the command::
+
+         $ ./bin/coverage-test
+
+       This runs the same test runner as ``./bin/test`` buts adds on the
+       options for generating coverage reports which are always a little
+       painful to remember.  By default, this will put all the reports in
+       a ``coverage/`` directory. After you have generated the coverage
+       reports, you render them into nice color-coded html pages with
+       this command::
+
+         $ ./bin/coverage-report
+
+  2. **new setup.py extras_require section**
+
+       In order to support the test runner, a new extras_require section
+       called ``test`` is added to the ``setup.py`` file.  This pulls in
+       z3c.coverage and zope.testing.
+
+  3. **``tests`` module**
+
+       The test runner will search through your project src for modules
+       starting with the name ``test``.  The Automated Testing feature
+       adds a sub package named ``tests`` to the project source, and adds
+       a python module called ``test_doc.py`` into the tests module.  The
+       test_doc module hooks up the test suite that references the doc
+       tests.
+"""
+
+class TestingFeature(base.BaseFeature):
+    zope.interface.implements(interfaces.ITestingFeature)
+
+    iface = interfaces.ITestingFeature
+
+    coverageDirectory = FieldProperty(
+        interfaces.ITestingFeature['coverageDirectory'])
+    coverageReportDirectory = FieldProperty(
+        interfaces.ITestingFeature['coverageReportDirectory'])
+
+    featureTitle = u'Automated Tests'
+    featureDocumentation = TESTING_DOCUMENTATION
+
+    def _applyTo(self, project):
+        testPartBuilder = buildout.PartBuilder(
+            u'test',
+            values=[('recipe','zc.recipe.testrunner'),
+                    ('eggs',project.projectName+' [test]')])
+
+        project.buildout.add(testPartBuilder)
+
+        coverageTestPart = buildout.PartBuilder(
+            u'coverage-test',
+            values=[('recipe','zc.recipe.testrunner'),
+                    ('eggs',project.projectName+' [test]'),
+                    ('defaults',"['--coverage', '%s']" % self.coverageDirectory)])
+
+        project.buildout.add(coverageTestPart)
+
+        coverageReportPart = buildout.PartBuilder(
+            u'coverage-report',
+            values=[('recipe','zc.recipe.egg'),
+                    ('eggs','z3c.coverage'),
+                    ('scripts','coverage=coverage-report'),
+                    ('arguments',"('coverage', '%s')" % self.coverageReportDirectory)])
+
+        project.buildout.add(coverageReportPart)
+
+        project.setup.addExtrasRequires('test',('zope.testing',
+                                                'z3c.coverage'))
+
+        project.package.add(python.PackageBuilder(u'tests'))
+        project.package['tests'].add(
+            SimpleFileBuilder(u'test_doc.py',
+                              template=os.path.join(
+                                  os.path.dirname(__file__),
+                                  'file-templates',
+                                  'test_doc.py')
+                              ))
+
+        project.package.add(
+            SimpleFileBuilder(u'README.txt',
+                              template=os.path.join(
+                                  os.path.dirname(__file__),
+                                  'file-templates',
+                                  'README.txt')
+                              ))


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/unittest.py
___________________________________________________________________
Added: svn:keywords
   + Id

Added: z3c.feature.core/trunk/src/z3c/feature/core/unittest.txt
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/unittest.txt	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/unittest.txt	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,96 @@
+=====================
+Unit Testing Features
+=====================
+
+    >>> from z3c.feature.core import interfaces, unittest
+
+Testing Feature
+---------------
+
+The testing feature provides automated testing hook up.
+
+    >>> feature = unittest.TestingFeature()
+    >>> from zope.interface.verify import verifyObject
+    >>> verifyObject(interfaces.ITestingFeature, feature)
+    True
+
+    >>> print feature.featureTitle
+    Automated Tests
+
+We can also convert this to xml.
+
+    >>> print feature.toXML(True, True)
+    <feature type="z3c.feature.core:unit-testing"/>
+
+Let's see how the testing feature modifies the project builder.  First
+it adds buildout parts for running the tests, with and without
+coverage reports and for generating coverage reports.
+
+    >>> from z3c.builder.core.project import BuildoutProjectBuilder
+    >>> project = BuildoutProjectBuilder(u'someproject')
+
+    >>> feature.applyTo(project)
+
+    >>> project.update()
+    >>> print project.buildout.render()
+    [buildout]
+    extends = http://download.zope.org/zope3.4/3.4.0/versions.cfg
+    develop = .
+    parts = test coverage-test coverage-report
+    versions = versions
+    <BLANKLINE>
+    [test]
+    recipe = zc.recipe.testrunner
+    eggs = someproject [test]
+    <BLANKLINE>
+    [coverage-test]
+    recipe = zc.recipe.testrunner
+    eggs = someproject [test]
+    defaults = ['--coverage', '${buildout:directory}/coverage']
+    <BLANKLINE>
+    [coverage-report]
+    recipe = zc.recipe.egg
+    eggs = z3c.coverage
+    scripts = coverage=coverage-report
+    arguments = ('coverage', '${buildout:directory}/coverage/report')
+
+Next it also modifies setup.py to add an extras requires section.
+
+    >>> project.setup.extras_requires
+    {'test': ['z3c.coverage', 'zope.testing']}
+
+It also adds a tests package with a test_doc module that hooks up
+doctests.
+
+    >>> print project.package['tests']['test_doc.py'].render()
+    import unittest
+    import zope.testing.doctest
+    <BLANKLINE>
+    def test_suite():
+        return unittest.TestSuite((
+    <BLANKLINE>
+            zope.testing.doctest.DocFileSuite(
+                '../README.txt',
+                optionflags=zope.testing.doctest.NORMALIZE_WHITESPACE |
+                            zope.testing.doctest.ELLIPSIS),
+    <BLANKLINE>
+    <BLANKLINE>
+            ))
+
+Finally there is also a sample doctest file.
+
+    >>> print project.package['README.txt'].render()
+    ================
+    A Simple DocTest
+    ================
+    <BLANKLINE>
+    Doctests work by evaluating txt documents that look like python
+    interpreter sessions.  This test will pass:
+    ...
+
+Let's now look at the documentation that this feature provides.
+
+    >>> print feature.featureDocumentation
+    The Automated Testing feature generates boiler plate code for running
+    unittests, specifically in the form of DocTests.  Here are the various
+    pieces generated by the Automated Testing Feature...


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/unittest.txt
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.feature.core/trunk/src/z3c/feature/core/web/__init__.py
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/web/__init__.py	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/web/__init__.py	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1 @@
+#package


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/web/__init__.py
___________________________________________________________________
Added: svn:keywords
   + Id

Added: z3c.feature.core/trunk/src/z3c/feature/core/web/metadata.py
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/web/metadata.py	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/web/metadata.py	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,156 @@
+import zope.schema
+from zope.schema.vocabulary import SimpleVocabulary
+from zope.schema.vocabulary import SimpleTerm
+from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
+
+from z3c.form.form import EditForm
+from z3c.form.field import Fields
+from z3c.form.browser.textlines import TextLinesFieldWidget
+
+from z3c.feature.core.metadata import MetaDataFeature
+from z3c.feature.core.metadata import CommentHeaderZPLFeature
+from z3c.feature.core.metadata import ProprietaryHeaderFeature
+from z3c.feature.core.metadata import DocumentationFeature
+from z3c.feature.core.python import PythonInterpreterFeature
+from z3c.feature.core import interfaces
+
+licenseVocabulary = SimpleVocabulary([
+    SimpleTerm('Aladdin Free Public License (AFPL)'),
+    SimpleTerm('DFSG approved'),
+    SimpleTerm('Eiffel Forum License (EFL)'),
+    SimpleTerm('Free For Educational Use'),
+    SimpleTerm('Free For Home Use'),
+    SimpleTerm('Free for non-commercial use'),
+    SimpleTerm('Freely Distributable'),
+    SimpleTerm('Free To Use But Restricted'),
+    SimpleTerm('Freeware'),
+    SimpleTerm('Netscape Public License (NPL)'),
+    SimpleTerm('Nokia Open Source License (NOKOS)'),
+    SimpleTerm('Academic Free License (AFL)'),
+    SimpleTerm('Apache Software License'),
+    SimpleTerm('Apple Public Source License'),
+    SimpleTerm('Artistic License'),
+    SimpleTerm('Attribution Assurance License'),
+    SimpleTerm('BSD License'),
+    SimpleTerm('Common Public License'),
+    SimpleTerm('Eiffel Forum License'),
+    SimpleTerm('GNU Affero General Public License v3'),
+    SimpleTerm('GNU Free Documentation License (FDL)'),
+    SimpleTerm('GNU General Public License (GPL)'),
+    SimpleTerm('GNU Library or Lesser General Public License (LGPL)'),
+    SimpleTerm('IBM Public License'),
+    SimpleTerm('Intel Open Source License'),
+    SimpleTerm('Jabber Open Source License'),
+    SimpleTerm('MIT License'),
+    SimpleTerm('MITRE Collaborative Virtual Workspace License (CVW)'),
+    SimpleTerm('Motosoto License'),
+    SimpleTerm('Mozilla Public License 1.0 (MPL)'),
+    SimpleTerm('Mozilla Public License 1.1 (MPL 1.1)'),
+    SimpleTerm('Nethack General Public License'),
+    SimpleTerm('Nokia Open Source License'),
+    SimpleTerm('Open Group Test Suite License'),
+    SimpleTerm('Python License (CNRI Python License)'),
+    SimpleTerm('Python Software Foundation License'),
+    SimpleTerm('Qt Public License (QPL)'),
+    SimpleTerm('Ricoh Source Code Public License'),
+    SimpleTerm('Sleepycat License'),
+    SimpleTerm('Sun Industry Standards Source License (SISSL)'),
+    SimpleTerm('Sun Public License'),
+    SimpleTerm('University of Illinois/NCSA Open Source License'),
+    SimpleTerm('Vovida Software License 1.0'),
+    SimpleTerm('W3C License'),
+    SimpleTerm('X.Net License'),
+    SimpleTerm('zlib/libpng License'),
+    SimpleTerm('Zope Public License'),
+    SimpleTerm('Other/Proprietary License'),
+    SimpleTerm('Public Domain'),
+    ])
+
+
+class MetaDataWebView(EditForm):
+
+    label = u'Project Metadata'
+    template = ViewPageTemplateFile('templates/metadata.pt')
+    javascript = ViewPageTemplateFile('templates/metadata.js')
+    fields = Fields(zope.schema.Choice(
+         __name__='commonLicense',
+        title=u'License',
+        default=u'GNU General Public License (GPL)',
+        vocabulary=licenseVocabulary,
+        required=False))
+
+    fields += Fields(interfaces.IMetaDataFeature).select(
+        'license','version','description','author',
+        'author_email','url','classifiers','keywords',
+        'namespace_packages','install_requires',
+        'commentHeader')
+
+    fields = fields.select(
+        'license','commonLicense','version','description','author',
+        'author_email','url','classifiers','keywords',
+        'namespace_packages','install_requires',
+        'commentHeader')
+
+    fields['keywords'].widgetFactory = TextLinesFieldWidget
+    fields['namespace_packages'].widgetFactory = TextLinesFieldWidget
+    fields['install_requires'].widgetFactory = TextLinesFieldWidget
+
+    def applyChanges(self, data):
+        commonLicense = data.pop('commonLicense')
+        data['license'] = data.get('license', commonLicense)
+        return super(MetaDataWebView, self).applyChanges(data)
+
+
+class MetaDataWebFeature(object):
+    viewFactory = MetaDataWebView
+    contentFactory = MetaDataFeature
+    title = u"Metadata"
+    description = (u"Let's you modify project metadata used by setuptools "
+                   u"such as version number, license information, author "
+                   u"information, project description, and more.")
+
+
+class CommentHeaderZPLWebView(EditForm):
+    label = u'Comment Header for the Zope Public License'
+    fields = Fields(interfaces.ICommentHeaderZPLFeature).select('year')
+
+
+class CommentHeaderZPLWebFeature(object):
+    viewFactory = CommentHeaderZPLWebView
+    contentFactory = CommentHeaderZPLFeature
+    title = u"ZPL Header"
+    description = u"Adds a ZPL Header to all generated files."
+
+
+
+class ProprietaryHeaderWebView(EditForm):
+    template = ViewPageTemplateFile("templates/proprietary-header.pt")
+    fields = Fields(interfaces.IProprietaryHeaderFeature).select(
+        'year','company','location')
+
+    def update(self):
+        super(ProprietaryHeaderWebView, self).update()
+        self.commentHeader = self.context.renderCommentHeader()
+
+class ProprietaryHeaderWebFeature(object):
+    viewFactory = ProprietaryHeaderWebView
+    contentFactory = ProprietaryHeaderFeature
+    title = u"Proprietary Header"
+    description = u"Adds a Proprietary Software Header to all generated files."
+
+
+class DocumentationWebView(EditForm):
+    label = u'Documentation'
+    fields = Fields(interfaces.IDocumentationFeature)
+
+
+class DocumentationWebFeature(object):
+    viewFactory = DocumentationWebView
+    contentFactory = DocumentationFeature
+    title = u"Documentation"
+    description = (u"Adds a script for generating project documentation from "
+                   u"ReSTructured Text files using Sphinx (just like the python "
+                   u"documetation).  Great in combination with the "
+                   u"Automated Tests Feature")
+
+


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/web/metadata.py
___________________________________________________________________
Added: svn:keywords
   + Id

Added: z3c.feature.core/trunk/src/z3c/feature/core/web/python.py
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/web/python.py	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/web/python.py	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,35 @@
+from z3c.form.form import EditForm
+from z3c.form.field import Fields
+
+from z3c.feature.core.python import PythonInterpreterFeature
+from z3c.feature.core.python import ScriptFeature
+from z3c.feature.core import interfaces
+
+
+class PythonInterpreterWebView(EditForm):
+    fields = Fields(interfaces.IPythonInterpreterFeature).select('executableName')
+
+class PythonInterpreterWebFeature(object):
+    viewFactory = PythonInterpreterWebView
+    contentFactory = PythonInterpreterFeature
+    title = u'Python Interpreter'
+    description = (u"Adds a Python interpreter with all the path "
+                   u"information needed to make project code available.")
+
+
+class ScriptWebView(EditForm):
+
+    fields = Fields(interfaces.IScriptFeature)
+
+    def update(self):
+        super(ScriptWebView, self).update()
+        self.label = self.context.featureTitle
+
+class ScriptWebFeature(object):
+    viewFactory = ScriptWebView
+    contentFactory = ScriptFeature
+    title = u'Console Script'
+    description = (u"Adds an entry point for setuptools to create a "
+                   u"console script that runs a python function. "
+                   u"Great for projects that run from the command line "
+                   u"and require insanely easy installation.")


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/web/python.py
___________________________________________________________________
Added: svn:keywords
   + Id

Added: z3c.feature.core/trunk/src/z3c/feature/core/web/templates/comment-header-zpl.pt
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/web/templates/comment-header-zpl.pt	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/web/templates/comment-header-zpl.pt	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1 @@
+<div metal:use-macro="macro:form"/>


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/web/templates/comment-header-zpl.pt
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.feature.core/trunk/src/z3c/feature/core/web/templates/metadata.js
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/web/templates/metadata.js	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/web/templates/metadata.js	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,25 @@
+$(document).ready(
+  function(){
+
+    $("label[for='form-widgets-license'] span").html("Custom License");
+
+    if ($(this).find("option:selected").val() !== '--NOVALUE--'){
+      $("#form-widgets-license-row").hide();
+    }
+
+    $("#form-widgets-commonLicense-novalue")
+      .html("Choose my own license")
+      .parent()
+      .change(
+        function(){
+          var selected = $(this).find("option:selected").val();
+          $("#form-widgets-license").val("");
+          if (selected == '--NOVALUE--'){
+            $("#form-widgets-license-row").show();
+            $("#form-widgets-license").focus();
+          } else {
+            $("#form-widgets-license-row").hide();
+          }
+        });
+
+  });

Added: z3c.feature.core/trunk/src/z3c/feature/core/web/templates/metadata.pt
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/web/templates/metadata.pt	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/web/templates/metadata.pt	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,20 @@
+<style type="text/css">
+  #form-widgets-license-row{
+    float: left;
+  }
+  #form-widgets-commentHeader{
+    font-family: Courier;
+    height: 200px;
+    width: 400px;
+  }
+  textarea,
+  input.text-widget{
+    width: 300px;
+  }
+</style>
+<div metal:use-macro="macro:form">
+</div>
+
+<script language="Javascript" type="text/javascript"
+        tal:content="structure view/javascript">
+</script>
\ No newline at end of file


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/web/templates/metadata.pt
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.feature.core/trunk/src/z3c/feature/core/web/templates/proprietary-header.pt
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/web/templates/proprietary-header.pt	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/web/templates/proprietary-header.pt	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,4 @@
+<div metal:use-macro="macro:form"/>
+
+<p>The comment header will look like this:</p>
+<pre tal:content="view/commentHeader"/>
\ No newline at end of file


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/web/templates/proprietary-header.pt
___________________________________________________________________
Added: svn:eol-style
   + native

Added: z3c.feature.core/trunk/src/z3c/feature/core/web/unittest.py
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/web/unittest.py	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/web/unittest.py	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,18 @@
+from z3c.form.form import EditForm
+from z3c.form.field import Fields
+
+from z3c.feature.core.unittest import TestingFeature
+from z3c.feature.core import interfaces
+
+
+class TestingWebView(EditForm):
+    fields = Fields(interfaces.ITestingFeature)
+
+
+class TestingWebFeature(object):
+    viewFactory = TestingWebView
+    contentFactory = TestingFeature
+    title = u"Automated Tests"
+    description = (u"Adds all the boiler plate for running an "
+                   u"automated doctest suite along with test "
+                   u"coverage reporting.")


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/web/unittest.py
___________________________________________________________________
Added: svn:keywords
   + Id

Added: z3c.feature.core/trunk/src/z3c/feature/core/xml.py
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/xml.py	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/xml.py	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,123 @@
+##############################################################################
+#
+# Copyright (c) 2009 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""XML Processing
+
+$Id$
+"""
+
+from lxml import etree
+import types
+import pkg_resources
+from zc.buildout import easy_install
+from zope.interface import directlyProvides
+
+from z3c.builder.core import project, base
+from z3c.feature.core import interfaces
+
+target_dir = pkg_resources.working_set.find(
+    pkg_resources.Requirement.parse('zc.buildout')).location
+
+
+class FeatureDocBuilder(base.FileBuilder):
+    """A builder to document all applied features of a project."""
+
+    def render(self):
+        result = open(
+            base.getTemplatePath('zboiler-doc-header.txt'), 'r').read()
+        for feature in self.getProject().appliedFeatures:
+            title = feature.featureTitle
+            result += "\n\n" + title + "\n"
+            result += "-"*len(title) + "\n"
+            docs = feature.featureDocumentation.strip()
+            result += "\n" + docs + "\n\n"
+        return result
+
+
+def getNode(nodeOrFileOrStr):
+    if isinstance(nodeOrFileOrStr, types.StringTypes):
+        return etree.fromstring(nodeOrFileOrStr)
+    elif hasattr(nodeOrFileOrStr, 'read'):
+        data = nodeOrFileOrStr.read()
+        nodeOrFileOrStr.seek(0)
+        return etree.fromstring(data)
+    elif isinstance(nodeOrFileOrStr, etree._Element):
+        return nodeOrFileOrStr
+    else:
+        raise TypeError("Could not get a lxml.etree.Element "
+                        "object from %s" % nodeOrFileOrStr)
+
+def getFeatureFactory(featureNode):
+    featureType = featureNode.get('type')
+    egg, entryPoint = featureType.split(':')
+    if egg not in pkg_resources.working_set.by_key:
+        ws = easy_install.install([egg], target_dir, newest=True)
+        distro = ws.find(pkg_resources.Requirement.parse(eggs))
+        pkg_resources.working_set.add(distro)
+    else:
+        distro = pkg_resources.get_distribution(egg)
+    try:
+        featureFactory = distro.load_entry_point(
+            interfaces.FEATURE_GROUP, entryPoint)
+    except ImportError, e:
+        raise ValueError("Unable to load feature factory for %s:%s because: %s" % (interfaces.FEATURE_GROUP, entryPoint, e))
+    return featureFactory
+
+def getFeaturesDict(node):
+    node = getNode(node)
+    features = {}
+    for featureNode in node.xpath('//feature'):
+        featureFactory = getFeatureFactory(featureNode).fromXMLNode(featureNode)
+        feature = featureFactory.fromXMLNode(featureNode)
+        features[featureNode.get('type')] = feature
+    return features
+
+def getFeatures(node):
+    return getFeaturesDict(node).values()
+
+
+def xmlToProject(node):
+    node = getNode(node)
+    name = node.get("name")
+    builder = project.BuildoutProjectBuilder(unicode(name))
+    from z3c.feature.core import base
+    base.applyFeatures(getFeatures(node), builder)
+    builder.add(FeatureDocBuilder(u'ZBOILER.txt'))
+    return builder
+
+def extractData(node, iface, convert=True):
+    node = getNode(node)
+    data = {}
+    for fieldName in iface:
+        matches = node.xpath('./%s'%fieldName.replace('_','-'))
+        match = matches[0] if matches else None
+        if match is not None:
+            if convert:
+                # ignore templated values when converting to actual fields.
+                if match.text == "?":
+                    continue
+                # see if this is a composite element representing a list of
+                # items.
+                items = match.xpath('./item')
+                if items:
+                    data[fieldName] = [unicode(item.text) for item in items]
+                # otherwise, see if we can parse a value from unicode
+                elif hasattr(iface[fieldName], 'fromUnicode'):
+                    data[fieldName] = iface[fieldName].fromUnicode(
+                        unicode(match.text))
+                # oh well, we'll just send back the unicode and hope that's ok
+                elif match.text:
+                    data[fieldName] = unicode(match.text)
+            else:
+                data[fieldName] = unicode(match.text)
+    return data


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/xml.py
___________________________________________________________________
Added: svn:keywords
   + Id

Added: z3c.feature.core/trunk/src/z3c/feature/core/xml.txt
===================================================================
--- z3c.feature.core/trunk/src/z3c/feature/core/xml.txt	                        (rev 0)
+++ z3c.feature.core/trunk/src/z3c/feature/core/xml.txt	2009-03-27 08:52:36 UTC (rev 98393)
@@ -0,0 +1,233 @@
+============================
+XML Configuration Processing
+============================
+
+We have an xml configuration layer for the project builder.
+
+    >>> from z3c.feature.core import xml
+
+Getting a node
+--------------
+
+We can get a node in a number of different ways:
+
+- from string
+
+    >>> node = xml.getNode("<root>this is the root node</root>")
+    >>> node
+    <Element root at ...>
+
+- from a file
+
+    >>> import StringIO
+    >>> io = StringIO.StringIO('<root>this is the root node</root>')
+
+    >>> xml.getNode(io)
+    <Element root at ...>
+
+- from a node (just returns the node)
+
+    >>> xml.getNode(node)
+    <Element root at ...>
+
+Or throws type error for something it doesn't know what to do with:
+
+    >>> xml.getNode(False)
+    Traceback (most recent call last):
+    ...
+    TypeError: Could not get a lxml.etree.Element object from False
+
+
+Extracting Data
+---------------
+
+We can extract data from an xml block based on an interface.  It will
+do the data conversion automatically if it can.
+
+  >>> import zope.interface
+  >>> import zope.schema
+
+  >>> class IMyFeature(zope.interface.Interface):
+  ...     name = zope.schema.TextLine()
+  ...     is_cool = zope.schema.Bool()
+  ...     developers = zope.schema.Int()
+  ...     aList = zope.schema.List()
+  ...     inTemplate = zope.schema.TextLine()
+  >>> node = '''
+  ... <feature type="my-feature">
+  ...   <name>myproject</name>
+  ...   <is-cool>t</is-cool>
+  ...   <developers>3</developers>
+  ...   <aList><item>one</item><item>two</item></aList>
+  ...   <inTemplate>?</inTemplate>
+  ... </feature>
+  ... '''
+
+  >>> sorted(xml.extractData(node, IMyFeature).items())
+  [('aList', [u'one', u'two']),
+   ('developers', 3),
+   ('is_cool', False),
+   ('name', u'myproject')]
+
+
+Applying Features
+-----------------
+
+Once features are loaded they need to be applied on their context. By default
+the context is a project:
+
+  >>> from z3c.builder.core import project
+  >>> context = project.BuildoutProjectBuilder(u'myproject')
+
+  >>> node = xml.getNode('''\
+  ... <project name="sampleproject">
+  ...
+  ...   <feature type="z3c.feature.core:meta-data">
+  ...     <license>ZPL</license>
+  ...     <version>0.1.0</version>
+  ...     <author>Paul Carduner and Stephan Richter</author>
+  ...     <author-email>zope-dev at zope.org</author-email>
+  ...     <description>A utility to start Zope 3 projects</description>
+  ...     <license>ZPL 2.1</license>
+  ...     <keywords>
+  ...       <item>zope3</item>
+  ...       <item>project</item>
+  ...       <item>builder</item>
+  ...     </keywords>
+  ...     <url>http://pypi.python.org/pypi/sampleproject</url>
+  ...   </feature>
+  ...
+  ...   <feature type="z3c.feature.core:python-interpreter">
+  ...     <executableName>py</executableName>
+  ...   </feature>
+  ... </project>
+  ... ''')
+
+We can extract all the features from this node:
+
+  >>> from pprint import pprint
+  >>> features = xml.getFeatures(node)
+  >>> pprint(sorted(features))
+  [<MetaDataFeature u'Metadata'>,
+   <PythonInterpreterFeature u'Python Interpreter'>]
+
+Let's now apply the feature onto the context.
+
+  >>> from z3c.feature.core import base
+  >>> base.applyFeatures(features, context)
+
+
+Creating a Project
+------------------
+
+We can create a simple project:
+
+  >>> project = xml.xmlToProject('<project name="helloworld"></project>')
+  >>> project
+  <BuildoutProjectBuilder u'helloworld'>
+
+Let's now render the project:
+
+  >>> project.update()
+  >>> project.write(buildPath)
+
+  >>> ls(buildPath)
+  helloworld/
+    ZBOILER.txt
+    bootstrap.py
+    buildout.cfg
+    setup.py
+    src/
+      helloworld/
+        __init__.py
+
+As you can see, when building a project from features, the project creates a
+`ZBOILER.txt` file that documents all applied features:
+
+  >>> more(buildPath, 'helloworld', 'ZBOILER.txt')
+  =========================================================
+  Documentation About Project Features Generated by ZBoiler
+  =========================================================
+  <BLANKLINE>
+  ZBoiler (http://zboiler.com) was used to generate the boiler plate
+  code for this project.  To build your project, run these commands::
+  <BLANKLINE>
+    $ python bootstrap.py
+    $ ./bin/buildout
+  <BLANKLINE>
+  What happens next depends on the features you used to generate your
+  project.  Check out more information about each feature below.
+
+The set pf all applied features is collected in the project, which now has a
+special interface it directly provides:
+
+  >>> from zope.interface.verify import verifyObject
+  >>> from z3c.feature.core import interfaces
+
+  >>> verifyObject(interfaces.IHaveAppliedFeatures, project)
+  True
+
+  >>> project.appliedFeatures
+  []
+
+Since our simple project does not have any features, none are lsited and the
+text file was rather empty. Let's change that situation:
+
+  >>> from z3c.feature.core.python import ScriptFeature
+
+  >>> feature = ScriptFeature()
+  >>> feature.applyTo(project)
+
+Let's now render the project again:
+
+  >>> project.update()
+  >>> project.write(buildPath, True)
+
+  >>> more(buildPath, 'helloworld', 'ZBOILER.txt')
+  =========================================================
+  Documentation About Project Features Generated by ZBoiler
+  =========================================================
+  <BLANKLINE>
+  ZBoiler (http://zboiler.com) was used to generate the boiler plate
+  code for this project.  To build your project, run these commands::
+  <BLANKLINE>
+    $ python bootstrap.py
+    $ ./bin/buildout
+  <BLANKLINE>
+  What happens next depends on the features you used to generate your
+  project.  Check out more information about each feature below.
+  <BLANKLINE>
+  <BLANKLINE>
+  <BLANKLINE>
+  Command Line Script
+  -------------------
+  <BLANKLINE>
+  The Command Line Script Feature exposes a python function in your
+  project as a command line script.  There are several pieces to this:
+  <BLANKLINE>
+    #. **The script file.**
+  <BLANKLINE>
+          There is a script file located at script.py with a function
+          in it called main.  This is what you should modify to
+          make your script actually do something.
+  <BLANKLINE>
+    #. **Setuptools entry point.**
+  <BLANKLINE>
+         When someone installs your project using setuptools, for example
+         with the command::
+  <BLANKLINE>
+           $ easy_install yourproject
+  <BLANKLINE>
+         any entry points in the console_script group will turn into
+         executable scripts that the end user can just run.  This make your
+         project into an application and not just a set of python
+         libraries.
+  <BLANKLINE>
+         The entry point is created by modifying the ``setup.py`` file.
+         Look for the keyword parameter called entry_points.
+  <BLANKLINE>
+    #. ``scripts`` **buildout part.**
+  <BLANKLINE>
+         Finally there is also a part added to buildout.cfg that makes the
+         script defined in the entry point available in the projects bin/
+         directory.  This is only for development purposes.


Property changes on: z3c.feature.core/trunk/src/z3c/feature/core/xml.txt
___________________________________________________________________
Added: svn:eol-style
   + native



More information about the Checkins mailing list