diff --git a/tests/builddocs.py b/tests/builddocs.py new file mode 100755 index 0000000000000..191d62838a100 --- /dev/null +++ b/tests/builddocs.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +""" +This module builds HTML documentation for models by introspecting the model +unit tests. +""" + +from django.core import meta, template +import runtests +import inspect, os, re, sys + +MODEL_DOC_TEMPLATE = """ +
+ +

{{ title }}

+{{ blurb }} + +

Model source code

+
{{ model_source }}
+ +

Sample API usage

+
{{ api_usage }}
+
+""" + +def make_docs_from_model_tests(output_dir): + from django.conf import settings + + # Manually set INSTALLED_APPS to point to the test app. + settings.INSTALLED_APPS = (runtests.APP_NAME,) + + for model_name in runtests.get_test_models(): + mod = meta.get_app(model_name) + + # Clean up the title and blurb. + title, blurb = mod.__doc__.strip().split('\n', 1) + blurb = '

%s

' % blurb.strip().replace('\n\n', '

') + api_usage = mod.API_TESTS + + # Get the source code of the model, without the docstring or the + # API_TESTS variable. + model_source = inspect.getsource(mod) + model_source = model_source.replace(mod.__doc__, '') + model_source = model_source.replace(mod.API_TESTS, '') + model_source = model_source.replace('""""""\n', '\n') + model_source = model_source.replace('API_TESTS = ', '') + model_source = model_source.strip() + + # Run this through the template system. + t = template.Template(MODEL_DOC_TEMPLATE) + c = template.Context(locals()) + html = t.render(c) + + file_name = os.path.join(output_dir, 'model_' + model_name + '.html') + try: + fp = open(file_name, 'w') + except IOError: + sys.stderr.write("Couldn't write to %s.\n" % file_name) + continue + else: + fp.write(html) + fp.close() + +if __name__ == "__main__": + import sys + make_docs_from_model_tests(sys.argv[1]) diff --git a/tests/runtests.py b/tests/runtests.py index cbf8ad810dcdb..495ff36879cc2 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -17,6 +17,11 @@ def log_error(model_name, title, description): 'description': description, }) +MODEL_DIR = os.path.join(os.path.dirname(__file__), APP_NAME, 'models') + +def get_test_models(): + return [f[:-3] for f in os.listdir(MODEL_DIR) if f.endswith('.py') and not f.startswith('__init__')] + class DjangoDoctestRunner(doctest.DocTestRunner): def __init__(self, verbosity_level, *args, **kwargs): self.verbosity_level = verbosity_level @@ -51,13 +56,15 @@ def run_tests(self): # Manually set INSTALLED_APPS to point to the test app. settings.INSTALLED_APPS = (APP_NAME,) - # If we're using SQLite, it's more convient to test against an in-memory database + # If we're using SQLite, it's more convenient to test against an + # in-memory database. if settings.DATABASE_ENGINE == "sqlite3": global TEST_DATABASE_NAME TEST_DATABASE_NAME = ":memory:" - else: - # Create the test database and connect to it. We need autocommit() because - # PostgreSQL doesn't allow CREATE DATABASE statements within transactions. + else: + # Create the test database and connect to it. We need autocommit() + # because PostgreSQL doesn't allow CREATE DATABASE statements + # within transactions. cursor = db.cursor() try: db.connection.autocommit() @@ -83,10 +90,8 @@ def run_tests(self): self.output(1, "Initializing test database") management.init() - # Run the tests for each model within APP_NAME/models. - model_dir = os.path.join(os.path.dirname(__file__), APP_NAME, 'models') - test_models = [f[:-3] for f in os.listdir(model_dir) if f.endswith('.py') and not f.startswith('__init__')] - for model_name in test_models: + # Run the tests for each test model. + for model_name in get_test_models(): self.output(1, "%s model: Importing" % model_name) try: mod = meta.get_app(model_name) @@ -106,9 +111,9 @@ def run_tests(self): self.output(1, "%s model: Running tests" % model_name) runner.run(dtest, clear_globs=True, out=sys.stdout.write) - # Unless we're using SQLite, remove the test database, to clean up after - # ourselves. Connect to the previous database (not the test database) - # to do so, because it's not allowed to delete a database while being + # Unless we're using SQLite, remove the test database to clean up after + # ourselves. Connect to the previous database (not the test database) + # to do so, because it's not allowed to delete a database while being # connected to it. if settings.DATABASE_ENGINE != "sqlite3": db.close() diff --git a/tests/testapp/models/lookup.py b/tests/testapp/models/lookup.py index 8eb0032036a98..6e8f61c22589e 100644 --- a/tests/testapp/models/lookup.py +++ b/tests/testapp/models/lookup.py @@ -1,6 +1,7 @@ """ 7. The lookup API +This demonstrates features of the database API. """ from django.core import meta