Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

[multi-db] Merge trunk to [3661]

  • Loading branch information...
commit b17f250907351923f39f8a50b87a35b26d2ca307 1 parent 5a58772
@jpellerin jpellerin authored
Showing with 2,425 additions and 4,498 deletions.
  1. +2 −0  AUTHORS
  2. +4 −1 django/bin/compile-messages.py
  3. +15 −1 django/conf/global_settings.py
  4. +7 −3 django/contrib/admin/views/doc.py
  5. +1 −1  django/contrib/auth/models.py
  6. +90 −0 django/contrib/sitemaps/__init__.py
  7. +11 −0 django/contrib/sitemaps/templates/sitemap.xml
  8. +8 −0 django/contrib/sitemaps/templates/sitemap_index.xml
  9. +30 −0 django/contrib/sitemaps/views.py
  10. +6 −3 django/core/management.py
  11. +29 −25 django/core/serializers/base.py
  12. +1 −1  django/core/serializers/json.py
  13. +1 −4 django/db/backends/postgresql_psycopg2/base.py
  14. +1 −1  django/db/backends/util.py
  15. +2 −10 django/template/__init__.py
  16. +1 −1  django/template/defaulttags.py
  17. +3 −5 django/template/loader.py
  18. +1 −1  django/template/loader_tags.py
  19. +2 −1  django/test/simple.py
  20. +43 −20 django/test/utils.py
  21. +17 −11 django/utils/datastructures.py
  22. +3 −3 django/views/debug.py
  23. +1 −1  django/views/static.py
  24. +9 −0 docs/add_ons.txt
  25. +13 −0 docs/contributing.txt
  26. +29 −0 docs/django-admin.txt
  27. +3 −3 docs/faq.txt
  28. +24 −0 docs/settings.txt
  29. +320 −0 docs/sitemaps.txt
  30. +11 −0 docs/sites.txt
  31. +14 −5 docs/templates_python.txt
  32. +303 −0 docs/testing.txt
  33. +3 −0  setup.cfg
  34. +0 −2,665 tests/doctest.py
  35. +5 −6 tests/modeltests/basic/models.py
  36. +2 −2 tests/modeltests/choices/models.py
  37. +2 −2 tests/modeltests/custom_columns/models.py
  38. +2 −2 tests/modeltests/custom_managers/models.py
  39. +2 −2 tests/modeltests/custom_methods/models.py
  40. +2 −2 tests/modeltests/custom_pk/models.py
  41. +2 −2 tests/modeltests/empty/models.py
  42. +2 −2 tests/modeltests/field_defaults/models.py
  43. +2 −2 tests/modeltests/generic_relations/models.py
  44. +6 −6 tests/modeltests/get_latest/models.py
  45. +2 −2 tests/modeltests/get_or_create/models.py
  46. +1 −1  tests/modeltests/invalid_models/models.py
  47. +2 −2 tests/modeltests/lookup/models.py
  48. +2 −2 tests/modeltests/m2m_and_m2o/models.py
  49. +2 −2 tests/modeltests/m2m_intermediary/models.py
  50. +2 −2 tests/modeltests/m2m_multiple/models.py
  51. +2 −2 tests/modeltests/m2m_recursive/models.py
  52. +2 −2 tests/modeltests/m2o_recursive/models.py
  53. +2 −2 tests/modeltests/m2o_recursive2/models.py
  54. +2 −2 tests/modeltests/manipulators/models.py
  55. +2 −2 tests/modeltests/many_to_many/models.py
  56. +2 −2 tests/modeltests/many_to_one/models.py
  57. +2 −2 tests/modeltests/many_to_one_null/models.py
  58. +2 −2 tests/modeltests/model_inheritance/models.py
  59. +18 −23 tests/modeltests/multiple_databases/models.py
  60. +2 −2 tests/modeltests/mutually_referential/models.py
  61. +2 −2 tests/modeltests/one_to_one/models.py
  62. +2 −2 tests/modeltests/or_lookups/models.py
  63. +2 −2 tests/modeltests/ordering/models.py
  64. +2 −2 tests/modeltests/pagination/models.py
  65. +2 −2 tests/modeltests/properties/models.py
  66. +2 −2 tests/modeltests/reserved_names/models.py
  67. +2 −2 tests/modeltests/reverse_lookup/models.py
  68. +2 −2 tests/modeltests/save_delete_hooks/models.py
  69. +2 −2 tests/modeltests/serializers/models.py
  70. +2 −2 tests/modeltests/str/models.py
  71. +3 −3 tests/modeltests/transactions/models.py
  72. +2 −2 tests/modeltests/validation/models.py
  73. +0 −60 tests/othertests/cache.py
  74. +0 −23 tests/othertests/manager_db.py
  75. +0 −70 tests/othertests/markup.py
  76. +0 −140 tests/othertests/request_isolation.py
  77. +0 −634 tests/othertests/templates.py
  78. +0 −300 tests/othertests/thread_isolation.py
  79. 0  tests/{othertests → regressiontests/ansi_sql}/__init__.py
  80. +27 −36 tests/{othertests/ansi_sql.py → regressiontests/ansi_sql/models.py}
  81. 0  tests/{othertests → regressiontests/ansi_sql}/sql/car.sql
  82. 0  tests/regressiontests/cache/__init__.py
  83. 0  tests/regressiontests/cache/models.py
  84. +71 −0 tests/regressiontests/cache/tests.py
  85. 0  tests/regressiontests/dateformat/__init__.py
  86. 0  tests/regressiontests/dateformat/models.py
  87. 0  tests/{othertests/dateformat.py → regressiontests/dateformat/tests.py}
  88. 0  tests/regressiontests/db_typecasts/__init__.py
  89. 0  tests/regressiontests/db_typecasts/models.py
  90. +10 −5 tests/{othertests/db_typecasts.py → regressiontests/db_typecasts/tests.py}
  91. 0  tests/regressiontests/defaultfilters/__init__.py
  92. 0  tests/regressiontests/defaultfilters/models.py
  93. 0  tests/{othertests/defaultfilters.py → regressiontests/defaultfilters/tests.py}
  94. 0  tests/regressiontests/httpwrappers/__init__.py
  95. 0  tests/regressiontests/httpwrappers/models.py
  96. 0  tests/{othertests/httpwrappers.py → regressiontests/httpwrappers/tests.py}
  97. +1 −1  tests/regressiontests/initial_sql_regress/models.py
  98. 0  tests/regressiontests/manager_db/__init__.py
  99. +5 −0 tests/regressiontests/manager_db/models.py
  100. +17 −0 tests/regressiontests/manager_db/tests.py
  101. 0  tests/regressiontests/manager_schema_manipulation/__init__.py
  102. 0  tests/regressiontests/manager_schema_manipulation/models.py
  103. +5 −5 tests/{othertests/manager_schema_manipulation.py → regressiontests/manager_schema_manipulation/tests.py}
  104. +1 −1  tests/regressiontests/many_to_one_regress/models.py
  105. 0  tests/regressiontests/markup/__init__.py
  106. 0  tests/regressiontests/markup/models.py
  107. +69 −0 tests/regressiontests/markup/tests.py
  108. +2 −2 tests/regressiontests/one_to_one_regress/models.py
  109. 0  tests/regressiontests/request_isolation/__init__.py
  110. +13 −0 tests/regressiontests/request_isolation/models.py
  111. +100 −0 tests/regressiontests/request_isolation/tests.py
  112. +2 −2 tests/regressiontests/string_lookup/models.py
  113. 0  tests/regressiontests/templates/__init__.py
  114. 0  tests/regressiontests/templates/models.py
  115. +621 −0 tests/regressiontests/templates/tests.py
  116. 0  tests/regressiontests/thread_isolation/__init__.py
  117. +19 −0 tests/regressiontests/thread_isolation/models.py
  118. +251 −0 tests/regressiontests/thread_isolation/tests.py
  119. 0  tests/regressiontests/urlpatterns_reverse/__init__.py
  120. 0  tests/regressiontests/urlpatterns_reverse/models.py
  121. +10 −18 tests/{othertests/urlpatterns_reverse.py → regressiontests/urlpatterns_reverse/tests.py}
  122. +94 −330 tests/runtests.py
