diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ce8563b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+*.pyc
+*~
+\#*#
+.#*
+._*
+build
+dist
+*.egg-info
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..46307a4
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,25 @@
+Copyright (c) 2012, Ryan Kaskel
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..9d5d250
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,2 @@
+include LICENSE
+include README.rst
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..d825ba1
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,47 @@
+django-discoverage
+==================
+
+Adds `coverage `_ to Jannis Leidel and
+Carl Meyer's `django-discover-runner
+`_.
+
+Inspired by `django-coverage `_.
+
+Usage
+-----
+
+One of the objectives of ``django-discover-runner`` is to allow separating a
+Django app's tests from the code it's testing. Since tests no longer reside in
+an app, ``django-discoverage`` needs a different way to know which apps to
+include in the coverage report. It currently collects apps (packages) by looking
+for an attribute (by default ``TESTS_APPS``) on each ``TestCase`` instance in
+the suite.
+
+For example::
+
+ class MyTestCase(TestCase):
+ TESTS_APPS = ('mycoolapp', 'myproject.anothercoolapp')
+ ...
+
+All modules in the listed apps (except those specified in ``OMIT_MODULES``) will
+appear in the standard ``coverage`` report.
+
+Settings
+--------
+
+``APPS_TEST_CASE_ATTR``
+ The attribute ``django-discoverage`` looks for on each ``TestCase`` instance.
+
+``OMIT_MODULES``
+ Modules not to be traced by ``coverage``. See the `coverage API
+ documentation
+ `_ for more
+ details.
+
+TODO
+----
+
+* Investigate discovering the apps being tested from the test modules imports
+ (this could be really annoying)
+* Check whether the report can be customized to display package names instead of
+ paths
diff --git a/discoverage/__init__.py b/discoverage/__init__.py
new file mode 100644
index 0000000..de959f0
--- /dev/null
+++ b/discoverage/__init__.py
@@ -0,0 +1,3 @@
+from discoverage.runner import DiscoverageRunner
+
+__version__ = '0.1.0'
diff --git a/discoverage/runner.py b/discoverage/runner.py
new file mode 100644
index 0000000..6ac3bd4
--- /dev/null
+++ b/discoverage/runner.py
@@ -0,0 +1,33 @@
+import coverage
+from discover_runner import DiscoverRunner
+
+from discoverage.settings import OMIT_MODULES, APPS_TEST_CASE_ATTR
+
+def find_coverage_apps(suite):
+ coverage_apps = set()
+ for test in suite:
+ apps = getattr(test, APPS_TEST_CASE_ATTR, ())
+ for app in apps:
+ coverage_apps.add(app)
+ return coverage_apps
+
+
+class DiscoverageRunner(DiscoverRunner):
+ def build_suite(self, *args, **kwargs):
+ if not hasattr(self, '_suite'):
+ self._suite = super(DiscoverageRunner, self).build_suite(
+ *args, **kwargs)
+ return self._suite
+
+ def run_tests(self, test_labels, extra_tests=None, **kwargs):
+ suite = self.build_suite(test_labels, extra_tests)
+ coverage_modules = find_coverage_apps(suite)
+ cov = coverage.coverage(source=coverage_modules, omit=OMIT_MODULES)
+ cov.start()
+ result = super(DiscoverageRunner, self).run_tests(test_labels,
+ extra_tests=None,
+ **kwargs)
+ cov.stop()
+ print
+ cov.report()
+ return result
diff --git a/discoverage/settings.py b/discoverage/settings.py
new file mode 100644
index 0000000..d338698
--- /dev/null
+++ b/discoverage/settings.py
@@ -0,0 +1,7 @@
+from django.conf import settings
+
+# The attriute on each test case that determines which apps to trace
+APPS_TEST_CASE_ATTR = getattr(settings, 'APPS_TEST_CASE_ATTR', 'TESTS_APPS')
+
+# Modules not to trace
+OMIT_MODULES = getattr(settings, 'OMIT_MODULES', ['*test*'])
diff --git a/setup.py b/setup.py
new file mode 100755
index 0000000..df43693
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+import os.path
+import re
+from setuptools import setup, find_packages
+
+def pkg_path(*components):
+ path = os.path.join(os.path.dirname(__file__), *components)
+ return os.path.realpath(path)
+
+def get_readme():
+ with open(pkg_path('README.rst'), 'r') as readme:
+ return readme.read()
+
+def get_version():
+ with open(pkg_path('discoverage', '__init__.py'), 'r') as init:
+ contents = init.read()
+ match = re.search(r'^__version__ = [\'"]([.\w]+)[\'"]', contents)
+ return match.group(1)
+
+setup(
+ name='django-discoverage',
+ version=get_version(),
+ author='Ryan Kaskel',
+ author_email='dev@ryankaskel.com',
+ url='https://github.com/ryankask/django-discoverage',
+ packages=find_packages(),
+ install_requires=['coverage>=3.5.3', 'django-discover-runner>=0.2.2'],
+ description='Jannis Leidel and Carl Meyer\'s django-discover-runner with coverage.',
+ long_description=get_readme(),
+ license='BSD',
+ classifiers=[
+ 'Development Status :: 3 - Alpha',
+ 'Environment :: Web Environment',
+ 'Framework :: Django',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: BSD License',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python',
+ 'Programming Language :: Python :: 2.6',
+ 'Programming Language :: Python :: 2.7',
+ 'Topic :: Software Development :: Testing',
+ ]
+)