View
2  AUTHORS
@@ -126,6 +126,7 @@ answer newbie questions, and generally made Django that much better:
Oliver Rutherfurd <http://rutherfurd.net/>
Ivan Sagalaev (Maniac) <http://www.softwaremaniacs.org/>
David Schein
+ Pete Shinners <pete@shinners.org>
sopel
Thomas Steinacher <tom@eggdrop.ch>
Radek Švarz <http://www.svarz.cz/translate/>
@@ -138,6 +139,7 @@ answer newbie questions, and generally made Django that much better:
Amit Upadhyay
Geert Vanderkelen
Milton Waddams
+ Dan Watson <http://theidioteque.net/>
Rachel Willmer <http://www.willmer.com/kb/>
wojtek
ye7cakf02@sneakemail.com
View
5 django/bin/compile-messages.py
@@ -26,7 +26,10 @@ def compile_messages():
# See http://cyberelk.net/tim/articles/cmdline/ar01s02.html
os.environ['djangocompilemo'] = pf + '.mo'
os.environ['djangocompilepo'] = pf + '.po'
- cmd = 'msgfmt -o "$djangocompilemo" "$djangocompilepo"'
+ if sys.platform == 'win32': # Different shell-variable syntax
+ cmd = 'msgfmt -o "%djangocompilemo%" "%djangocompilepo%"'
+ else:
+ cmd = 'msgfmt -o "$djangocompilemo" "$djangocompilepo"'
os.system(cmd)
if __name__ == "__main__":
View
16 django/conf/global_settings.py
@@ -304,4 +304,18 @@
# TESTING #
###########
-TEST_RUNNER='django.test.simple.run_tests'
+# The name of the method to use to invoke the test suite
+TEST_RUNNER = 'django.test.simple.run_tests'
+
+# The name of the database to use for testing purposes.
+# If None, a name of 'test_' + DATABASE_NAME will be assumed
+TEST_DATABASE_NAME = None
+
+# Tuple of other test databases to create. Names in this tuple
+# are suffixes that will be appended to TEST_DATABASE_NAME
+TEST_DATABASES = []
+
+# Models to assign to each test database. This must be a list of
+# dicts, with each dict key being a name from TEST_DATABASES and value
+# a list of models or app_labels that will use that database.
+TEST_DATABASE_MODELS = []
View
10 django/contrib/admin/views/doc.py
@@ -328,13 +328,17 @@ def extract_views_from_urlpatterns(urlpatterns, base=''):
"""
views = []
for p in urlpatterns:
- if hasattr(p, 'get_callback'):
+ if hasattr(p, '_get_callback'):
try:
- views.append((p.get_callback(), base + p.regex.pattern))
+ views.append((p._get_callback(), base + p.regex.pattern))
except ViewDoesNotExist:
continue
elif hasattr(p, '_get_url_patterns'):
- views.extend(extract_views_from_urlpatterns(p.url_patterns, base + p.regex.pattern))
+ try:
+ patterns = p.url_patterns
+ except ImportError:
+ continue
+ views.extend(extract_views_from_urlpatterns(patterns, base + p.regex.pattern))
else:
raise TypeError, _("%s does not appear to be a urlpattern object") % p
return views
View
2  django/contrib/auth/models.py
@@ -33,7 +33,7 @@ class Permission(models.Model):
Permissions are set globally per type of object, not per specific object instance. It is possible to say "Mary may change news stories," but it's not currently possible to say "Mary may change news stories, but only the ones she created herself" or "Mary may only change news stories that have a certain status or publication date."
- Three basic permissions -- add, create and delete -- are automatically created for each Django model.
+ Three basic permissions -- add, change and delete -- are automatically created for each Django model.
"""
name = models.CharField(_('name'), maxlength=50)
content_type = models.ForeignKey(ContentType)
View
90 django/contrib/sitemaps/__init__.py
@@ -0,0 +1,90 @@
+from django.core import urlresolvers
+import urllib
+
+PING_URL = "http://www.google.com/webmasters/sitemaps/ping"
+
+class SitemapNotFound(Exception):
+ pass
+
+def ping_google(sitemap_url=None, ping_url=PING_URL):
+ """
+ Alerts Google that the sitemap for the current site has been updated.
+ If sitemap_url is provided, it should be an absolute path to the sitemap
+ for this site -- e.g., '/sitemap.xml'. If sitemap_url is not provided, this
+ function will attempt to deduce it by using urlresolvers.reverse().
+ """
+ if sitemap_url is None:
+ try:
+ # First, try to get the "index" sitemap URL.
+ sitemap_url = urlresolvers.reverse('django.contrib.sitemap.views.index')
+ except urlresolvers.NoReverseMatch:
+ try:
+ # Next, try for the "global" sitemap URL.
+ sitemap_url = urlresolvers.reverse('django.contrib.sitemap.views.sitemap')
+ except urlresolvers.NoReverseMatch:
+ pass
+
+ if sitemap_url is None:
+ raise SitemapNotFound("You didn't provide a sitemap_url, and the sitemap URL couldn't be auto-detected.")
+
+ from django.contrib.sites.models import Site
+ current_site = Site.objects.get_current()
+ url = "%s%s" % (current_site.domain, sitemap)
+ params = urllib.urlencode({'sitemap':url})
+ urllib.urlopen("%s?%s" % (ping_url, params))
+
+class Sitemap:
+ def __get(self, name, obj, default=None):
+ try:
+ attr = getattr(self, name)
+ except AttributeError:
+ return default
+ if callable(attr):
+ return attr(obj)
+ return attr
+
+ def items(self):
+ return []
+
+ def location(self, obj):
+ return obj.get_absolute_url()
+
+ def get_urls(self):
+ from django.contrib.sites.models import Site
+ current_site = Site.objects.get_current()
+ urls = []
+ for item in self.items():
+ loc = "http://%s%s" % (current_site.domain, self.__get('location', item))
+ url_info = {
+ 'location': loc,
+ 'lastmod': self.__get('lastmod', item, None),
+ 'changefreq': self.__get('changefreq', item, None),
+ 'priority': self.__get('priority', item, None)
+ }
+ urls.append(url_info)
+ return urls
+
+class FlatPageSitemap(Sitemap):
+ def items(self):
+ from django.contrib.sites.models import Site
+ current_site = Site.objects.get_current()
+ return current_site.flatpage_set.all()
+
+class GenericSitemap(Sitemap):
+ priority = None
+ changefreq = None
+
+ def __init__(self, info_dict, priority=None, changefreq=None):
+ self.queryset = info_dict['queryset']
+ self.date_field = info_dict.get('date_field', None)
+ self.priority = priority
+ self.changefreq = changefreq
+
+ def items(self):
+ # Make sure to return a clone; we don't want premature evaluation.
+ return self.queryset.filter()
+
+ def lastmod(self, item):
+ if self.date_field is not None:
+ return getattr(item, self.date_field)
+ return None
View
11 django/contrib/sitemaps/templates/sitemap.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<urlset xmlns="http://www.google.com/schemas/sitemap/0.84">
+{% for url in urlset %}
+ <url>
+ <loc>{{ url.location|escape }}</loc>
+ {% if url.lastmod %}<lastmod>{{ url.lastmod|date:"Y-m-d" }}</lastmod>{% endif %}
+ {% if url.changefreq %}<changefreq>{{ url.changefreq }}</changefreq>{% endif %}
+ {% if url.priority %}<priority>{{ url.priority }}</priority>{% endif %}
+ </url>
+{% endfor %}
+</urlset>
View
8 django/contrib/sitemaps/templates/sitemap_index.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<sitemapindex xmlns="http://www.google.com/schemas/sitemap/0.84">
+{% for location in sitemaps %}
+ <sitemap>
+ <loc>{{ location|escape }}</loc>
+ </sitemap>
+{% endfor %}
+</sitemapindex>
View
30 django/contrib/sitemaps/views.py
@@ -0,0 +1,30 @@
+from django.http import HttpResponse, Http404
+from django.template import loader
+from django.contrib.sites.models import Site
+from django.core import urlresolvers
+
+def index(request, sitemaps):
+ current_site = Site.objects.get_current()
+ sites = []
+ protocol = request.is_secure() and 'https' or 'http'
+ for section in sitemaps.keys():
+ sitemap_url = urlresolvers.reverse('django.contrib.sitemap.views.sitemap', kwargs={'section': section})
+ sites.append('%s://%s%s' % (protocol, current_site.domain, sitemap_url))
+ xml = loader.render_to_string('sitemap_index.xml', {'sitemaps': sites})
+ return HttpResponse(xml, mimetype='application/xml')
+
+def sitemap(request, sitemaps, section=None):
+ maps, urls = [], []
+ if section is not None:
+ if not sitemaps.has_key(section):
+ raise Http404("No sitemap available for section: %r" % section)
+ maps.append(sitemaps[section])
+ else:
+ maps = sitemaps.values()
+ for site in maps:
+ if callable(site):
+ urls.extend(site().get_urls())
+ else:
+ urls.extend(site.get_urls())
+ xml = loader.render_to_string('sitemap.xml', {'urlset': urls})
+ return HttpResponse(xml, mimetype='application/xml')
View
9 django/core/management.py
@@ -294,8 +294,7 @@ def syncdb(verbosity=2, interactive=True):
except ImportError:
pass
- # Send the post_syncdb signal, so individual apps can do whatever they need
- # to do at this point.
+ # Install each app
for app in models.get_apps():
# Install each application (models already installed will be skipped)
created = _install(app, commit=False, initial_data=False)
@@ -303,10 +302,14 @@ def syncdb(verbosity=2, interactive=True):
for model in created:
print "Created table %s" % model._meta.db_table
created_models.extend(created)
+ transaction.commit_unless_managed()
+
+ # Send the post_syncdb signal, so individual apps can do whatever they need
+ # to do at this point.
+ for app in models.get_apps():
dispatcher.send(signal=signals.post_syncdb, sender=app,
app=app, created_models=created_models,
verbosity=verbosity, interactive=interactive)
- transaction.commit_unless_managed()
# Install initial data for the app (but only if this is a model we've
# just created)
View
54 django/core/serializers/base.py
@@ -11,7 +11,7 @@
class SerializationError(Exception):
"""Something bad happened during serialization."""
pass
-
+
class DeserializationError(Exception):
"""Something bad happened during deserialization."""
pass
@@ -20,15 +20,15 @@ class Serializer(object):
"""
Abstract serializer base class.
"""
-
+
def serialize(self, queryset, **options):
"""
Serialize a queryset.
"""
self.options = options
-
+
self.stream = options.get("stream", StringIO())
-
+
self.start_serialization()
for obj in queryset:
self.start_object(obj)
@@ -44,61 +44,65 @@ def serialize(self, queryset, **options):
self.end_object(obj)
self.end_serialization()
return self.getvalue()
-
+
def get_string_value(self, obj, field):
"""
Convert a field's value to a string.
"""
if isinstance(field, models.DateTimeField):
- value = getattr(obj, field.name).strftime("%Y-%m-%d %H:%M:%S")
+ value = getattr(obj, field.name)
+ if value is None:
+ value = ''
+ else:
+ value = value.strftime("%Y-%m-%d %H:%M:%S")
elif isinstance(field, models.FileField):
value = getattr(obj, "get_%s_url" % field.name, lambda: None)()
else:
value = field.flatten_data(follow=None, obj=obj).get(field.name, "")
return str(value)
-
+
def start_serialization(self):
"""
Called when serializing of the queryset starts.
"""
raise NotImplementedError
-
+
def end_serialization(self):
"""
Called when serializing of the queryset ends.
"""
pass
-
+
def start_object(self, obj):
"""
Called when serializing of an object starts.
"""
raise NotImplementedError
-
+
def end_object(self, obj):
"""
Called when serializing of an object ends.
"""
pass
-
+
def handle_field(self, obj, field):
"""
Called to handle each individual (non-relational) field on an object.
"""
raise NotImplementedError
-
+
def handle_fk_field(self, obj, field):
"""
Called to handle a ForeignKey field.
"""
raise NotImplementedError
-
+
def handle_m2m_field(self, obj, field):
"""
Called to handle a ManyToManyField.
"""
raise NotImplementedError
-
+
def getvalue(self):
"""
Return the fully serialized queryset.
@@ -109,7 +113,7 @@ class Deserializer(object):
"""
Abstract base deserializer class.
"""
-
+
def __init__(self, stream_or_string, **options):
"""
Init this serializer given a stream or a string
@@ -123,39 +127,39 @@ def __init__(self, stream_or_string, **options):
# deserialization starts (otherwise subclass calls to get_model()
# and friends might fail...)
models.get_apps()
-
+
def __iter__(self):
return self
-
+
def next(self):
"""Iteration iterface -- return the next item in the stream"""
raise NotImplementedError
-
+
class DeserializedObject(object):
"""
A deserialzed model.
-
+
Basically a container for holding the pre-saved deserialized data along
with the many-to-many data saved with the object.
-
+
Call ``save()`` to save the object (with the many-to-many data) to the
database; call ``save(save_m2m=False)`` to save just the object fields
(and not touch the many-to-many stuff.)
"""
-
+
def __init__(self, obj, m2m_data=None):
self.object = obj
self.m2m_data = m2m_data
-
+
def __repr__(self):
return "<DeserializedObject: %s>" % str(self.object)
-
+
def save(self, save_m2m=True):
self.object.save()
if self.m2m_data and save_m2m:
for accessor_name, object_list in self.m2m_data.items():
setattr(self.object, accessor_name, object_list)
-
- # prevent a second (possibly accidental) call to save() from saving
+
+ # prevent a second (possibly accidental) call to save() from saving
# the m2m data twice.
self.m2m_data = None
View
2  django/core/serializers/json.py
@@ -48,4 +48,4 @@ def default(self, o):
elif isinstance(o, datetime.time):
return o.strftime(self.TIME_FORMAT)
else:
- return super(self, DateTimeAwareJSONEncoder).default(o)
+ return super(DateTimeAwareJSONEncoder, self).default(o)
View
5 django/db/backends/postgresql_psycopg2/base.py
@@ -11,10 +11,6 @@
from django.core.exceptions import ImproperlyConfigured
raise ImproperlyConfigured, "Error loading psycopg2 module: %s" % e
-# Register Unicode conversions
-import psycopg2.extensions
-psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
-
DatabaseError = Database.DatabaseError
try:
@@ -47,6 +43,7 @@ def cursor(self):
self.connection = Database.connect(conn_string)
self.connection.set_isolation_level(1) # make transactions transparent to all cursors
cursor = self.connection.cursor()
+ cursor.tzinfo_factory = None
cursor.execute("SET TIME ZONE %s", [settings.TIME_ZONE])
if settings.DEBUG:
return util.CursorDebugWrapper(cursor, self)
View
2  django/db/backends/util.py
@@ -98,7 +98,7 @@ def rev_typecast_boolean(obj, d):
def _dict_helper(desc, row):
"Returns a dictionary for the given cursor.description and result row."
- return dict([(desc[col[0]][0], col[1]) for col in enumerate(row)])
+ return dict(zip([col[0] for col in desc], row))
def dictfetchone(cursor):
"Returns a row from the cursor as a dict"
View
12 django/template/__init__.py
@@ -60,8 +60,6 @@
from django.template.context import Context, RequestContext, ContextPopException
from django.utils.functional import curry
from django.utils.text import smart_split
-from django.dispatch import dispatcher
-from django.template import signals
__all__ = ('Template', 'Context', 'RequestContext', 'compile_string')
@@ -139,14 +137,13 @@ def reload(self):
return self.source
class Template(object):
- def __init__(self, template_string, origin=None, name='<Unknown Template>'):
+ def __init__(self, template_string, origin=None):
"Compilation stage"
if settings.TEMPLATE_DEBUG and origin == None:
origin = StringOrigin(template_string)
# Could do some crazy stack-frame stuff to record where this string
# came from...
self.nodelist = compile_string(template_string, origin)
- self.name = name
def __iter__(self):
for node in self.nodelist:
@@ -155,7 +152,6 @@ def __iter__(self):
def render(self, context):
"Display stage -- can be called many times"
- dispatcher.send(signal=signals.template_rendered, sender=self, template=self, context=context)
return self.nodelist.render(context)
def compile_string(template_string, origin):
@@ -618,11 +614,7 @@ def resolve_variable(path, context):
(The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.')
"""
- if path == 'False':
- current = False
- elif path == 'True':
- current = True
- elif path[0].isdigit():
+ if path[0].isdigit():
number_type = '.' in path and float or int
try:
current = number_type(path)
View
2  django/template/defaulttags.py
@@ -251,7 +251,7 @@ def render(self, context):
output = ''
if self.parsed:
try:
- t = Template(output, name=self.filepath)
+ t = Template(output)
return t.render(context)
except TemplateSyntaxError, e:
if settings.DEBUG:
View
8 django/template/loader.py
@@ -76,16 +76,14 @@ def get_template(template_name):
Returns a compiled Template object for the given template name,
handling template inheritance recursively.
"""
- source, origin = find_template_source(template_name)
- template = get_template_from_string(source, origin, template_name)
- return template
+ return get_template_from_string(*find_template_source(template_name))
-def get_template_from_string(source, origin=None, name=None):
+def get_template_from_string(source, origin=None):
"""
Returns a compiled Template object for the given template code,
handling template inheritance recursively.
"""
- return Template(source, origin, name)
+ return Template(source, origin)
def render_to_string(template_name, dictionary=None, context_instance=None):
"""
View
2  django/template/loader_tags.py
@@ -57,7 +57,7 @@ def get_parent(self, context):
except TemplateDoesNotExist:
raise TemplateSyntaxError, "Template %r cannot be extended, because it doesn't exist" % parent
else:
- return get_template_from_string(source, origin, parent)
+ return get_template_from_string(source, origin)
def render(self, context):
compiled_parent = self.get_parent(context)
View
3  django/test/simple.py
@@ -61,7 +61,8 @@ def run_tests(module_list, verbosity=1, extra_tests=[]):
for test in extra_tests:
suite.addTest(test)
- old_name = create_test_db(verbosity)
+ old_name = settings.DATABASE_NAME
+ create_test_db(verbosity)
management.syncdb(verbosity, interactive=False)
unittest.TextTestRunner(verbosity=verbosity).run(suite)
destroy_test_db(old_name, verbosity)
View
63 django/test/utils.py
@@ -1,6 +1,6 @@
import sys, time
from django.conf import settings
-from django.db import connection, transaction
+from django.db import backend, connect, connection, connection_info, connections
# The prefix to put on the default database name when creating
# the test database.
@@ -12,67 +12,90 @@ def _set_autocommit(connection):
connection.connection.autocommit(True)
elif hasattr(connection.connection, "set_isolation_level"):
connection.connection.set_isolation_level(0)
-
+
def create_test_db(verbosity=1, autoclobber=False):
if verbosity >= 1:
print "Creating test database..."
+
# If we're using SQLite, it's more convenient to test against an
# in-memory database.
if settings.DATABASE_ENGINE == "sqlite3":
TEST_DATABASE_NAME = ":memory:"
+ if verbosity >= 2:
+ print "Using in-memory sqlite database for testing"
else:
- TEST_DATABASE_NAME = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
-
+ if settings.TEST_DATABASE_NAME:
+ TEST_DATABASE_NAME = settings.TEST_DATABASE_NAME
+ else:
+ TEST_DATABASE_NAME = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
+
+ qn = backend.quote_name
# Create the test database and connect to it. We need to autocommit
# if the database supports it because PostgreSQL doesn't allow
# CREATE/DROP DATABASE statements within transactions.
cursor = connection.cursor()
_set_autocommit(connection)
try:
- cursor.execute("CREATE DATABASE %s" % TEST_DATABASE_NAME)
+ cursor.execute("CREATE DATABASE %s" % qn(db_name))
except Exception, e:
sys.stderr.write("Got an error creating the test database: %s\n" % e)
if not autoclobber:
- confirm = raw_input("It appears the test database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_DATABASE_NAME)
+ confirm = raw_input("It appears the test database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % db_name)
if autoclobber or confirm == 'yes':
try:
if verbosity >= 1:
print "Destroying old test database..."
- cursor.execute("DROP DATABASE %s" % TEST_DATABASE_NAME)
+ cursor.execute("DROP DATABASE %s" % qn(db_name))
if verbosity >= 1:
print "Creating test database..."
- cursor.execute("CREATE DATABASE %s" % TEST_DATABASE_NAME)
+ cursor.execute("CREATE DATABASE %s" % qn(db_name))
except Exception, e:
sys.stderr.write("Got an error recreating the test database: %s\n" % e)
sys.exit(2)
else:
print "Tests cancelled."
sys.exit(1)
-
- connection.close()
- old_database_name = settings.DATABASE_NAME
- settings.DATABASE_NAME = TEST_DATABASE_NAME
+ # Close the old connection
+ connection.close()
- # Get a cursor (even though we don't need one yet). This has
- # the side effect of initializing the test database.
- cursor = connection.cursor()
-
- return old_database_name
+ # Get a cursor (even though we don't need one yet). This has
+ # the side effect of initializing the test database.
+ cursor = connection.cursor()
-def destroy_test_db(old_database_name, verbosity=1):
+ # Fill OTHER_DATABASES with the TEST_DATABASES settings,
+ # and connect each named connection to the test database, using
+ # a separate connection instance for each (so, eg, transactions don't
+ # collide)
+ test_databases = {}
+ for db_name in settings.TEST_DATABASES:
+ if settings.DATABASE_ENGINE == 'sqlite3':
+ full_name = TEST_DATABASE_NAME
+ else:
+ full_name = TEST_DATABASE_NAME + db_name
+ db_st = {'DATABASE_NAME': full_name}
+ if db_name in settings.TEST_DATABASE_MODELS:
+ db_st['MODELS'] = settings.TEST_DATABASE_MODELS.get(db_name, [])
+ test_databases[db_name] = db_st
+ connections[db_name] = connect(connection_info.settings)
+ connections[db_name].connection.cursor() # Initialize it
+ settings.OTHER_DATABASES = test_databases
+
+def destroy_test_db(old_database_name, old_databases, verbosity=1):
# 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 verbosity >= 1:
print "Destroying test database..."
- if settings.DATABASE_ENGINE != "sqlite3":
+ if settings.DATABASE_ENGINE != "sqlite3":
connection.close()
TEST_DATABASE_NAME = settings.DATABASE_NAME
settings.DATABASE_NAME = old_database_name
+ settings.OTHER_DATABASES = old_databases
cursor = connection.cursor()
_set_autocommit(connection)
time.sleep(1) # To avoid "database is being accessed by other users" errors.
- cursor.execute("DROP DATABASE %s" % TEST_DATABASE_NAME)
+ cursor.execute("DROP DATABASE %s" % backend.quote_name(TEST_DATABASE_NAME))
connection.close()
+
View
28 django/utils/datastructures.py
@@ -187,17 +187,23 @@ def copy(self):
"Returns a copy of this object."
return self.__deepcopy__()
- def update(self, other_dict):
- "update() extends rather than replaces existing key lists."
- if isinstance(other_dict, MultiValueDict):
- for key, value_list in other_dict.lists():
- self.setlistdefault(key, []).extend(value_list)
- else:
- try:
- for key, value in other_dict.items():
- self.setlistdefault(key, []).append(value)
- except TypeError:
- raise ValueError, "MultiValueDict.update() takes either a MultiValueDict or dictionary"
+ def update(self, *args, **kwargs):
+ "update() extends rather than replaces existing key lists. Also accepts keyword args."
+ if len(args) > 1:
+ raise TypeError, "update expected at most 1 arguments, got %d", len(args)
+ if args:
+ other_dict = args[0]
+ if isinstance(other_dict, MultiValueDict):
+ for key, value_list in other_dict.lists():
+ self.setlistdefault(key, []).extend(value_list)
+ else:
+ try:
+ for key, value in other_dict.items():
+ self.setlistdefault(key, []).append(value)
+ except TypeError:
+ raise ValueError, "MultiValueDict.update() takes either a MultiValueDict or dictionary"
+ for key, value in kwargs.iteritems():
+ self.setlistdefault(key, []).append(value)
class DotExpandedDict(dict):
"""
View
6 django/views/debug.py
@@ -115,7 +115,7 @@ def technical_500_response(request, exc_type, exc_value, tb):
'function': '?',
'lineno': '?',
}]
- t = Template(TECHNICAL_500_TEMPLATE, name='Technical 500 Template')
+ t = Template(TECHNICAL_500_TEMPLATE)
c = Context({
'exception_type': exc_type.__name__,
'exception_value': exc_value,
@@ -141,7 +141,7 @@ def technical_404_response(request, exception):
# tried exists but is an empty list. The URLconf must've been empty.
return empty_urlconf(request)
- t = Template(TECHNICAL_404_TEMPLATE, name='Technical 404 Template')
+ t = Template(TECHNICAL_404_TEMPLATE)
c = Context({
'root_urlconf': settings.ROOT_URLCONF,
'urlpatterns': tried,
@@ -154,7 +154,7 @@ def technical_404_response(request, exception):
def empty_urlconf(request):
"Create an empty URLconf 404 error response."
- t = Template(EMPTY_URLCONF_TEMPLATE, name='Empty URLConf Template')
+ t = Template(EMPTY_URLCONF_TEMPLATE)
c = Context({
'project_name': settings.SETTINGS_MODULE.split('.')[0]
})
View
2  django/views/static.py
@@ -81,7 +81,7 @@ def directory_index(path, fullpath):
try:
t = loader.get_template('static/directory_index')
except TemplateDoesNotExist:
- t = Template(DEFAULT_DIRECTORY_INDEX_TEMPLATE, name='Default Directory Index Template')
+ t = Template(DEFAULT_DIRECTORY_INDEX_TEMPLATE)
files = []
for f in os.listdir(fullpath):
if not f.startswith('.'):
View
9 docs/add_ons.txt
@@ -153,6 +153,15 @@ See the `sites documentation`_.
.. _sites documentation: http://www.djangoproject.com/documentation/sites/
+sitemaps
+========
+
+A framework for generating Google sitemap XML files.
+
+See the `sitemaps documentation`_.
+
+.. _sitemaps documentation: http://www.djangoproject.com/documentation/sitemaps/
+
syndication
===========
View
13 docs/contributing.txt
@@ -168,6 +168,19 @@ Please follow these coding standards when writing code for inclusion in Django:
{{foo}}
+ * In Django views, the first parameter in a view function should be called
+ ``request``.
+
+ Do this::
+
+ def my_view(request, foo):
+ # ...
+
+ Don't do this::
+
+ def my_view(req, foo):
+ # ...
+
* Please don't put your name in the code. While we appreciate all
contributions to Django, our policy is not to publish individual
developer names in code -- for instance, at the top of Python modules.
View
29 docs/django-admin.txt
@@ -292,6 +292,13 @@ this command to install the default apps.
If you're installing the ``django.contrib.auth`` application, ``syncdb`` will
give you the option of creating a superuser immediately.
+test
+----
+
+Discover and run tests for all installed models. See `Testing Django applications`_ for more information.
+
+.. _testing django applications: ../testing/
+
validate
--------
@@ -338,6 +345,17 @@ setting the Python path for you.
Displays a help message that includes a terse list of all available actions and
options.
+--noinput
+---------
+
+Inform django-admin that the user should NOT be prompted for any input. Useful if
+the django-admin script will be executed as an unattended, automated script.
+
+--noreload
+----------
+
+Disable the use of the auto-reloader when running the development server.
+
--version
---------
@@ -348,6 +366,17 @@ Example output::
0.9.1
0.9.1 (SVN)
+--verbosity
+-----------
+
+Example usage::
+
+ django-admin.py syncdb --verbosity=2
+
+Verbosity determines the amount of notification and debug information that
+will be printed to the console. '0' is no output, '1' is normal output,
+and `2` is verbose output.
+
Extra niceties
==============
View
6 docs/faq.txt
@@ -103,10 +103,10 @@ Lawrence, Kansas, USA.
On IRC, Simon goes by ``SimonW``.
`Wilson Miner`_
- Wilson's design-fu makes us all look like rock stars. By day, he's an
+ Wilson's design-fu makes us all look like rock stars. By day, he's an
interactive designer for `Apple`. Don't ask him what he's working on, or
he'll have to kill you. He lives in San Francisco.
-
+
On IRC, Wilson goes by ``wilsonian``.
.. _`World Online`: http://code.djangoproject.com/wiki/WorldOnline
@@ -641,7 +641,7 @@ How can I get started contributing code to Django?
Thanks for asking! We've written an entire document devoted to this question.
It's titled `Contributing to Django`_.
-.. _Contributing do Django: http://www.djangoproject.com/documentation/contributing/
+.. _Contributing to Django: http://www.djangoproject.com/documentation/contributing/
I submitted a bug fix in the ticket system several weeks ago. Why are you ignoring my patch?
--------------------------------------------------------------------------------------------
View
24 docs/settings.txt
@@ -754,6 +754,30 @@ misspelled) variables. See `How invalid variables are handled`_.
.. _How invalid variables are handled: http://www.djangoproject.com/documentation/templates_python/#how-invalid-variables-are-handled
+TEST_RUNNER
+-----------
+
+**New in Django development version**
+
+Default: ``'django.test.simple.run_tests'``
+
+The name of the method to use for starting the test suite. See
+`Testing Django Applications`_.
+
+.. _Testing Django Applications: ../testing/
+
+TEST_DATABASE_NAME
+------------------
+
+**New in Django development version**
+
+Default: ``None``
+
+The name of database to use when running the test suite. If a value of
+``None`` is specified, the test database will use the name ``'test_' + settings.DATABASE_NAME``. See `Testing Django Applications`_.
+
+.. _Testing Django Applications: ../testing/
+
TIME_FORMAT
-----------
View
320 docs/sitemaps.txt
@@ -0,0 +1,320 @@
+=====================
+The sitemap framework
+=====================
+
+**New in Django development version**.
+
+Django comes with a high-level sitemap-generating framework that makes
+creating `Google Sitemap`_ XML files easy.
+
+.. _Google Sitemap: http://www.google.com/webmasters/sitemaps/docs/en/protocol.html
+
+Overview
+========
+
+A sitemap is an XML file on your Web site that tells search-engine indexers how
+frequently your pages change and how "important" certain pages are in relation
+to other pages on your site. This information helps search engines index your
+site.
+
+The Django sitemap framework automates the creation of this XML file by letting
+you express this information in Python code.
+
+It works much like Django's `syndication framework`_. To create a sitemap, just
+write a ``Sitemap`` class and point to it in your URLconf_.
+
+.. _syndication framework: http://www.djangoproject.com/documentation/syndication/
+.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
+
+Installation
+============
+
+To install the sitemap app, follow these steps:
+
+ 1. Add ``'django.contrib.sitemaps'`` to your INSTALLED_APPS_ setting.
+ 2. Make sure ``'django.template.loaders.app_directories.load_template_source'``
+ is in your TEMPLATE_LOADERS_ setting. It's in there by default, so
+ you'll only need to change this if you've changed that setting.
+ 3. Make sure you've installed the `sites framework`_.
+
+(Note: The sitemap application doesn't install any database tables. The only
+reason it needs to go into ``INSTALLED_APPS`` is so that the
+``load_template_source`` template loader can find the default templates.)
+
+.. _INSTALLED_APPS: http://www.djangoproject.com/documentation/settings/#installed-apps
+.. _TEMPLATE_LOADERS: http://www.djangoproject.com/documentation/settings/#template-loaders
+.. _sites framework: http://www.djangoproject.com/documentation/sites/
+
+Initialization
+==============
+
+To activate sitemap generation on your Django site, add this line to your
+URLconf_:
+
+ (r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})
+
+This tells Django to build a sitemap when a client accesses ``/sitemap.xml``.
+
+The name of the sitemap file is not important, but the location is. Google will
+only index links in your sitemap for the current URL level and below. For
+instance, if ``sitemap.xml`` lives in your root directory, it may reference any
+URL in your site. However, if your sitemap lives at ``/content/sitemap.xml``,
+it may only reference URLs that begin with ``/content/``.
+
+The sitemap view takes an extra, required argument: ``{'sitemaps': sitemaps}``.
+``sitemaps`` should be a dictionary that maps a short section label (e.g.,
+``blog`` or ``news``) to its ``Sitemap`` class (e.g., ``BlogSitemap`` or
+``NewsSitemap``). It may also map to an *instance* of a ``Sitemap`` class
+(e.g., ``BlogSitemap(some_var)``).
+
+.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
+
+Sitemap classes
+===============
+
+A ``Sitemap`` class is a simple Python class that represents a "section" of
+entries in your sitemap. For example, one ``Sitemap`` class could represent all
+the entries of your weblog, while another could represent all of the events in
+your events calendar.
+
+In the simplest case, all these sections get lumped together into one
+``sitemap.xml``, but it's also possible to use the framework to generate a
+sitemap index that references individual sitemap files, one per section. (See
+`Creating a sitemap index`_ below.)
+
+``Sitemap`` classes must subclass ``django.contrib.sitemaps.Sitemap``. They can
+live anywhere in your codebase.
+
+A simple example
+================
+
+Let's assume you have a blog system, with an ``Entry`` model, and you want your
+sitemap to include all the links to your individual blog entries. Here's how
+your sitemap class might look::
+
+ from django.contrib.sitemaps import Sitemap
+ from mysite.blog.models import Entry
+
+ class BlogSitemap(Sitemap):
+ changefreq = "never"
+ priority = 0.5
+
+ def items(self):
+ return Entry.objects.filter(is_draft=False)
+
+ def lastmod(self, obj):
+ return obj.pub_date
+
+Note:
+
+ * ``changefreq`` and ``priority`` are class attributes corresponding to
+ ``<changefreq>`` and ``<priority>`` elements, respectively. They can be
+ made callable as functions, as ``lastmod`` was in the example.
+ * ``items()`` is simply a method that returns a list of objects. The objects
+ returned will get passed to any callable methods corresponding to a
+ sitemap property (``location``, ``lastmod``, ``changefreq``, and
+ ``priority``).
+ * ``lastmod`` should return a Python ``datetime`` object.
+ * There is no ``location`` method in this example, but you can provide it
+ in order to specify the URL for your object. By default, ``location()``
+ calls ``get_absolute_url()`` on each object and returns the result.
+
+Sitemap class reference
+=======================
+
+A ``Sitemap`` class can define the following methods/attributes:
+
+``items``
+---------
+
+**Required.** A method that returns a list of objects. The framework doesn't
+care what *type* of objects they are; all that matters is that these objects
+get passed to the ``location()``, ``lastmod()``, ``changefreq()`` and
+``priority()`` methods.
+
+``location``
+------------
+
+**Optional.** Either a method or attribute.
+
+If it's a method, it should return the absolute URL for a given object as
+returned by ``items()``.
+
+If it's an attribute, its value should be a string representing an absolute URL
+to use for *every* object returned by ``items()``.
+
+In both cases, "absolute URL" means a URL that doesn't include the protocol or
+domain. Examples:
+
+ * Good: ``'/foo/bar/'``
+ * Bad: ``'example.com/foo/bar/'``
+ * Bad: ``'http://example.com/foo/bar/'``
+
+If ``location`` isn't provided, the framework will call the
+``get_absolute_url()`` method on each object as returned by ``items()``.
+
+``lastmod``
+-----------
+
+**Optional.** Either a method or attribute.
+
+If it's a method, it should take one argument -- an object as returned by
+``items()`` -- and return that object's last-modified date/time, as a Python
+``datetime.datetime`` object.
+
+If it's an attribute, its value should be a Python ``datetime.datetime`` object
+representing the last-modified date/time for *every* object returned by
+``items()``.
+
+``changefreq``
+--------------
+
+**Optional.** Either a method or attribute.
+
+If it's a method, it should take one argument -- an object as returned by
+``items()`` -- and return that object's change frequency, as a Python string.
+
+If it's an attribute, its value should be a string representing the change
+frequency of *every* object returned by ``items()``.
+
+Possible values for ``changefreq``, whether you use a method or attribute, are:
+
+ * ``'always'``
+ * ``'hourly'``
+ * ``'daily'``
+ * ``'weekly'``
+ * ``'monthly'``
+ * ``'yearly'``
+ * ``'never'``
+
+``priority``
+------------
+
+**Optional.** Either a method or attribute.
+
+If it's a method, it should take one argument -- an object as returned by
+``items()`` -- and return that object's priority, as either a string or float.
+
+If it's an attribute, its value should be either a string or float representing
+the priority of *every* object returned by ``items()``.
+
+Example values for ``priority``: ``0.4``, ``1.0``. The default priority of a
+page is ``0.5``. See Google's documentation for more documentation.
+
+.. _Google's documentation: http://www.google.com/webmasters/sitemaps/docs/en/protocol.html
+
+Shortcuts
+=========
+
+The sitemap framework provides a couple convenience classes for common cases:
+
+``FlatPageSitemap``
+-------------------
+
+The ``django.contrib.sitemaps.FlatPageSitemap`` class looks at all flatpages_
+defined for the current ``SITE_ID`` (see the `sites documentation`_) and
+creates an entry in the sitemap. These entries include only the ``location``
+attribute -- not ``lastmod``, ``changefreq`` or ``priority``.
+
+.. _flatpages: http://www.djangoproject.com/documentation/flatpages/
+.. _sites documentation: http://www.djangoproject.com/documentation/sites/
+
+``GenericSitemap``
+------------------
+
+The ``GenericSitemap`` class works with any `generic views`_ you already have.
+To use it, create an instance, passing in the same ``info_dict`` you pass to
+the generic views. The only requirement is that the dictionary have a
+``queryset`` entry. It may also have a ``date_field`` entry that specifies a
+date field for objects retrieved from the ``queryset``. This will be used for
+the ``lastmod`` attribute in the generated sitemap. You may also pass
+``priority`` and ``changefreq`` keyword arguments to the ``GenericSitemap``
+constructor to specify these attributes for all URLs.
+
+.. _generic views: http://www.djangoproject.com/documentation/generic_views/
+
+Example
+-------
+
+Here's an example of a URLconf_ using both::
+
+ from django.conf.urls.defaults import *
+ from django.contrib.sitemaps import FlatPageSitemap, GenericSitemap
+ from mysite.blog.models import Entry
+
+ info_dict = {
+ 'queryset': Entry.objects.all(),
+ 'date_field': 'pub_date',
+ }
+
+ sitemaps = {
+ 'flatpages': FlatPageSitemap,
+ 'blog': GenericSitemap(info_dict, priority=0.6),
+ }
+
+ urlpatterns = patterns('',
+ # some generic view using info_dict
+ # ...
+
+ # the sitemap
+ (r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})
+ )
+
+.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
+
+Creating a sitemap index
+========================
+
+The sitemap framework also has the ability to create a sitemap index that
+references individual sitemap files, one per each section defined in your
+``sitemaps`` dictionary. The only differences in usage are:
+
+ * You use two views in your URLconf: ``django.contrib.sitemaps.views.index``
+ and ``django.contrib.sitemaps.views.sitemap``.
+ * The ``django.contrib.sitemaps.views.sitemap`` view should take a
+ ``section`` keyword argument.
+
+Here is what the relevant URLconf lines would look like for the example above::
+
+ (r'^sitemap.xml$', 'django.contrib.sitemaps.views.index', {'sitemaps': sitemaps})
+ (r'^sitemap-(?P<section>.+).xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})
+
+This will automatically generate a ``sitemap.xml`` file that references
+both ``sitemap-flatpages.xml`` and ``sitemap-blog.xml``. The ``Sitemap``
+classes and the ``sitemaps`` dict don't change at all.
+
+Pinging Google
+==============
+
+You may want to "ping" Google when your sitemap changes, to let it know to
+reindex your site. The framework provides a function to do just that:
+``django.contrib.sitemaps.ping_google()``.
+
+``ping_google()`` takes an optional argument, ``sitemap_url``, which should be
+the absolute URL of your site's sitemap (e.g., ``'/sitemap.xml'``). If this
+argument isn't provided, ``ping_google()`` will attempt to figure out your
+sitemap by performing a reverse looking in your URLconf.
+
+``ping_google()`` raises the exception
+``django.contrib.sitemaps.SitemapNotFound`` if it cannot determine your sitemap
+URL.
+
+One useful way to call ``ping_google()`` is from a model's ``save()`` method::
+
+ from django.contrib.sitemaps import ping_google
+
+ class Entry(models.Model):
+ # ...
+ def save(self):
+ super(Entry, self).save()
+ try:
+ ping_google()
+ except Exception:
+ # Bare 'except' because we could get a variety
+ # of HTTP-related exceptions.
+ pass
+
+A more efficient solution, however, would be to call ``ping_google()`` from a
+cron script, or some other scheduled task. The function makes an HTTP request
+to Google's servers, so you may not want to introduce that network overhead
+each time you call ``save()``.
View
11 docs/sites.txt
@@ -266,7 +266,18 @@ this::
If you attempt to use ``CurrentSiteManager`` and pass a field name that doesn't
exist, Django will raise a ``ValueError``.
+Finally, note that you'll probably want to keep a normal (non-site-specific)
+``Manager`` on your model, even if you use ``CurrentSiteManager``. As explained
+in the `manager documentation`_, if you define a manager manually, then Django
+won't create the automatic ``objects = models.Manager()`` manager for you.
+Also, note that certain parts of Django -- namely, the Django admin site and
+generic views -- use whichever manager is defined *first* in the model, so if
+you want your admin site to have access to all objects (not just site-specific
+ones), put ``objects = models.Manager()`` in your model, before you define
+``CurrentSiteManager``.
+
.. _manager: http://www.djangoproject.com/documentation/model_api/#managers
+.. _manager documentation: http://www.djangoproject.com/documentation/model_api/#managers
How Django uses the sites framework
===================================
View
19 docs/templates_python.txt
@@ -300,13 +300,22 @@ If ``TEMPLATE_CONTEXT_PROCESSORS`` contains this processor, every
* ``user`` -- An ``auth.User`` instance representing the currently
logged-in user (or an ``AnonymousUser`` instance, if the client isn't
logged in). See the `user authentication docs`.
- * ``messages`` -- A list of ``auth.Message`` objects for the currently
- logged-in user.
- * ``perms`` -- An instance of ``django.core.context_processors.PermWrapper``,
- representing the permissions that the currently logged-in user has. See
- the `permissions docs`_.
+
+ * ``messages`` -- A list of messages (as strings) for the currently
+ logged-in user. Behind the scenes, this calls
+ ``request.user.get_and_delete_messages()`` for every request. That method
+ collects the user's messages and deletes them from the database.
+
+ Note that messages are set with ``user.add_message()``. See the
+ `message docs`_ for more.
+
+ * ``perms`` -- An instance of
+ ``django.core.context_processors.PermWrapper``, representing the
+ permissions that the currently logged-in user has. See the `permissions
+ docs`_.
.. _user authentication docs: http://www.djangoproject.com/documentation/authentication/#users
+.. _message docs: http://www.djangoproject.com/documentation/authentication/#messages
.. _permissions docs: http://www.djangoproject.com/documentation/authentication/#permissions
django.core.context_processors.debug
View
303 docs/testing.txt
@@ -0,0 +1,303 @@
+===========================
+Testing Django applications
+===========================
+
+**New in Django development version**.
+
+Automated testing is an extremely useful weapon in the bug-killing arsenal
+of the modern developer. When initially writing code, a test suite can be
+used to validate that code behaves as expected. When refactoring or
+modifying code, tests serve as a guide to ensure that behavior hasn't
+changed unexpectedly as a result of the refactor.
+
+Testing an web application is a complex task, as there are many
+components of a web application that must be validated and tested. To
+help you test your application, Django provides a test execution
+framework, and range of utilities that can be used to stimulate and
+inspect various facets of a web application.
+
+ This testing framework is currently under development, and may change
+ slightly before the next official Django release.
+
+ (That's *no* excuse not to write tests, though!)
+
+Writing tests
+=============
+
+Tests in Django come in two forms: doctests and unit tests.
+
+Writing doctests
+----------------
+
+Doctests use Python's standard doctest_ module, which searches for tests in
+your docstrings. Django's test runner looks for doctests in your ``models.py``
+file, and executes any that it finds. Django will also search for a file
+called ``tests.py`` in the application directory (i.e., the directory that
+holds ``models.py``). If a ``tests.py`` is found, it will also be searched
+for doctests.
+
+.. admonition:: What's a **docstring**?
+
+ A good explanation of docstrings (and some guidlines for using them
+ effectively) can be found in :PEP:`257`:
+
+ A docstring is a string literal that occurs as the first statement in
+ a module, function, class, or method definition. Such a docstring
+ becomes the ``__doc__`` special attribute of that object.
+
+ Since tests often make great documentation, doctest lets you put your
+ tests directly in your docstrings.
+
+You can put doctest strings on any object in your ``models.py``, but it's
+common practice to put application-level doctests in the module docstring, and
+model-level doctests in the docstring for each model.
+
+For example::
+
+ from django.db import model
+
+ class Animal(models.Model):
+ """
+ An animal that knows how to make noise
+
+ # Create some animals
+ >>> lion = Animal.objects.create(name="lion", sound="roar")
+ >>> cat = Animal.objects.create(name="cat", sound="meow")
+
+ # Make 'em speak
+ >>> lion.speak()
+ 'The lion says "roar"'
+ >>> cat.speak()
+ 'The cat says "meow"'
+ """
+
+ name = models.CharField(maxlength=20)
+ sound = models.CharField(maxlength=20)
+
+ def speak(self):
+ return 'The %s says "%s"' % (self.name, self.sound)
+
+When you `run your tests`_, the test utility will find this docstring, notice
+that portions of it look like an interactive Python session, and execute those
+lines while checking that the results match.
+
+For more details about how doctest works, see the `standard library
+documentation for doctest`_
+
+.. _doctest: http://docs.python.org/lib/module-doctest.html
+.. _standard library documentation for doctest: doctest_
+
+Writing unittests
+-----------------
+
+Like doctests, Django's unit tests use a standard library module: unittest_.
+As with doctests, Django's test runner looks for any unit test cases defined
+in ``models.py``, or in a ``tests.py`` file in your application directory.
+
+An equivalent unittest test case for the above example would look like::
+
+ import unittest
+ from myapp.models import Animal
+
+ class AnimalTestCase(unittest.TestCase):
+
+ def setUp(self):
+ self.lion = Animal.objects.create(name="lion", sound="roar")
+ self.cat = Animal.objects.create(name="cat", sound="meow")
+
+ def testSpeaking(self):
+ self.assertEquals(self.lion.speak(), 'The lion says "roar"')
+ self.assertEquals(self.cat.speak(), 'The cat says "meow"')
+
+When you `run your tests`_, the test utility will find all the test cases
+(that is, subclasses of ``unittest.TestCase``) in ``tests.py``, automatically
+build a test suite out of those test cases, and run that suite.
+
+For more details about ``unittest``, see the `standard library unittest
+documentation`_.
+
+.. _unittest: http://docs.python.org/lib/module-unittest.html
+.. _standard library unittest documentation: unittest_
+.. _run your tests: `Running tests`_
+
+Which should I use?
+-------------------
+
+Choosing a test framework is often contentious, so Django simply supports
+both of the standard Python test frameworks. Choosing one is up to each
+developer's personal tastes; each is supported equally. Since each test
+system has different benefits, the best approach is probably to use both
+together, picking the test system to match the type of tests you need to
+write.
+
+For developers new to testing, however, this choice can seem
+confusing, so here are a few key differences to help you decide weather
+doctests or unit tests are right for you.
+
+If you've been using Python for a while, ``doctest`` will probably feel more
+"pythonic". It's designed to make writing tests as easy as possible, so
+there's no overhead of writing classes or methods; you simply put tests in
+docstrings. This gives the added advantage of given your modules automatic
+documentation -- well-written doctests can kill both the documentation and the
+testing bird with a single stone.
+
+For developers just getting started with testing, using doctests will probably
+get you started faster.
+
+The ``unittest`` framework will probably feel very familiar to developers
+coming from Java. Since ``unittest`` is inspired by Java's JUnit, if
+you've used testing frameworks in other languages that similarly were
+inspired by JUnit, ``unittest`` should also feel pretty familiar.
+
+Since ``unittest`` is organized around classes and methods, if you need
+to write a bunch of tests that all share similar code, you can easily use
+subclass to abstract common tasks; this makes test code shorter and cleaner.
+There's also support for explicit setup and/or cleanup routines, which give
+you a high level of control over the environment your test cases run in.
+
+Again, remember that you can use both systems side-by-side (even in the same
+app). In the end, most projects will eventually end up using both; each shines
+in different circumstances.
+
+Testing utilities
+=================
+
+Test Client
+-----------
+
+A dummy browser; instruments the template generation process...
+
+Fixtures
+--------
+
+Feature still to come...
+
+
+Running tests
+=============
+
+Run your tests using your project's ``manage.py`` utility::
+
+ $ ./manage.py test
+
+If you only want to run tests for a particular application, add the
+application name to the command line. For example, if your
+``INSTALLED_APPS`` contains ``myproject.polls`` and ``myproject.animals``,
+but you only want to run the animals unit tests, run::
+
+ $ ./manage.py test animals
+
+When you run your tests, you'll see a bunch of text flow by as the test
+database is created and models are initialized. This test database is
+created from scratch every time you run your tests.
+
+By default, the test database gets its name by prepending ``test_`` to
+the database name specified by the ``DATABASE_NAME`` setting; all other
+database settings will the same as they would be for the project normally.
+If you wish to use a name other than the default for the test database,
+you can use the ``TEST_DATABASE_NAME`` setting to provide a name.
+
+Once the test database has been established, Django will run your tests.
+If everything goes well, at the end you'll see::
+
+ ----------------------------------------------------------------------
+ Ran 22 tests in 0.221s
+
+ OK
+
+If there are test failures, however, you'll see full details about what tests
+failed::
+
+ ======================================================================
+ FAIL: Doctest: ellington.core.throttle.models
+ ----------------------------------------------------------------------
+ Traceback (most recent call last):
+ File "/dev/django/test/doctest.py", line 2153, in runTest
+ raise self.failureException(self.format_failure(new.getvalue()))
+ AssertionError: Failed doctest test for myapp.models
+ File "/dev/myapp/models.py", line 0, in models
+
+ ----------------------------------------------------------------------
+ File "/dev/myapp/models.py", line 14, in myapp.models
+ Failed example:
+ throttle.check("actor A", "action one", limit=2, hours=1)
+ Expected:
+ True
+ Got:
+ False
+
+ ----------------------------------------------------------------------
+ Ran 2 tests in 0.048s
+
+ FAILED (failures=1)
+
+When the tests have all been executed, the test database is destroyed.
+
+Using a different testing framework
+===================================
+
+Doctest and Unittest are not the only Python testing frameworks. While
+Django doesn't provide explicit support these alternative frameworks,
+it does provide a mechanism to allow you to invoke tests constructed for
+an alternative framework as if they were normal Django tests.
+
+When you run ``./manage.py test``, Django looks at the ``TEST_RUNNER``
+setting to determine what to do. By default, ``TEST_RUNNER`` points to ``django.test.simple.run_tests``. This method defines the default Django
+testing behaviour. This behaviour involves:
+
+#. Creating the test database
+#. Running ``syncdb`` to install models and initial data into the test database
+#. Looking for Unit Tests and Doctests in ``models.py`` and ``tests.py`` file for each installed application
+#. Running the Unit Tests and Doctests that are found
+#. Destroying the test database.
+
+If you define your own test runner method and point ``TEST_RUNNER``
+at that method, Django will execute your test runner whenever you run
+``./manage.py test``. In this way, it is possible to use any test
+framework that can be executed from Python code.
+
+Defining a test runner
+----------------------
+By convention, a test runner should be called ``run_tests``; however, you
+can call it anything you want. The only requirement is that it accept two
+arguments:
+
+``run_tests(module_list, verbosity=1)``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The module list is the list of Python modules that contain the models to be
+tested. This is the same format returned by ``django.db.models.get_apps()``
+
+Verbosity determines the amount of notification and debug information that
+will be printed to the console; '0' is no output, '1' is normal output,
+and `2` is verbose output.
+
+Testing utilities
+-----------------
+
+To assist in the creation of your own test runner, Django provides
+a number of utility methods in the ``django.test.utils`` module.
+
+``create_test_db(verbosity=1, autoclobber=False)``:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Creates a new test database, and run ``syncdb`` against it.
+
+``verbosity`` has the same behaviour as in the test runner.
+
+``Autoclobber`` describes the behavior that will occur if a database with
+the same name as the test database is discovered. If ``autoclobber`` is False,
+the user will be asked to approve destroying the existing database. ``sys.exit``
+is called if the user does not approve. If autoclobber is ``True``, the database
+will be destroyed without consulting the user.
+
+``create_test_db()`` has the side effect of modifying
+``settings.DATABASE_NAME`` to match the name of the test database.
+
+``destroy_test_db(old_database_name, verbosity=1)``:
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Destroys the database with the name ``settings.DATABASE_NAME`` matching,
+and restores the value of ``settings.DATABASE_NAME`` to the provided name.
+
+``verbosity`` has the same behaviour as in the test runner.
View
3  setup.cfg
@@ -0,0 +1,3 @@
+[bdist_rpm]
+doc_files = docs/*.txt
+
View
2,665 tests/doctest.py
@@ -1,2665 +0,0 @@
-# Module doctest.
-# Released to the public domain 16-Jan-2001, by Tim Peters (tim@python.org).
-# Major enhancements and refactoring by:
-# Jim Fulton
-# Edward Loper
-
-# Provided as-is; use at your own risk; no warranty; no promises; enjoy!
-
-r"""Module doctest -- a framework for running examples in docstrings.
-
-In simplest use, end each module M to be tested with:
-
-def _test():
- import doctest
- doctest.testmod()
-
-if __name__ == "__main__":
- _test()
-
-Then running the module as a script will cause the examples in the
-docstrings to get executed and verified:
-
-python M.py
-
-This won't display anything unless an example fails, in which case the
-failing example(s) and the cause(s) of the failure(s) are printed to stdout
-(why not stderr? because stderr is a lame hack <0.2 wink>), and the final
-line of output is "Test failed.".
-
-Run it with the -v switch instead:
-
-python M.py -v
-
-and a detailed report of all examples tried is printed to stdout, along
-with assorted summaries at the end.
-
-You can force verbose mode by passing "verbose=True" to testmod, or prohibit
-it by passing "verbose=False". In either of those cases, sys.argv is not
-examined by testmod.
-
-There are a variety of other ways to run doctests, including integration
-with the unittest framework, and support for running non-Python text
-files containing doctests. There are also many ways to override parts
-of doctest's default behaviors. See the Library Reference Manual for
-details.
-"""
-
-__docformat__ = 'reStructuredText en'
-
-__all__ = [
- # 0, Option Flags
- 'register_optionflag',
- 'DONT_ACCEPT_TRUE_FOR_1',
- 'DONT_ACCEPT_BLANKLINE',
- 'NORMALIZE_WHITESPACE',
- 'ELLIPSIS',
- 'IGNORE_EXCEPTION_DETAIL',
- 'COMPARISON_FLAGS',
- 'REPORT_UDIFF',
- 'REPORT_CDIFF',
- 'REPORT_NDIFF',
- 'REPORT_ONLY_FIRST_FAILURE',
- 'REPORTING_FLAGS',
- # 1. Utility Functions
- 'is_private',
- # 2. Example & DocTest
- 'Example',
- 'DocTest',
- # 3. Doctest Parser
- 'DocTestParser',
- # 4. Doctest Finder
- 'DocTestFinder',
- # 5. Doctest Runner
- 'DocTestRunner',
- 'OutputChecker',
- 'DocTestFailure',
- 'UnexpectedException',
- 'DebugRunner',
- # 6. Test Functions
- 'testmod',
- 'testfile',
- 'run_docstring_examples',
- # 7. Tester
- 'Tester',
- # 8. Unittest Support
- 'DocTestSuite',
- 'DocFileSuite',
- 'set_unittest_reportflags',
- # 9. Debugging Support
- 'script_from_examples',
- 'testsource',
- 'debug_src',
- 'debug',
-]
-
-import __future__
-
-import sys, traceback, inspect, linecache, os, re, types
-import unittest, difflib, pdb, tempfile
-import warnings
-from StringIO import StringIO
-
-# Don't whine about the deprecated is_private function in this
-# module's tests.
-warnings.filterwarnings("ignore", "is_private", DeprecationWarning,
- __name__, 0)
-
-# There are 4 basic classes:
-# - Example: a <source, want> pair, plus an intra-docstring line number.
-# - DocTest: a collection of examples, parsed from a docstring, plus
-# info about where the docstring came from (name, filename, lineno).
-# - DocTestFinder: extracts DocTests from a given object's docstring and
-# its contained objects' docstrings.
-# - DocTestRunner: runs DocTest cases, and accumulates statistics.
-#
-# So the basic picture is:
-#
-# list of:
-# +------+ +---------+ +-------+
-# |object| --DocTestFinder-> | DocTest | --DocTestRunner-> |results|
-# +------+ +---------+ +-------+
-# | Example |
-# | ... |
-# | Example |
-# +---------+
-
-# Option constants.
-
-OPTIONFLAGS_BY_NAME = {}
-def register_optionflag(name):
- flag = 1 << len(OPTIONFLAGS_BY_NAME)
- OPTIONFLAGS_BY_NAME[name] = flag
- return flag
-
-DONT_ACCEPT_TRUE_FOR_1 = register_optionflag('DONT_ACCEPT_TRUE_FOR_1')
-DONT_ACCEPT_BLANKLINE = register_optionflag('DONT_ACCEPT_BLANKLINE')
-NORMALIZE_WHITESPACE = register_optionflag('NORMALIZE_WHITESPACE')
-ELLIPSIS = register_optionflag('ELLIPSIS')
-IGNORE_EXCEPTION_DETAIL = register_optionflag('IGNORE_EXCEPTION_DETAIL')
-
-COMPARISON_FLAGS = (DONT_ACCEPT_TRUE_FOR_1 |
- DONT_ACCEPT_BLANKLINE |
- NORMALIZE_WHITESPACE |
- ELLIPSIS |
- IGNORE_EXCEPTION_DETAIL)
-
-REPORT_UDIFF = register_optionflag('REPORT_UDIFF')
-REPORT_CDIFF = register_optionflag('REPORT_CDIFF')
-REPORT_NDIFF = register_optionflag('REPORT_NDIFF')
-REPORT_ONLY_FIRST_FAILURE = register_optionflag('REPORT_ONLY_FIRST_FAILURE')
-
-REPORTING_FLAGS = (REPORT_UDIFF |
- REPORT_CDIFF |
- REPORT_NDIFF |
- REPORT_ONLY_FIRST_FAILURE)
-
-# Special string markers for use in `want` strings:
-BLANKLINE_MARKER = '<BLANKLINE>'
-ELLIPSIS_MARKER = '...'
-
-######################################################################
-## Table of Contents
-######################################################################
-# 1. Utility Functions
-# 2. Example & DocTest -- store test cases
-# 3. DocTest Parser -- extracts examples from strings
-# 4. DocTest Finder -- extracts test cases from objects
-# 5. DocTest Runner -- runs test cases
-# 6. Test Functions -- convenient wrappers for testing
-# 7. Tester Class -- for backwards compatibility
-# 8. Unittest Support
-# 9. Debugging Support
-# 10. Example Usage
-
-######################################################################
-## 1. Utility Functions
-######################################################################
-
-def is_private(prefix, base):
- """prefix, base -> true iff name prefix + "." + base is "private".
-
- Prefix may be an empty string, and base does not contain a period.
- Prefix is ignored (although functions you write conforming to this
- protocol may make use of it).
- Return true iff base begins with an (at least one) underscore, but
- does not both begin and end with (at least) two underscores.
-
- >>> is_private("a.b", "my_func")
- False
- >>> is_private("____", "_my_func")
- True
- >>> is_private("someclass", "__init__")
- False
- >>> is_private("sometypo", "__init_")
- True
- >>> is_private("x.y.z", "_")
- True
- >>> is_private("_x.y.z", "__")
- False
- >>> is_private("", "") # senseless but consistent
- False
- """
- warnings.warn("is_private is deprecated; it wasn't useful; "
- "examine DocTestFinder.find() lists instead",
- DeprecationWarning, stacklevel=2)
- return base[:1] == "_" and not base[:2] == "__" == base[-2:]
-
-def _extract_future_flags(globs):
- """
- Return the compiler-flags associated with the future features that
- have been imported into the given namespace (globs).
- """
- flags = 0
- for fname in __future__.all_feature_names:
- feature = globs.get(fname, None)
- if feature is getattr(__future__, fname):
- flags |= feature.compiler_flag
- return flags
-
-def _normalize_module(module, depth=2):
- """
- Return the module specified by `module`. In particular:
- - If `module` is a module, then return module.
- - If `module` is a string, then import and return the
- module with that name.
- - If `module` is None, then return the calling module.
- The calling module is assumed to be the module of
- the stack frame at the given depth in the call stack.
- """
- if inspect.ismodule(module):
- return module
- elif isinstance(module, (str, unicode)):
- return __import__(module, globals(), locals(), ["*"])
- elif module is None:
- return sys.modules[sys._getframe(depth).f_globals['__name__']]
- else:
- raise TypeError("Expected a module, string, or None")
-
-def _indent(s, indent=4):
- """
- Add the given number of space characters to the beginning every
- non-blank line in `s`, and return the result.
- """
- # This regexp matches the start of non-blank lines:
- return re.sub('(?m)^(?!$)', indent*' ', s)
-
-def _exception_traceback(exc_info):
- """
- Return a string containing a traceback message for the given
- exc_info tuple (as returned by sys.exc_info()).
- """
- # Get a traceback message.
- excout = StringIO()
- exc_type, exc_val, exc_tb = exc_info
- traceback.print_exception(exc_type, exc_val, exc_tb, file=excout)
- return excout.getvalue()
-
-# Override some StringIO methods.
-class _SpoofOut(StringIO):
- def getvalue(self):
- result = StringIO.getvalue(self)
- # If anything at all was written, make sure there's a trailing
- # newline. There's no way for the expected output to indicate
- # that a trailing newline is missing.
- if result and not result.endswith("\n"):
- result += "\n"
- # Prevent softspace from screwing up the next test case, in
- # case they used print with a trailing comma in an example.
- if hasattr(self, "softspace"):
- del self.softspace
- return result
-
- def truncate(self, size=None):
- StringIO.truncate(self, size)
- if hasattr(self, "softspace"):
- del self.softspace
-
-# Worst-case linear-time ellipsis matching.
-def _ellipsis_match(want, got):
- """
- Essentially the only subtle case:
- >>> _ellipsis_match('aa...aa', 'aaa')
- False
- """
- if ELLIPSIS_MARKER not in want:
- return want == got
-
- # Find "the real" strings.
- ws = want.split(ELLIPSIS_MARKER)
- assert len(ws) >= 2
-
- # Deal with exact matches possibly needed at one or both ends.
- startpos, endpos = 0, len(got)
- w = ws[0]
- if w: # starts with exact match
- if got.startswith(w):
- startpos = len(w)
- del ws[0]
- else:
- return False
- w = ws[-1]
- if w: # ends with exact match
- if got.endswith(w):
- endpos -= len(w)
- del ws[-1]
- else:
- return False
-
- if startpos > endpos:
- # Exact end matches required more characters than we have, as in
- # _ellipsis_match('aa...aa', 'aaa')
- return False
-
- # For the rest, we only need to find the leftmost non-overlapping
- # match for each piece. If there's no overall match that way alone,
- # there's no overall match period.
- for w in ws:
- # w may be '' at times, if there are consecutive ellipses, or
- # due to an ellipsis at the start or end of `want`. That's OK.
- # Search for an empty string succeeds, and doesn't change startpos.
- startpos = got.find(w, startpos, endpos)
- if startpos < 0:
- return False
- startpos += len(w)
-
- return True
-
-def _comment_line(line):
- "Return a commented form of the given line"
- line = line.rstrip()
- if line:
- return '# '+line
- else:
- return '#'
-
-class _OutputRedirectingPdb(pdb.Pdb):
- """
- A specialized version of the python debugger that redirects stdout
- to a given stream when interacting with the user. Stdout is *not*
- redirected when traced code is executed.
- """
- def __init__(self, out):
- self.__out = out
- pdb.Pdb.__init__(self)
-
- def trace_dispatch(self, *args):
- # Redirect stdout to the given stream.
- save_stdout = sys.stdout
- sys.stdout = self.__out
- # Call Pdb's trace dispatch method.
- try:
- return pdb.Pdb.trace_dispatch(self, *args)
- finally:
- sys.stdout = save_stdout
-
-# [XX] Normalize with respect to os.path.pardir?
-def _module_relative_path(module, path):
- if not inspect.ismodule(module):
- raise TypeError, 'Expected a module: %r' % module
- if path.startswith('/'):
- raise ValueError, 'Module-relative files may not have absolute paths'
-
- # Find the base directory for the path.
- if hasattr(module, '__file__'):
- # A normal module/package
- basedir = os.path.split(module.__file__)[0]
- elif module.__name__ == '__main__':
- # An interactive session.
- if len(sys.argv)>0 and sys.argv[0] != '':
- basedir = os.path.split(sys.argv[0])[0]
- else:
- basedir = os.curdir
- else:
- # A module w/o __file__ (this includes builtins)
- raise ValueError("Can't resolve paths relative to the module " +
- module + " (it has no __file__)")
-
- # Combine the base directory and the path.
- return os.path.join(basedir, *(path.split('/')))
-
-######################################################################
-## 2. Example & DocTest
-######################################################################
-## - An "example" is a <source, want> pair, where "source" is a
-## fragment of source code, and "want" is the expected output for
-## "source." The Example class also includes information about
-## where the example was extracted from.
-##
-## - A "doctest" is a collection of examples, typically extracted from
-## a string (such as an object's docstring). The DocTest class also
-## includes information about where the string was extracted from.
-
-class Example:
- """
- A single doctest example, consisting of source code and expected
- output. `Example` defines the following attributes:
-
- - source: A single Python statement, always ending with a newline.
- The constructor adds a newline if needed.
-
- - want: The expected output from running the source code (either
- from stdout, or a traceback in case of exception). `want` ends
- with a newline unless it's empty, in which case it's an empty
- string. The constructor adds a newline if needed.
-
- - exc_msg: The exception message generated by the example, if
- the example is expected to generate an exception; or `None` if
- it is not expected to generate an exception. This exception
- message is compared against the return value of
- `traceback.format_exception_only()`. `exc_msg` ends with a
- newline unless it's `None`. The constructor adds a newline
- if needed.
-
- - lineno: The line number within the DocTest string containing
- this Example where the Example begins. This line number is
- zero-based, with respect to the beginning of the DocTest.
-
- - indent: The example's indentation in the DocTest string.
- I.e., the number of space characters that preceed the
- example's first prompt.
-
- - options: A dictionary mapping from option flags to True or
- False, which is used to override default options for this
- example. Any option flags not contained in this dictionary
- are left at their default value (as specified by the
- DocTestRunner's optionflags). By default, no options are set.
- """
- def __init__(self, source, want, exc_msg=None, lineno=0, indent=0,
- options=None):
- # Normalize inputs.
- if not source.endswith('\n'):
- source += '\n'
- if want and not want.endswith('\n'):
- want += '\n'
- if exc_msg is not None and not exc_msg.endswith('\n'):
- exc_msg += '\n'
- # Store properties.
- self.source = source
- self.want = want
- self.lineno = lineno
- self.indent = indent
- if options is None: options = {}
- self.options = options
- self.exc_msg = exc_msg
-
-class DocTest:
- """
- A collection of doctest examples that should be run in a single
- namespace. Each `DocTest` defines the following attributes:
-
- - examples: the list of examples.
-
- - globs: The namespace (aka globals) that the examples should
- be run in.
-
- - name: A name identifying the DocTest (typically, the name of
- the object whose docstring this DocTest was extracted from).
-
- - filename: The name of the file that this DocTest was extracted
- from, or `None` if the filename is unknown.
-
- - lineno: The line number within filename where this DocTest
- begins, or `None` if the line number is unavailable. This
- line number is zero-based, with respect to the beginning of
- the file.
-
- - docstring: The string that the examples were extracted from,
- or `None` if the string is unavailable.
- """
- def __init__(self, examples, globs, name, filename, lineno, docstring):
- """
- Create a new DocTest containing the given examples. The
- DocTest's globals are initialized with a copy of `globs`.
- """
- assert not isinstance(examples, basestring), \
- "DocTest no longer accepts str; use DocTestParser instead"
- self.examples = examples
- self.docstring = docstring
- self.globs = globs.copy()
- self.name = name
- self.filename = filename
- self.lineno = lineno
-
- def __repr__(self):
- if len(self.examples) == 0:
- examples = 'no examples'
- elif len(self.examples) == 1:
- examples = '1 example'
- else:
- examples = '%d examples' % len(self.examples)
- return ('<DocTest %s from %s:%s (%s)>' %
- (self.name, self.filename, self.lineno, examples))
-
-
- # This lets us sort tests by name:
- def __cmp__(self, other):
- if not isinstance(other, DocTest):
- return -1
- return cmp((self.name, self.filename, self.lineno, id(self)),
- (other.name, other.filename, other.lineno, id(other)))
-
-######################################################################
-## 3. DocTestParser
-######################################################################
-