From d34639b09bf85dfb1b6f9a4b59f8ccbc3117230b Mon Sep 17 00:00:00 2001 From: Adrian Holovaty Date: Thu, 7 Jun 2007 21:49:06 +0000 Subject: newforms-admin: Merged to [5439] git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@5440 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- AUTHORS | 2 + django/bin/daily_cleanup.py | 10 +- django/conf/global_settings.py | 8 + django/conf/locale/ca/LC_MESSAGES/django.mo | Bin 48325 -> 49408 bytes django/conf/locale/ca/LC_MESSAGES/django.po | 4028 ++++++++++---------- django/conf/locale/ca/LC_MESSAGES/djangojs.mo | Bin 1556 -> 1646 bytes django/conf/locale/ca/LC_MESSAGES/djangojs.po | 4 +- django/conf/locale/de/LC_MESSAGES/django.mo | Bin 44063 -> 44039 bytes django/conf/locale/de/LC_MESSAGES/django.po | 4 +- django/conf/project_template/settings.py | 5 +- django/conf/urls/defaults.py | 8 +- django/contrib/comments/feeds.py | 9 +- django/core/context_processors.py | 7 + django/core/management.py | 2 +- django/core/serializers/json.py | 2 + django/core/serializers/python.py | 7 +- django/core/serializers/pyyaml.py | 2 + django/core/serializers/xml_serializer.py | 8 +- django/db/backends/dummy/base.py | 5 +- django/db/models/query.py | 20 +- django/middleware/common.py | 2 +- django/newforms/forms.py | 27 +- django/oldforms/__init__.py | 2 +- django/template/__init__.py | 2 +- django/template/defaulttags.py | 4 +- django/test/_doctest.py | 2674 +++++++++++++ django/test/doctest.py | 2669 ------------- django/test/simple.py | 3 +- django/test/testcases.py | 3 +- django/test/utils.py | 24 +- django/utils/feedgenerator.py | 2 + docs/add_ons.txt | 9 +- docs/db-api.txt | 16 +- docs/django-admin.txt | 9 +- docs/fastcgi.txt | 2 +- docs/generic_views.txt | 3 + docs/install.txt | 4 +- docs/model-api.txt | 36 +- docs/modpython.txt | 49 +- docs/newforms.txt | 8 +- docs/serialization.txt | 23 + docs/settings.txt | 53 +- docs/templates.txt | 26 +- docs/templates_python.txt | 12 +- docs/testing.txt | 12 +- docs/tutorial03.txt | 5 +- docs/tutorial04.txt | 28 +- docs/url_dispatch.txt | 35 + tests/modeltests/lookup/models.py | 21 + tests/modeltests/serializers/models.py | 4 + tests/regressiontests/forms/tests.py | 19 + .../regressiontests/serializers_regress/models.py | 7 + tests/regressiontests/serializers_regress/tests.py | 39 + 53 files changed, 5202 insertions(+), 4761 deletions(-) create mode 100644 django/test/_doctest.py delete mode 100644 django/test/doctest.py diff --git a/AUTHORS b/AUTHORS index cd54dbad08..fde35507a0 100644 --- a/AUTHORS +++ b/AUTHORS @@ -142,6 +142,7 @@ answer newbie questions, and generally made Django that much better: Joseph Kocherhans konrad@gwu.edu lakin.wecker@gmail.com + Nick Lane Stuart Langridge Nicola Larosa Eugene Lazutkin @@ -230,6 +231,7 @@ answer newbie questions, and generally made Django that much better: viestards.lists@gmail.com Milton Waddams wam-djangobug@wamber.net + wangchun Dan Watson Chris Wesseling charly.wilhelm@gmail.com diff --git a/django/bin/daily_cleanup.py b/django/bin/daily_cleanup.py index 3b83583d73..c87be1e4c3 100644 --- a/django/bin/daily_cleanup.py +++ b/django/bin/daily_cleanup.py @@ -7,13 +7,13 @@ Can be run as a cronjob to clean out old data from the database (only expired sessions at the moment). """ -from django.db import backend, connection, transaction +import datetime +from django.db import transaction +from django.contrib.sessions.models import Session def clean_up(): - # Clean up old database records - cursor = connection.cursor() - cursor.execute("DELETE FROM %s WHERE %s < NOW()" % \ - (backend.quote_name('django_session'), backend.quote_name('expire_date'))) + """Clean up expired sessions.""" + Session.objects.filter(expire_date__lt=datetime.datetime.now()).delete() transaction.commit_unless_managed() if __name__ == "__main__": diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index 9abc8d6a6c..2be6a4ef95 100644 --- a/django/conf/global_settings.py +++ b/django/conf/global_settings.py @@ -144,6 +144,7 @@ TEMPLATE_CONTEXT_PROCESSORS = ( 'django.core.context_processors.auth', 'django.core.context_processors.debug', 'django.core.context_processors.i18n', + 'django.core.context_processors.media', # 'django.core.context_processors.request', ) @@ -331,6 +332,13 @@ TEST_RUNNER = 'django.test.simple.run_tests' # If None, a name of 'test_' + DATABASE_NAME will be assumed TEST_DATABASE_NAME = None +# Strings used to set the character set and collation order for the test +# database. These values are passed literally to the server, so they are +# backend-dependent. If None, no special settings are sent (system defaults are +# used). +TEST_DATABASE_CHARSET = None +TEST_DATABASE_COLLATION = None + ############ # FIXTURES # ############ diff --git a/django/conf/locale/ca/LC_MESSAGES/django.mo b/django/conf/locale/ca/LC_MESSAGES/django.mo index ca03991f2b..665d7d41a9 100644 Binary files a/django/conf/locale/ca/LC_MESSAGES/django.mo and b/django/conf/locale/ca/LC_MESSAGES/django.mo differ diff --git a/django/conf/locale/ca/LC_MESSAGES/django.po b/django/conf/locale/ca/LC_MESSAGES/django.po index ccef4f8e3b..30a8797a91 100644 --- a/django/conf/locale/ca/LC_MESSAGES/django.po +++ b/django/conf/locale/ca/LC_MESSAGES/django.po @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: django\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2007-05-20 18:25+0200\n" -"PO-Revision-Date: 2007-05-20 18:24+0200\n" +"POT-Creation-Date: 2007-05-25 13:04+0200\n" +"PO-Revision-Date: 2007-05-25 13:04+0200\n" "Last-Translator: Marc Fargas \n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -16,278 +16,302 @@ msgstr "" "X-Generator: VIM 7.0\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: utils/dateformat.py:40 -msgid "p.m." -msgstr "p.m." +#: template/defaultfilters.py:491 +msgid "yes,no,maybe" +msgstr "si,no,potser" -#: utils/dateformat.py:41 -msgid "a.m." -msgstr "a.m." +#: template/defaultfilters.py:520 +#, python-format +msgid "%(size)d byte" +msgid_plural "%(size)d bytes" +msgstr[0] "%(size)d byte" +msgstr[1] "%(size)d bytes" -#: utils/dateformat.py:46 -msgid "PM" -msgstr "PM" +#: template/defaultfilters.py:522 +#, python-format +msgid "%.1f KB" +msgstr "%.1f KB" -#: utils/dateformat.py:47 -msgid "AM" -msgstr "AM" +#: template/defaultfilters.py:524 +#, python-format +msgid "%.1f MB" +msgstr "%.1f MB" -#: utils/dateformat.py:95 -msgid "midnight" -msgstr "mitja nit" +#: template/defaultfilters.py:525 +#, python-format +msgid "%.1f GB" +msgstr "%.1f GB" -#: utils/dateformat.py:97 -msgid "noon" -msgstr "mig dia" +#: newforms/models.py:173 newforms/fields.py:432 +msgid "Select a valid choice. That choice is not one of the available choices." +msgstr "" +"Esculli una opció vàlida; Aquesta opció no és una de les opcions disponibles." -#: utils/dates.py:6 -msgid "Monday" -msgstr "Dilluns" +#: newforms/models.py:186 newforms/fields.py:87 newforms/fields.py:444 +#: newforms/fields.py:520 newforms/fields.py:531 oldforms/__init__.py:357 +#: db/models/fields/__init__.py:121 db/models/fields/__init__.py:278 +#: db/models/fields/__init__.py:675 db/models/fields/__init__.py:686 +msgid "This field is required." +msgstr "Aquest camp és obligatori." -#: utils/dates.py:6 -msgid "Tuesday" -msgstr "Dimarts" +#: newforms/models.py:190 newforms/fields.py:448 newforms/fields.py:524 +msgid "Enter a list of values." +msgstr "Introdueixi una llista de valors." -#: utils/dates.py:6 -msgid "Wednesday" -msgstr "Dimecres" +#: newforms/models.py:196 newforms/fields.py:457 +#, python-format +msgid "Select a valid choice. %s is not one of the available choices." +msgstr "Esculli una opció vàlida; %s' no és una de les opcions vàlides." -#: utils/dates.py:6 -msgid "Thursday" -msgstr "Dijous" +#: newforms/widgets.py:182 contrib/admin/filterspecs.py:150 +#: oldforms/__init__.py:577 +msgid "Unknown" +msgstr "Desconegut" -#: utils/dates.py:6 -msgid "Friday" -msgstr "Divendres" +#: newforms/widgets.py:182 contrib/admin/filterspecs.py:143 +#: oldforms/__init__.py:577 +msgid "Yes" +msgstr "Si" -#: utils/dates.py:7 -msgid "Saturday" -msgstr "Dissabte" +#: newforms/widgets.py:182 contrib/admin/filterspecs.py:143 +#: oldforms/__init__.py:577 +msgid "No" +msgstr "No" -#: utils/dates.py:7 -msgid "Sunday" -msgstr "Diumenge" +#: newforms/fields.py:110 newforms/fields.py:324 +#, python-format +msgid "Ensure this value has at most %d characters." +msgstr "Asseguris de que el seu texte té com a màxim %d caracters." -#: utils/dates.py:14 -msgid "January" -msgstr "Gener" +#: newforms/fields.py:112 newforms/fields.py:326 +#, python-format +msgid "Ensure this value has at least %d characters." +msgstr "Asseguris de que el seu texte té almenys %d caracters." -#: utils/dates.py:14 -msgid "February" -msgstr "Febrer" +#: newforms/fields.py:135 core/validators.py:121 +msgid "Enter a whole number." +msgstr "Introdueixi un número sencer." -#: utils/dates.py:14 utils/dates.py:27 -msgid "March" -msgstr "Març" +#: newforms/fields.py:137 newforms/fields.py:160 newforms/fields.py:192 +#, python-format +msgid "Ensure this value is less than or equal to %s." +msgstr "Aquest valor ha de ser menor o igual a %s." -#: utils/dates.py:14 utils/dates.py:27 -msgid "April" -msgstr "Abril" +#: newforms/fields.py:139 newforms/fields.py:162 newforms/fields.py:194 +#, python-format +msgid "Ensure this value is greater than or equal to %s." +msgstr "Asseguris de que aquest valor sigui superior o igual a %s." -#: utils/dates.py:14 utils/dates.py:27 -msgid "May" -msgstr "Maig" +#: newforms/fields.py:158 newforms/fields.py:186 +msgid "Enter a number." +msgstr "Introdueixi un número." -#: utils/dates.py:14 utils/dates.py:27 -msgid "June" -msgstr "Juny" +#: newforms/fields.py:196 +#, python-format +msgid "Ensure that there are no more than %s digits in total." +msgstr "Asseguris de que no hi ha més de %s dígits en total." -#: utils/dates.py:15 utils/dates.py:27 -msgid "July" -msgstr "Juliol" +#: newforms/fields.py:198 +#, python-format +msgid "Ensure that there are no more than %s decimal places." +msgstr "Asseguris de que no hi ha més de %s decimals." -#: utils/dates.py:15 -msgid "August" -msgstr "Agost" +#: newforms/fields.py:200 +#, python-format +msgid "Ensure that there are no more than %s digits before the decimal point." +msgstr "Asseguris de que no hia ha més de %s dígits decimals." -#: utils/dates.py:15 -msgid "September" -msgstr "Setembre" +#: newforms/fields.py:233 +msgid "Enter a valid date." +msgstr "Introdueixi una data vàlida." -#: utils/dates.py:15 -msgid "October" -msgstr "Octubre" +#: newforms/fields.py:260 +msgid "Enter a valid time." +msgstr "Introdueixi una hora vàlida." -#: utils/dates.py:15 -msgid "November" -msgstr "Novembre" +#: newforms/fields.py:296 +msgid "Enter a valid date/time." +msgstr "Introdueixi una data/hora vàlides." -#: utils/dates.py:16 -msgid "December" -msgstr "Desembre" +#: newforms/fields.py:310 +msgid "Enter a valid value." +msgstr "Introdueixi un valor vàlid." -#: utils/dates.py:19 -msgid "jan" -msgstr "gen" +#: newforms/fields.py:339 core/validators.py:163 +msgid "Enter a valid e-mail address." +msgstr "Introdueixi una adreça de correu vàlida." -#: utils/dates.py:19 -msgid "feb" -msgstr "feb" +#: newforms/fields.py:357 newforms/fields.py:379 +msgid "Enter a valid URL." +msgstr "Introdueixi una URL vàlida." -#: utils/dates.py:19 -msgid "mar" -msgstr "mar" +#: newforms/fields.py:381 +msgid "This URL appears to be a broken link." +msgstr "Aquesta URL sembla ser un enllaç trencat." -#: utils/dates.py:19 -msgid "apr" -msgstr "abr" +#: conf/global_settings.py:39 +msgid "Arabic" +msgstr "Arabic" -#: utils/dates.py:19 -msgid "may" -msgstr "mai" +#: conf/global_settings.py:40 +msgid "Bengali" +msgstr "Bengalí" -#: utils/dates.py:19 -msgid "jun" -msgstr "jun" +#: conf/global_settings.py:41 +msgid "Bulgarian" +msgstr "Bulgar" -#: utils/dates.py:20 -msgid "jul" -msgstr "jul" +#: conf/global_settings.py:42 +msgid "Catalan" +msgstr "Català" -#: utils/dates.py:20 -msgid "aug" -msgstr "ago" +#: conf/global_settings.py:43 +msgid "Czech" +msgstr "Txec" -#: utils/dates.py:20 -msgid "sep" -msgstr "set" +#: conf/global_settings.py:44 +msgid "Welsh" +msgstr "Galès" -#: utils/dates.py:20 -msgid "oct" -msgstr "oct" +#: conf/global_settings.py:45 +msgid "Danish" +msgstr "Danès" -#: utils/dates.py:20 -msgid "nov" -msgstr "nov" +#: conf/global_settings.py:46 +msgid "German" +msgstr "Alemany" -#: utils/dates.py:20 -msgid "dec" -msgstr "des" +#: conf/global_settings.py:47 +msgid "Greek" +msgstr "Grec" -#: utils/dates.py:27 -msgid "Jan." -msgstr "Gen." +#: conf/global_settings.py:48 +msgid "English" +msgstr "Anglès" -#: utils/dates.py:27 -msgid "Feb." -msgstr "Feb." +#: conf/global_settings.py:49 +msgid "Spanish" +msgstr "Espanyol" -#: utils/dates.py:28 -msgid "Aug." -msgstr "Ago." +#: conf/global_settings.py:50 +msgid "Argentinean Spanish" +msgstr "Castellà Argentí" -#: utils/dates.py:28 -msgid "Sept." -msgstr "Set." +#: conf/global_settings.py:51 +msgid "Finnish" +msgstr "Finlandès" -#: utils/dates.py:28 -msgid "Oct." -msgstr "Oct." +#: conf/global_settings.py:52 +msgid "French" +msgstr "Francès" -#: utils/dates.py:28 -msgid "Nov." -msgstr "Nov." +#: conf/global_settings.py:53 +msgid "Galician" +msgstr "Galleg" -#: utils/dates.py:28 -msgid "Dec." -msgstr "Des." +#: conf/global_settings.py:54 +msgid "Hungarian" +msgstr "Húngar" -#: utils/timesince.py:12 -msgid "year" -msgid_plural "years" -msgstr[0] "any" -msgstr[1] "anys" +#: conf/global_settings.py:55 +msgid "Hebrew" +msgstr "Hebreu" -#: utils/timesince.py:13 -msgid "month" -msgid_plural "months" -msgstr[0] "mes" -msgstr[1] "mesos" +#: conf/global_settings.py:56 +msgid "Icelandic" +msgstr "Islandès" -#: utils/timesince.py:14 -msgid "week" -msgid_plural "weeks" -msgstr[0] "setmana" -msgstr[1] "setmanes" +#: conf/global_settings.py:57 +msgid "Italian" +msgstr "Italià" -#: utils/timesince.py:15 -msgid "day" -msgid_plural "days" -msgstr[0] "dia" -msgstr[1] "dies" +#: conf/global_settings.py:58 +msgid "Japanese" +msgstr "Japonès" -#: utils/timesince.py:16 -msgid "hour" -msgid_plural "hours" -msgstr[0] "hora" -msgstr[1] "hores" +#: conf/global_settings.py:59 +msgid "Korean" +msgstr "Coreà" -#: utils/timesince.py:17 -msgid "minute" -msgid_plural "minutes" -msgstr[0] "minut" -msgstr[1] "minuts" +#: conf/global_settings.py:60 +msgid "Kannada" +msgstr "" -#: utils/timesince.py:40 -#, python-format -msgid "%d milliseconds" -msgstr "%d milisegons" +#: conf/global_settings.py:61 +msgid "Latvian" +msgstr "" -#: utils/timesince.py:41 -#, python-format -msgid "%(number)d %(type)s" -msgstr "%(number)d %(type)s" +#: conf/global_settings.py:62 +msgid "Macedonian" +msgstr "Macedoni" -#: utils/timesince.py:47 -#, python-format -msgid ", %(number)d %(type)s" -msgstr ", %(number)d %(type)s" +#: conf/global_settings.py:63 +msgid "Dutch" +msgstr "Holandès" -#: utils/translation/trans_real.py:358 -msgid "DATE_FORMAT" -msgstr "F j, Y" +#: conf/global_settings.py:64 +msgid "Norwegian" +msgstr "Norueg" -#: utils/translation/trans_real.py:359 -msgid "DATETIME_FORMAT" -msgstr "F j, Y, H:i" +#: conf/global_settings.py:65 +msgid "Polish" +msgstr "Polac" -#: utils/translation/trans_real.py:360 -msgid "TIME_FORMAT" -msgstr "H:i" +#: conf/global_settings.py:66 +msgid "Portugese" +msgstr "Portuguès" -#: utils/translation/trans_real.py:376 -msgid "YEAR_MONTH_FORMAT" -msgstr "j de/d' F del Y" +#: conf/global_settings.py:67 +msgid "Brazilian" +msgstr "Brasileny" -#: utils/translation/trans_real.py:377 -msgid "MONTH_DAY_FORMAT" -msgstr "j de/d' F del Y" +#: conf/global_settings.py:68 +msgid "Romanian" +msgstr "Rumanès" -#: template/defaultfilters.py:491 -msgid "yes,no,maybe" -msgstr "si,no,potser" +#: conf/global_settings.py:69 +msgid "Russian" +msgstr "Rús" -#: template/defaultfilters.py:520 -#, python-format -msgid "%(size)d byte" -msgid_plural "%(size)d bytes" -msgstr[0] "%(size)d byte" -msgstr[1] "%(size)d bytes" +#: conf/global_settings.py:70 +msgid "Slovak" +msgstr "Eslovac" -#: template/defaultfilters.py:522 -#, python-format -msgid "%.1f KB" -msgstr "%.1f KB" +#: conf/global_settings.py:71 +msgid "Slovenian" +msgstr "Esloveni" -#: template/defaultfilters.py:524 -#, python-format -msgid "%.1f MB" -msgstr "%.1f MB" +#: conf/global_settings.py:72 +msgid "Serbian" +msgstr "Serbi" -#: template/defaultfilters.py:525 -#, python-format -msgid "%.1f GB" -msgstr "%.1f GB" +#: conf/global_settings.py:73 +msgid "Swedish" +msgstr "Suec" + +#: conf/global_settings.py:74 +msgid "Tamil" +msgstr "" + +#: conf/global_settings.py:75 +msgid "Telugu" +msgstr "" + +#: conf/global_settings.py:76 +msgid "Turkish" +msgstr "Turc" + +#: conf/global_settings.py:77 +msgid "Ukrainian" +msgstr "Ucranià" + +#: conf/global_settings.py:78 +msgid "Simplified Chinese" +msgstr "Xinés simplificat" + +#: conf/global_settings.py:79 +msgid "Traditional Chinese" +msgstr "Xinés tradicional" #: contrib/comments/models.py:67 contrib/comments/models.py:166 msgid "object ID" @@ -346,7 +370,7 @@ msgstr "data/hora d'enviament" msgid "is public" msgstr "és públic" -#: contrib/comments/models.py:85 contrib/admin/views/doc.py:304 +#: contrib/comments/models.py:85 contrib/admin/views/doc.py:305 msgid "IP address" msgstr "Adreça IP" @@ -471,6 +495,75 @@ msgstr "eliminacions del moderador" msgid "Moderator deletion by %r" msgstr "eliminació del moderador per %r" +#: contrib/comments/templates/comments/freeform.html:4 +msgid "Your name:" +msgstr "El seu nom:" + +#: contrib/comments/templates/comments/freeform.html:5 +#: contrib/comments/templates/comments/form.html:28 +msgid "Comment:" +msgstr "Comentari:" + +#: contrib/comments/templates/comments/freeform.html:10 +#: contrib/comments/templates/comments/form.html:35 +msgid "Preview comment" +msgstr "Previsualitzar comentari" + +#: contrib/comments/templates/comments/form.html:6 +#: contrib/comments/templates/comments/form.html:8 +#: contrib/admin/templates/admin/login.html:17 +msgid "Username:" +msgstr "Usuari:" + +#: contrib/comments/templates/comments/form.html:6 +#: contrib/admin/templates/admin/object_history.html:3 +#: contrib/admin/templates/admin/delete_confirmation.html:3 +#: contrib/admin/templates/admin/change_form.html:10 +#: contrib/admin/templates/admin/change_list.html:5 +#: contrib/admin/templates/admin/base.html:25 +#: contrib/admin/templates/admin/auth/user/change_password.html:9 +#: contrib/admin/templates/registration/password_change_form.html:3 +#: contrib/admin/templates/registration/password_change_done.html:3 +#: contrib/admin/templates/admin_doc/bookmarklets.html:4 +#: contrib/admin/templates/admin_doc/template_tag_index.html:5 +#: contrib/admin/templates/admin_doc/view_detail.html:4 +#: contrib/admin/templates/admin_doc/missing_docutils.html:4 +#: contrib/admin/templates/admin_doc/model_index.html:5 +#: contrib/admin/templates/admin_doc/index.html:4 +#: contrib/admin/templates/admin_doc/template_filter_index.html:5 +#: contrib/admin/templates/admin_doc/model_detail.html:3 +#: contrib/admin/templates/admin_doc/template_detail.html:4 +#: contrib/admin/templates/admin_doc/view_index.html:5 +msgid "Log out" +msgstr "Finalitzar sessió" + +#: contrib/comments/templates/comments/form.html:8 +#: contrib/admin/templates/admin/login.html:20 +msgid "Password:" +msgstr "Contrasenya:" + +#: contrib/comments/templates/comments/form.html:8 +msgid "Forgotten your password?" +msgstr "Contrasenya oblidada?" + +#: contrib/comments/templates/comments/form.html:12 +msgid "Ratings" +msgstr "Qualificacions" + +#: contrib/comments/templates/comments/form.html:12 +#: contrib/comments/templates/comments/form.html:23 +msgid "Required" +msgstr "Requerit" + +#: contrib/comments/templates/comments/form.html:12 +#: contrib/comments/templates/comments/form.html:23 +msgid "Optional" +msgstr "Opcional" + +#: contrib/comments/templates/comments/form.html:23 +msgid "Post a photo" +msgstr "Enviar una fotografia" + #: contrib/comments/views/comments.py:27 msgid "" "This rating is required because you've entered at least one other rating." @@ -553,114 +646,13 @@ msgstr "ID del comentari invàlid" msgid "No voting for yourself" msgstr "No pots votar-te a tu mateix" -#: contrib/comments/templates/comments/form.html:6 -#: contrib/comments/templates/comments/form.html:8 -#: contrib/admin/templates/admin/login.html:17 -msgid "Username:" -msgstr "Usuari:" +#: contrib/admin/models.py:16 +msgid "action time" +msgstr "moment de l'acció" -#: contrib/comments/templates/comments/form.html:6 -#: contrib/admin/templates/admin/change_form.html:10 -#: contrib/admin/templates/admin/change_list.html:5 -#: contrib/admin/templates/admin/delete_confirmation.html:3 -#: contrib/admin/templates/admin/object_history.html:3 -#: contrib/admin/templates/admin/base.html:25 -#: contrib/admin/templates/admin/auth/user/change_password.html:9 -#: contrib/admin/templates/admin_doc/missing_docutils.html:4 -#: contrib/admin/templates/admin_doc/template_detail.html:4 -#: contrib/admin/templates/admin_doc/view_detail.html:4 -#: contrib/admin/templates/admin_doc/view_index.html:5 -#: contrib/admin/templates/admin_doc/index.html:4 -#: contrib/admin/templates/admin_doc/template_tag_index.html:5 -#: contrib/admin/templates/admin_doc/bookmarklets.html:4 -#: contrib/admin/templates/admin_doc/model_index.html:5 -#: contrib/admin/templates/admin_doc/template_filter_index.html:5 -#: contrib/admin/templates/admin_doc/model_detail.html:3 -#: contrib/admin/templates/registration/password_change_form.html:3 -#: contrib/admin/templates/registration/password_change_done.html:3 -msgid "Log out" -msgstr "Finalitzar sessió" - -#: contrib/comments/templates/comments/form.html:8 -#: contrib/admin/templates/admin/login.html:20 -msgid "Password:" -msgstr "Contrasenya:" - -#: contrib/comments/templates/comments/form.html:8 -msgid "Forgotten your password?" -msgstr "Contrasenya oblidada?" - -#: contrib/comments/templates/comments/form.html:12 -msgid "Ratings" -msgstr "Qualificacions" - -#: contrib/comments/templates/comments/form.html:12 -#: contrib/comments/templates/comments/form.html:23 -msgid "Required" -msgstr "Requerit" - -#: contrib/comments/templates/comments/form.html:12 -#: contrib/comments/templates/comments/form.html:23 -msgid "Optional" -msgstr "Opcional" - -#: contrib/comments/templates/comments/form.html:23 -msgid "Post a photo" -msgstr "Enviar una fotografia" - -#: contrib/comments/templates/comments/form.html:28 -#: contrib/comments/templates/comments/freeform.html:5 -msgid "Comment:" -msgstr "Comentari:" - -#: contrib/comments/templates/comments/form.html:35 -#: contrib/comments/templates/comments/freeform.html:10 -msgid "Preview comment" -msgstr "Previsualitzar comentari" - -#: contrib/comments/templates/comments/freeform.html:4 -msgid "Your name:" -msgstr "El seu nom:" - -#: contrib/redirects/models.py:7 -msgid "redirect from" -msgstr "redreçar des de" - -#: contrib/redirects/models.py:8 -msgid "" -"This should be an absolute path, excluding the domain name. Example: '/" -"events/search/'." -msgstr "" -"Aquesta ruta hauria de ser el camí absolut, excloent-ne el nom del domini. " -"Exemple '/events/search/'." - -#: contrib/redirects/models.py:9 -msgid "redirect to" -msgstr "redreçar a" - -#: contrib/redirects/models.py:10 -msgid "" -"This can be either an absolute path (as above) or a full URL starting with " -"'http://'." -msgstr "" -"Això pot ser bé una ruta absoluta (com abans) o una URL completa que comenci " -"per http:// ." - -#: contrib/redirects/models.py:13 -msgid "redirect" -msgstr "redreçament" - -#: contrib/redirects/models.py:14 -msgid "redirects" -msgstr "redreçaments" - -#: contrib/admin/models.py:16 -msgid "action time" -msgstr "moment de l'acció" - -#: contrib/admin/models.py:19 -msgid "object id" -msgstr "id del objecte" +#: contrib/admin/models.py:19 +msgid "object id" +msgstr "id del objecte" #: contrib/admin/models.py:20 msgid "object repr" @@ -716,1120 +708,1187 @@ msgstr "Aquest mes" msgid "This year" msgstr "Aquest any" -#: contrib/admin/filterspecs.py:143 newforms/widgets.py:182 -#: oldforms/__init__.py:577 -msgid "Yes" -msgstr "Si" - -#: contrib/admin/filterspecs.py:143 newforms/widgets.py:182 -#: oldforms/__init__.py:577 -msgid "No" -msgstr "No" - -#: contrib/admin/filterspecs.py:150 newforms/widgets.py:182 -#: oldforms/__init__.py:577 -msgid "Unknown" -msgstr "Desconegut" - -#: contrib/admin/templatetags/admin_list.py:247 +#: contrib/admin/templatetags/admin_list.py:249 msgid "All dates" msgstr "Totes les dates" -#: contrib/admin/views/doc.py:46 contrib/admin/views/doc.py:48 -#: contrib/admin/views/doc.py:50 -msgid "tag:" -msgstr "etiqueta:" +#: contrib/admin/templates/admin/object_history.html:3 +#: contrib/admin/templates/admin/delete_confirmation.html:3 +#: contrib/admin/templates/admin/change_form.html:10 +#: contrib/admin/templates/admin/change_list.html:5 +#: contrib/admin/templates/admin/base.html:25 +#: contrib/admin/templates/admin/auth/user/change_password.html:9 +#: contrib/admin/templates/registration/password_change_form.html:3 +#: contrib/admin/templates/registration/password_change_done.html:3 +#: contrib/admin/templates/admin_doc/bookmarklets.html:3 +msgid "Documentation" +msgstr "Documentació" -#: contrib/admin/views/doc.py:77 contrib/admin/views/doc.py:79 -#: contrib/admin/views/doc.py:81 -msgid "filter:" -msgstr "filtre:" +#: contrib/admin/templates/admin/object_history.html:3 +#: contrib/admin/templates/admin/delete_confirmation.html:3 +#: contrib/admin/templates/admin/change_form.html:10 +#: contrib/admin/templates/admin/change_list.html:5 +#: contrib/admin/templates/admin/base.html:25 +#: contrib/admin/templates/admin/auth/user/change_password.html:9 +#: contrib/admin/templates/admin/auth/user/change_password.html:15 +#: contrib/admin/templates/admin/auth/user/change_password.html:46 +#: contrib/admin/templates/registration/password_change_form.html:3 +#: contrib/admin/templates/registration/password_change_done.html:3 +#: contrib/admin/templates/admin_doc/bookmarklets.html:4 +#: contrib/admin/templates/admin_doc/template_tag_index.html:5 +#: contrib/admin/templates/admin_doc/view_detail.html:4 +#: contrib/admin/templates/admin_doc/missing_docutils.html:4 +#: contrib/admin/templates/admin_doc/model_index.html:5 +#: contrib/admin/templates/admin_doc/index.html:4 +#: contrib/admin/templates/admin_doc/template_filter_index.html:5 +#: contrib/admin/templates/admin_doc/model_detail.html:3 +#: contrib/admin/templates/admin_doc/template_detail.html:4 +#: contrib/admin/templates/admin_doc/view_index.html:5 +msgid "Change password" +msgstr "Canviar clau" -#: contrib/admin/views/doc.py:135 contrib/admin/views/doc.py:137 -#: contrib/admin/views/doc.py:139 -msgid "view:" -msgstr "vista:" +#: contrib/admin/templates/admin/object_history.html:5 +#: contrib/admin/templates/admin/invalid_setup.html:4 +#: contrib/admin/templates/admin/delete_confirmation.html:6 +#: contrib/admin/templates/admin/change_form.html:13 +#: contrib/admin/templates/admin/500.html:4 +#: contrib/admin/templates/admin/change_list.html:6 +#: contrib/admin/templates/admin/base.html:30 +#: contrib/admin/templates/admin/auth/user/change_password.html:12 +#: contrib/admin/templates/registration/logged_out.html:4 +#: contrib/admin/templates/registration/password_change_form.html:4 +#: contrib/admin/templates/registration/password_reset_done.html:4 +#: contrib/admin/templates/registration/password_change_done.html:4 +#: contrib/admin/templates/registration/password_reset_form.html:4 +#: contrib/admin/templates/admin_doc/bookmarklets.html:3 +msgid "Home" +msgstr "Inici" -#: contrib/admin/views/doc.py:164 -#, python-format -msgid "App %r not found" -msgstr "La aplicació %r no s'ha pogut trobar" +#: contrib/admin/templates/admin/object_history.html:5 +#: contrib/admin/templates/admin/change_form.html:21 +msgid "History" +msgstr "Històric" -#: contrib/admin/views/doc.py:171 -#, python-format -msgid "Model %(name)r not found in app %(label)r" -msgstr "El model %(name)r no s'ha trobat en la aplicació %(label)r" +#: contrib/admin/templates/admin/object_history.html:18 +msgid "Date/time" +msgstr "Data/hora" -#: contrib/admin/views/doc.py:183 -#, python-format -msgid "the related `%(label)s.%(type)s` object" -msgstr "el objecte relacionat `%(label)s.%(type)s`" +#: contrib/admin/templates/admin/object_history.html:19 +msgid "User" +msgstr "Usuari" -#: contrib/admin/views/doc.py:183 contrib/admin/views/doc.py:205 -#: contrib/admin/views/doc.py:219 contrib/admin/views/doc.py:224 -msgid "model:" -msgstr "model:" +#: contrib/admin/templates/admin/object_history.html:20 +msgid "Action" +msgstr "Acció" -#: contrib/admin/views/doc.py:214 -#, python-format -msgid "related `%(label)s.%(name)s` objects" -msgstr "objectes relacionats `%(label)s.%(name)s`" +#: contrib/admin/templates/admin/object_history.html:26 +msgid "DATE_WITH_TIME_FULL" +msgstr "F j, Y, H:i " -#: contrib/admin/views/doc.py:219 -#, python-format -msgid "all %s" -msgstr "tots %s" +#: contrib/admin/templates/admin/object_history.html:36 +msgid "" +"This object doesn't have a change history. It probably wasn't added via this " +"admin site." +msgstr "" +"Aquest objecte no te historial de canvis. Probablement no va ser afegit " +"utilitzant aquest lloc administratiu." -#: contrib/admin/views/doc.py:224 +#: contrib/admin/templates/admin/invalid_setup.html:8 +msgid "" +"Something's wrong with your database installation. Make sure the appropriate " +"database tables have been created, and make sure the database is readable by " +"the appropriate user." +msgstr "" +"Alguna cosa està malament en la instal·lació de la teva base de dades. " +"Assegurat de que s'han creat les taules, i de que la base de dades és " +"llegible per l'usuari apropiat." + +#: contrib/admin/templates/admin/search_form.html:8 +msgid "Go" +msgstr "Cercar" + +#: contrib/admin/templates/admin/search_form.html:10 #, python-format -msgid "number of %s" -msgstr "nombre de %s" +msgid "1 result" +msgid_plural "%(counter)s results" +msgstr[0] "1 resultat" +msgstr[1] "%(counter)s resultats" -#: contrib/admin/views/doc.py:229 +#: contrib/admin/templates/admin/search_form.html:10 #, python-format -msgid "Fields on %s objects" -msgstr "Camps en objectes %s" +msgid "%(full_result_count)s total" +msgstr "%(full_result_count)s en total" -#: contrib/admin/views/doc.py:291 contrib/admin/views/doc.py:301 -#: contrib/admin/views/doc.py:303 contrib/admin/views/doc.py:309 -#: contrib/admin/views/doc.py:310 contrib/admin/views/doc.py:312 -msgid "Integer" -msgstr "Enter" +#: contrib/admin/templates/admin/delete_confirmation.html:9 +#: contrib/admin/templates/admin/submit_line.html:3 +msgid "Delete" +msgstr "Eliminar" -#: contrib/admin/views/doc.py:292 -msgid "Boolean (Either True or False)" -msgstr "Booleà (Verdader o Fals)" +#: contrib/admin/templates/admin/delete_confirmation.html:14 +#, python-format +msgid "" +"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " +"related objects, but your account doesn't have permission to delete the " +"following types of objects:" +msgstr "" +"Eliminar el/la %(object_name)s '%(escaped_object)s' provocaria l'eliminació " +"d'objectes relacionats, però el seu compte no te permisos per a esborrar els " +"tipus d'objecte següents:" -#: contrib/admin/views/doc.py:293 contrib/admin/views/doc.py:311 +#: contrib/admin/templates/admin/delete_confirmation.html:21 #, python-format -msgid "String (up to %(maxlength)s)" -msgstr "Cadena (fins a %(maxlength)s)" +msgid "" +"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " +"All of the following related items will be deleted:" +msgstr "" +"Està segur de voler esborrar els/les %(object_name)s \"%(escaped_object)s\"? " +"S'esborraran els següents elements relacionats:" -#: contrib/admin/views/doc.py:294 -msgid "Comma-separated integers" -msgstr "Enters separats per comes" +#: contrib/admin/templates/admin/delete_confirmation.html:26 +msgid "Yes, I'm sure" +msgstr "Si, estic segur" -#: contrib/admin/views/doc.py:295 -msgid "Date (without time)" -msgstr "Data (sense hora)" +#: contrib/admin/templates/admin/pagination.html:10 +msgid "Show all" +msgstr "Mostrar tots" -#: contrib/admin/views/doc.py:296 -msgid "Date (with time)" -msgstr "Data (amb hora)" +#: contrib/admin/templates/admin/login.html:25 +#: contrib/admin/views/decorators.py:24 +msgid "Log in" +msgstr "Iniciar sessió" -#: contrib/admin/views/doc.py:297 -msgid "E-mail address" -msgstr "Adreça de correu electrònic" +#: contrib/admin/templates/admin/change_form.html:15 +#: contrib/admin/templates/admin/index.html:28 +msgid "Add" +msgstr "Afegir" -#: contrib/admin/views/doc.py:298 contrib/admin/views/doc.py:299 -#: contrib/admin/views/doc.py:302 -msgid "File path" -msgstr "Ruta del fitxer" +#: contrib/admin/templates/admin/change_form.html:22 +msgid "View on site" +msgstr "Veure en el lloc" -#: contrib/admin/views/doc.py:300 -msgid "Decimal number" -msgstr "Número decimal" +#: contrib/admin/templates/admin/change_form.html:32 +#: contrib/admin/templates/admin/auth/user/change_password.html:24 +msgid "Please correct the error below." +msgid_plural "Please correct the errors below." +msgstr[0] "Si us plau, corregeixi l'error mostrat abaix." +msgstr[1] "Si us plau, corregeixi els errors mostrats abaix." -#: contrib/admin/views/doc.py:306 -msgid "Boolean (Either True, False or None)" -msgstr "Booleà (Verdader, Fals o 'None' (cap))" +#: contrib/admin/templates/admin/change_form.html:50 +msgid "Ordering" +msgstr "Ordre" -#: contrib/admin/views/doc.py:307 -msgid "Relation to parent model" -msgstr "Relació amb el model pare" +#: contrib/admin/templates/admin/change_form.html:53 +msgid "Order:" +msgstr "Ordre:" -#: contrib/admin/views/doc.py:308 -msgid "Phone number" -msgstr "Número de telèfon" +#: contrib/admin/templates/admin/index.html:17 +#, python-format +msgid "Models available in the %(name)s application." +msgstr "Models disponibles en la aplicació %(name)s." -#: contrib/admin/views/doc.py:313 -msgid "Text" -msgstr "Texte" +#: contrib/admin/templates/admin/index.html:18 +#, python-format +msgid "%(name)s" +msgstr "%(name)s" -#: contrib/admin/views/doc.py:314 -msgid "Time" -msgstr "Hora" +#: contrib/admin/templates/admin/index.html:34 +msgid "Change" +msgstr "Modificar" -#: contrib/admin/views/doc.py:315 contrib/flatpages/models.py:7 -msgid "URL" -msgstr "URL" +#: contrib/admin/templates/admin/index.html:44 +msgid "You don't have permission to edit anything." +msgstr "No té permís per editar res." -#: contrib/admin/views/doc.py:316 -msgid "U.S. state (two uppercase letters)" -msgstr "Estat dels E.U.A. (dos lletres majúscules)" +#: contrib/admin/templates/admin/index.html:52 +msgid "Recent Actions" +msgstr "Accions recents" -#: contrib/admin/views/doc.py:317 -msgid "XML text" -msgstr "Texte XML" +#: contrib/admin/templates/admin/index.html:53 +msgid "My Actions" +msgstr "Les meves accions" -#: contrib/admin/views/doc.py:343 -#, python-format -msgid "%s does not appear to be a urlpattern object" -msgstr "%s no sembla ser un objecte 'urlpattern'" +#: contrib/admin/templates/admin/index.html:57 +msgid "None available" +msgstr "Cap disponible" -#: contrib/admin/views/decorators.py:10 contrib/auth/forms.py:60 -msgid "" -"Please enter a correct username and password. Note that both fields are case-" -"sensitive." -msgstr "" -"Si us plau, introdueixi un nom d'usuari i contrasenya vàlids. Tingui en " -"compte que tots dos camps son sensibles a majúscules i minúscules." +#: contrib/admin/templates/admin/500.html:4 +msgid "Server error" +msgstr "Error del servidor" -#: contrib/admin/views/decorators.py:24 -#: contrib/admin/templates/admin/login.html:25 -msgid "Log in" -msgstr "Iniciar sessió" +#: contrib/admin/templates/admin/500.html:6 +msgid "Server error (500)" +msgstr "Error del servidor (500)" -#: contrib/admin/views/decorators.py:62 -msgid "" -"Please log in again, because your session has expired. Don't worry: Your " -"submission has been saved." -msgstr "" -"Si us plau, identifiquis de nou doncs la seva sessió ha expirat. No es " -"preocupi, el seu enviament està emmagatzemat." +#: contrib/admin/templates/admin/500.html:9 +msgid "Server Error (500)" +msgstr "Error del servidor (500)" -#: contrib/admin/views/decorators.py:69 +#: contrib/admin/templates/admin/500.html:10 msgid "" -"Looks like your browser isn't configured to accept cookies. Please enable " -"cookies, reload this page, and try again." -msgstr "" -"Sembla ser que el seu navegador no està configurat per acceptar " -"'cookies' (galetes). Si us plau, habiliti les 'cookies', recarregui aquesta " -"pàgina i provi-ho de nou. " - -#: contrib/admin/views/decorators.py:83 -msgid "Usernames cannot contain the '@' character." -msgstr "Els noms d'usuari no poden contenir el caracter '@'." - -#: contrib/admin/views/decorators.py:85 -#, python-format -msgid "Your e-mail address is not your username. Try '%s' instead." +"There's been an error. It's been reported to the site administrators via e-" +"mail and should be fixed shortly. Thanks for your patience." msgstr "" -"La seva adreça de correu no és el seu nom d'usuari. Provi '%s' en tot cas." +"Hi ha hagut un error. S'ha informat als administradors del lloc per correu " +"electrònic y hauria d'arreglar-se en breu. Gràcies per la seva paciència." -#: contrib/admin/views/auth.py:19 contrib/admin/views/main.py:257 +#: contrib/admin/templates/admin/change_list.html:12 #, python-format -msgid "The %(name)s \"%(obj)s\" was added successfully." -msgstr "El/la %(name)s \"%(obj)s\".ha estat agregat/da amb èxit." - -#: contrib/admin/views/auth.py:24 contrib/admin/views/main.py:261 -#: contrib/admin/views/main.py:347 -msgid "You may edit it again below." -msgstr "Pot editar-lo de nou abaix." - -#: contrib/admin/views/auth.py:30 -msgid "Add user" -msgstr "Agregar usuari" +msgid "Add %(name)s" +msgstr "Afegir %(name)s" -#: contrib/admin/views/auth.py:57 -msgid "Password changed successfully." -msgstr "Canvi de clau exitós" +#: contrib/admin/templates/admin/base.html:25 +msgid "Welcome," +msgstr "Benvingut," -#: contrib/admin/views/auth.py:64 -#, python-format -msgid "Change password: %s" -msgstr "Canviar clau: %s" +#: contrib/admin/templates/admin/base_site.html:4 +msgid "Django site admin" +msgstr "Lloc administratiu de Django" -#: contrib/admin/views/main.py:223 -msgid "Site administration" -msgstr "Lloc administratiu" +#: contrib/admin/templates/admin/base_site.html:7 +msgid "Django administration" +msgstr "Adminsitració de Django" -#: contrib/admin/views/main.py:271 contrib/admin/views/main.py:356 -#, python-format -msgid "You may add another %s below." -msgstr "Pot agregar un altre %s abaix." +#: contrib/admin/templates/admin/filters.html:4 +msgid "Filter" +msgstr "Filtre" -#: contrib/admin/views/main.py:289 -#, python-format -msgid "Add %s" -msgstr "Agregar %s" +#: contrib/admin/templates/admin/submit_line.html:4 +msgid "Save as new" +msgstr "Desar com a nou" -#: contrib/admin/views/main.py:335 -#, python-format -msgid "Added %s." -msgstr "Agregat %s." +#: contrib/admin/templates/admin/submit_line.html:5 +msgid "Save and add another" +msgstr "Desar i afegir-ne un de nou" -#: contrib/admin/views/main.py:335 contrib/admin/views/main.py:337 -#: contrib/admin/views/main.py:339 db/models/manipulators.py:308 -msgid "and" -msgstr "i" +#: contrib/admin/templates/admin/submit_line.html:6 +msgid "Save and continue editing" +msgstr "Desar i continuar editant" -#: contrib/admin/views/main.py:337 -#, python-format -msgid "Changed %s." -msgstr "Modificat %s." +#: contrib/admin/templates/admin/submit_line.html:7 +msgid "Save" +msgstr "Desar" -#: contrib/admin/views/main.py:339 -#, python-format -msgid "Deleted %s." -msgstr "Eliminat %s." +#: contrib/admin/templates/admin/404.html:4 +#: contrib/admin/templates/admin/404.html:8 +msgid "Page not found" +msgstr "No s'ha pogut trobar la pàgina" -#: contrib/admin/views/main.py:342 -msgid "No fields changed." -msgstr "Cap camp canviat." +#: contrib/admin/templates/admin/404.html:10 +msgid "We're sorry, but the requested page could not be found." +msgstr "Ho sentim, però no s'ha pogut trobar la pàgina solicitada" -#: contrib/admin/views/main.py:345 +#: contrib/admin/templates/admin/filter.html:2 #, python-format -msgid "The %(name)s \"%(obj)s\" was changed successfully." -msgstr "S'ha modificat amb èxist el/la %(name)s \"%(obj)s." +msgid " By %(filter_title)s " +msgstr "Per %(filter_title)s " -#: contrib/admin/views/main.py:353 -#, python-format +#: contrib/admin/templates/admin/auth/user/add_form.html:6 msgid "" -"The %(name)s \"%(obj)s\" was added successfully. You may edit it again below." +"First, enter a username and password. Then, you'll be able to edit more user " +"options." msgstr "" -"S'ha agregat amb èxit el/la %(name)s \"%(obj)s\". Pot editar-lo de nou abaix." +"Primer, entri un usuari i una contrasenya. Després podrà editar més opcions " +"del usuari." -#: contrib/admin/views/main.py:391 -#, python-format -msgid "Change %s" -msgstr "Modificar %s" +#: contrib/admin/templates/admin/auth/user/add_form.html:12 +msgid "Username" +msgstr "Usuari" -#: contrib/admin/views/main.py:476 -#, python-format -msgid "One or more %(fieldname)s in %(name)s: %(obj)s" -msgstr "Un o més %(fieldname)s en %(name)s: %(obj)s" +#: contrib/admin/templates/admin/auth/user/add_form.html:18 +#: contrib/admin/templates/admin/auth/user/change_password.html:34 +msgid "Password" +msgstr "Contrasenya" -#: contrib/admin/views/main.py:481 -#, python-format -msgid "One or more %(fieldname)s in %(name)s:" -msgstr "Un o més %(fieldname)s en %(name)s:" +#: contrib/admin/templates/admin/auth/user/add_form.html:23 +#: contrib/admin/templates/admin/auth/user/change_password.html:39 +msgid "Password (again)" +msgstr "Contrasenya (de nou)" -#: contrib/admin/views/main.py:514 +#: contrib/admin/templates/admin/auth/user/add_form.html:24 +#: contrib/admin/templates/admin/auth/user/change_password.html:40 +msgid "Enter the same password as above, for verification." +msgstr "Introdueixi la mateixa contrasenya que a sobre, per a verificació." + +#: contrib/admin/templates/admin/auth/user/change_password.html:28 #, python-format -msgid "The %(name)s \"%(obj)s\" was deleted successfully." -msgstr "El/la %(name)s \"%(obj)s\".ha estat eliminat amb èxit." +msgid "Enter a new password for the user %(username)s." +msgstr "Introdueixi una contrasenya per l'usuari %(username)s" -#: contrib/admin/views/main.py:517 -msgid "Are you sure?" -msgstr "Està segur?" +#: contrib/admin/templates/widget/date_time.html:3 +msgid "Date:" +msgstr "Data:" -#: contrib/admin/views/main.py:539 -#, python-format -msgid "Change history: %s" -msgstr "Modificar històric: %s" +#: contrib/admin/templates/widget/date_time.html:4 +msgid "Time:" +msgstr "Hora:" -#: contrib/admin/views/main.py:573 -#, python-format -msgid "Select %s" -msgstr "Seleccioni %s" +#: contrib/admin/templates/widget/file.html:2 +msgid "Currently:" +msgstr "Actualment:" -#: contrib/admin/views/main.py:573 -#, python-format -msgid "Select %s to change" -msgstr "Seleccioni %s per modificar" +#: contrib/admin/templates/widget/file.html:3 +msgid "Change:" +msgstr "Modificar:" -#: contrib/admin/views/main.py:768 -msgid "Database error" -msgstr "Error de/en la base de dades" +#: contrib/admin/templates/registration/logged_out.html:8 +msgid "Thanks for spending some quality time with the Web site today." +msgstr "Gràcies per emprar algun temps de cualitat amb el lloc web avui." -#: contrib/admin/templates/admin/filter.html:2 -#, python-format -msgid " By %(filter_title)s " -msgstr "Per %(filter_title)s " +#: contrib/admin/templates/registration/logged_out.html:10 +msgid "Log in again" +msgstr "Iniciar sessió de nou" -#: contrib/admin/templates/admin/filters.html:4 -msgid "Filter" -msgstr "Filtre" +#: contrib/admin/templates/registration/password_change_form.html:4 +#: contrib/admin/templates/registration/password_change_form.html:6 +#: contrib/admin/templates/registration/password_change_form.html:10 +#: contrib/admin/templates/registration/password_change_done.html:4 +msgid "Password change" +msgstr "Canvi de clau" -#: contrib/admin/templates/admin/change_form.html:10 -#: contrib/admin/templates/admin/change_list.html:5 -#: contrib/admin/templates/admin/delete_confirmation.html:3 -#: contrib/admin/templates/admin/object_history.html:3 -#: contrib/admin/templates/admin/base.html:25 -#: contrib/admin/templates/admin/auth/user/change_password.html:9 -#: contrib/admin/templates/admin_doc/bookmarklets.html:3 -#: contrib/admin/templates/registration/password_change_form.html:3 -#: contrib/admin/templates/registration/password_change_done.html:3 -msgid "Documentation" -msgstr "Documentació" +#: contrib/admin/templates/registration/password_change_form.html:12 +msgid "" +"Please enter your old password, for security's sake, and then enter your new " +"password twice so we can verify you typed it in correctly." +msgstr "" +"Si us plau, introdueixi la seva contrasenya antiga, per seguretat, i tot " +"seguit introdueixi la seva nova contrasenya dues vegades per verificar que " +"l'ha escrit correctament." -#: contrib/admin/templates/admin/change_form.html:10 -#: contrib/admin/templates/admin/change_list.html:5 -#: contrib/admin/templates/admin/delete_confirmation.html:3 -#: contrib/admin/templates/admin/object_history.html:3 -#: contrib/admin/templates/admin/base.html:25 -#: contrib/admin/templates/admin/auth/user/change_password.html:9 -#: contrib/admin/templates/admin/auth/user/change_password.html:15 -#: contrib/admin/templates/admin/auth/user/change_password.html:46 -#: contrib/admin/templates/admin_doc/missing_docutils.html:4 -#: contrib/admin/templates/admin_doc/template_detail.html:4 -#: contrib/admin/templates/admin_doc/view_detail.html:4 -#: contrib/admin/templates/admin_doc/view_index.html:5 -#: contrib/admin/templates/admin_doc/index.html:4 -#: contrib/admin/templates/admin_doc/template_tag_index.html:5 -#: contrib/admin/templates/admin_doc/bookmarklets.html:4 -#: contrib/admin/templates/admin_doc/model_index.html:5 -#: contrib/admin/templates/admin_doc/template_filter_index.html:5 -#: contrib/admin/templates/admin_doc/model_detail.html:3 -#: contrib/admin/templates/registration/password_change_form.html:3 -#: contrib/admin/templates/registration/password_change_done.html:3 -msgid "Change password" -msgstr "Canviar clau" +#: contrib/admin/templates/registration/password_change_form.html:17 +msgid "Old password:" +msgstr "Contrasenya antiga:" + +#: contrib/admin/templates/registration/password_change_form.html:19 +msgid "New password:" +msgstr "Contrasenya nova:" + +#: contrib/admin/templates/registration/password_change_form.html:21 +msgid "Confirm password:" +msgstr "Confirmar contrasenya:" + +#: contrib/admin/templates/registration/password_change_form.html:23 +msgid "Change my password" +msgstr "Canviar la meva clau:" -#: contrib/admin/templates/admin/change_form.html:13 -#: contrib/admin/templates/admin/change_list.html:6 -#: contrib/admin/templates/admin/500.html:4 -#: contrib/admin/templates/admin/delete_confirmation.html:6 -#: contrib/admin/templates/admin/object_history.html:5 -#: contrib/admin/templates/admin/base.html:30 -#: contrib/admin/templates/admin/invalid_setup.html:4 -#: contrib/admin/templates/admin/auth/user/change_password.html:12 -#: contrib/admin/templates/admin_doc/bookmarklets.html:3 -#: contrib/admin/templates/registration/password_reset_form.html:4 #: contrib/admin/templates/registration/password_reset_done.html:4 -#: contrib/admin/templates/registration/password_change_form.html:4 -#: contrib/admin/templates/registration/password_change_done.html:4 -#: contrib/admin/templates/registration/logged_out.html:4 -msgid "Home" -msgstr "Inici" +#: contrib/admin/templates/registration/password_reset_form.html:4 +#: contrib/admin/templates/registration/password_reset_form.html:6 +#: contrib/admin/templates/registration/password_reset_form.html:10 +msgid "Password reset" +msgstr "Restablir contrasenya" -#: contrib/admin/templates/admin/change_form.html:15 -#: contrib/admin/templates/admin/index.html:28 -msgid "Add" -msgstr "Afegir" +#: contrib/admin/templates/registration/password_reset_done.html:6 +#: contrib/admin/templates/registration/password_reset_done.html:10 +msgid "Password reset successful" +msgstr "Contrasenya restaber-ta amb èxit" -#: contrib/admin/templates/admin/change_form.html:21 -#: contrib/admin/templates/admin/object_history.html:5 -msgid "History" -msgstr "Històric" +#: contrib/admin/templates/registration/password_reset_done.html:12 +msgid "" +"We've e-mailed a new password to the e-mail address you submitted. You " +"should be receiving it shortly." +msgstr "" +"Li hem enviat una contrasenya nova a l'adreça de correu electrònic que ens " +"ha indicat. L'hauria de rebre en breu." -#: contrib/admin/templates/admin/change_form.html:22 -msgid "View on site" -msgstr "Veure en el lloc" +#: contrib/admin/templates/registration/password_change_done.html:6 +#: contrib/admin/templates/registration/password_change_done.html:10 +msgid "Password change successful" +msgstr "Canvi de clau exitò" -#: contrib/admin/templates/admin/change_form.html:32 -#: contrib/admin/templates/admin/auth/user/change_password.html:24 -msgid "Please correct the error below." -msgid_plural "Please correct the errors below." -msgstr[0] "Si us plau, corregeixi l'error mostrat abaix." -msgstr[1] "Si us plau, corregeixi els errors mostrats abaix." +#: contrib/admin/templates/registration/password_change_done.html:12 +msgid "Your password was changed." +msgstr "La seva clau ha estat canviada." -#: contrib/admin/templates/admin/change_form.html:50 -msgid "Ordering" -msgstr "Ordre" +#: contrib/admin/templates/registration/password_reset_email.html:2 +msgid "You're receiving this e-mail because you requested a password reset" +msgstr "" +"Està rebent aquest missatge degut a que va solicitar un restabliment de " +"contrasenya." -#: contrib/admin/templates/admin/change_form.html:53 -msgid "Order:" -msgstr "Ordre:" +#: contrib/admin/templates/registration/password_reset_email.html:3 +#, python-format +msgid "for your user account at %(site_name)s" +msgstr "del seu compte d'usuari a %(site_name)s." -#: contrib/admin/templates/admin/change_list.html:12 +#: contrib/admin/templates/registration/password_reset_email.html:5 #, python-format -msgid "Add %(name)s" -msgstr "Afegir %(name)s" +msgid "Your new password is: %(new_password)s" +msgstr "La seva nova contrasenya és: %(new_password)s" -#: contrib/admin/templates/admin/submit_line.html:3 -#: contrib/admin/templates/admin/delete_confirmation.html:9 -msgid "Delete" -msgstr "Eliminar" +#: contrib/admin/templates/registration/password_reset_email.html:7 +msgid "Feel free to change this password by going to this page:" +msgstr "Sentis lliure de canviar-la en aquesta pàgina:" -#: contrib/admin/templates/admin/submit_line.html:4 -msgid "Save as new" -msgstr "Desar com a nou" +#: contrib/admin/templates/registration/password_reset_email.html:11 +msgid "Your username, in case you've forgotten:" +msgstr "El seu nom d'usuari, en cas d'haver-lo oblidat:" -#: contrib/admin/templates/admin/submit_line.html:5 -msgid "Save and add another" -msgstr "Desar i afegir-ne un de nou" +#: contrib/admin/templates/registration/password_reset_email.html:13 +msgid "Thanks for using our site!" +msgstr "Gràcies per fer us del nostre lloc!" -#: contrib/admin/templates/admin/submit_line.html:6 -msgid "Save and continue editing" -msgstr "Desar i continuar editant" +#: contrib/admin/templates/registration/password_reset_email.html:15 +#, python-format +msgid "The %(site_name)s team" +msgstr "L'equip de %(site_name)s" -#: contrib/admin/templates/admin/submit_line.html:7 -msgid "Save" -msgstr "Desar" +#: contrib/admin/templates/registration/password_reset_form.html:12 +msgid "" +"Forgotten your password? Enter your e-mail address below, and we'll reset " +"your password and e-mail the new one to you." +msgstr "" +"Ha oblidat la seva contrasenya? Introdueixi la seva adreça de correu " +"electrònic i crearem una nova que li enviarem per correu." -#: contrib/admin/templates/admin/404.html:4 -#: contrib/admin/templates/admin/404.html:8 -msgid "Page not found" -msgstr "No s'ha pogut trobar la pàgina" +#: contrib/admin/templates/registration/password_reset_form.html:16 +msgid "E-mail address:" +msgstr "Adreça de correu electrònic:" -#: contrib/admin/templates/admin/404.html:10 -msgid "We're sorry, but the requested page could not be found." -msgstr "Ho sentim, però no s'ha pogut trobar la pàgina solicitada" +#: contrib/admin/templates/registration/password_reset_form.html:16 +msgid "Reset my password" +msgstr "Restablir la meva contrasenya" -#: contrib/admin/templates/admin/pagination.html:10 -msgid "Show all" -msgstr "Mostrar tots" +#: contrib/admin/templates/admin_doc/bookmarklets.html:3 +msgid "Bookmarklets" +msgstr "'Bookmarklets'" -#: contrib/admin/templates/admin/500.html:4 -msgid "Server error" -msgstr "Error del servidor" +#: contrib/admin/templates/admin_doc/bookmarklets.html:5 +msgid "Documentation bookmarklets" +msgstr "'Bookmarklets' de documentació" -#: contrib/admin/templates/admin/500.html:6 -msgid "Server error (500)" -msgstr "Error del servidor (500)" +#: contrib/admin/templates/admin_doc/bookmarklets.html:9 +msgid "" +"\n" +"

To install bookmarklets, drag the link to your bookmarks\n" +"toolbar, or right-click the link and add it to your bookmarks. Now you can\n" +"select the bookmarklet from any page in the site. Note that some of these\n" +"bookmarklets require you to be viewing the site from a computer designated\n" +"as \"internal\" (talk to your system administrator if you aren't sure if\n" +"your computer is \"internal\").

\n" +msgstr "" +"\n" +"

Per a instalar 'bookmarklets', arrosegui l'enllaç a la " +"seva barra de\n" +"marcadors, o faci click amb el botò dret en l'enllaç i afegeixi'l als " +"marcadors.\n" +"Ara pot escollir el 'bookmarklet' des de qualsevol pàgina del lloc.\n" +"Observi que alguns d'aquests 'bookmarklets' precisen que estigui veient\n" +"el lloc des de un ordinador senyalat com a \"intern\" (parli\n" +"amb el seu administrador de sistemes si no està segur de la condició del " +"seu).

\n" -#: contrib/admin/templates/admin/500.html:9 -msgid "Server Error (500)" -msgstr "Error del servidor (500)" +#: contrib/admin/templates/admin_doc/bookmarklets.html:19 +msgid "Documentation for this page" +msgstr "Documentació d'aquesta pàgina" -#: contrib/admin/templates/admin/500.html:10 +#: contrib/admin/templates/admin_doc/bookmarklets.html:20 msgid "" -"There's been an error. It's been reported to the site administrators via e-" -"mail and should be fixed shortly. Thanks for your patience." +"Jumps you from any page to the documentation for the view that generates " +"that page." msgstr "" -"Hi ha hagut un error. S'ha informat als administradors del lloc per correu " -"electrònic y hauria d'arreglar-se en breu. Gràcies per la seva paciència." +"El porta des de qualsevol pàgina de la documentació a la vista que la genera." -#: contrib/admin/templates/admin/base_site.html:4 -msgid "Django site admin" -msgstr "Lloc administratiu de Django" +#: contrib/admin/templates/admin_doc/bookmarklets.html:22 +msgid "Show object ID" +msgstr "Mostra el ID de l'objecte" -#: contrib/admin/templates/admin/base_site.html:7 -msgid "Django administration" -msgstr "Adminsitració de Django" +#: contrib/admin/templates/admin_doc/bookmarklets.html:23 +msgid "" +"Shows the content-type and unique ID for pages that represent a single " +"object." +msgstr "" +"Mostra el 'content-type' (tipus de contingut) i el ID inequívoc de les " +"pàgines que representen un únic objecte." -#: contrib/admin/templates/admin/search_form.html:8 -msgid "Go" -msgstr "Cercar" +#: contrib/admin/templates/admin_doc/bookmarklets.html:25 +msgid "Edit this object (current window)" +msgstr "Editar aquest objecte (finestra actual)" -#: contrib/admin/templates/admin/search_form.html:10 -#, python-format -msgid "1 result" -msgid_plural "%(counter)s results" -msgstr[0] "1 resultat" -msgstr[1] "%(counter)s resultats" +#: contrib/admin/templates/admin_doc/bookmarklets.html:26 +msgid "Jumps to the admin page for pages that represent a single object." +msgstr "" +"El porta a la pàgina d'administració de pàgines que representen un únic " +"objecte." -#: contrib/admin/templates/admin/search_form.html:10 -#, python-format -msgid "%(full_result_count)s total" -msgstr "%(full_result_count)s en total" +#: contrib/admin/templates/admin_doc/bookmarklets.html:28 +msgid "Edit this object (new window)" +msgstr "Editar aquest objecte (nova finestra)" -#: contrib/admin/templates/admin/index.html:17 -#, python-format -msgid "Models available in the %(name)s application." -msgstr "Models disponibles en la aplicació %(name)s." +#: contrib/admin/templates/admin_doc/bookmarklets.html:29 +msgid "As above, but opens the admin page in a new window." +msgstr "Com abans, però obre la pàgina d'administració en una nova finestra." -#: contrib/admin/templates/admin/index.html:18 +#: contrib/admin/views/auth.py:19 contrib/admin/views/main.py:257 #, python-format -msgid "%(name)s" -msgstr "%(name)s" +msgid "The %(name)s \"%(obj)s\" was added successfully." +msgstr "El/la %(name)s \"%(obj)s\".ha estat agregat/da amb èxit." -#: contrib/admin/templates/admin/index.html:34 -msgid "Change" -msgstr "Modificar" +#: contrib/admin/views/auth.py:24 contrib/admin/views/main.py:261 +#: contrib/admin/views/main.py:347 +msgid "You may edit it again below." +msgstr "Pot editar-lo de nou abaix." -#: contrib/admin/templates/admin/index.html:44 -msgid "You don't have permission to edit anything." -msgstr "No té permís per editar res." +#: contrib/admin/views/auth.py:30 +msgid "Add user" +msgstr "Agregar usuari" -#: contrib/admin/templates/admin/index.html:52 -msgid "Recent Actions" -msgstr "Accions recents" +#: contrib/admin/views/auth.py:57 +msgid "Password changed successfully." +msgstr "Canvi de clau exitós" -#: contrib/admin/templates/admin/index.html:53 -msgid "My Actions" -msgstr "Les meves accions" +#: contrib/admin/views/auth.py:64 +#, python-format +msgid "Change password: %s" +msgstr "Canviar clau: %s" -#: contrib/admin/templates/admin/index.html:57 -msgid "None available" -msgstr "Cap disponible" +#: contrib/admin/views/main.py:223 +msgid "Site administration" +msgstr "Lloc administratiu" -#: contrib/admin/templates/admin/delete_confirmation.html:14 +#: contrib/admin/views/main.py:271 contrib/admin/views/main.py:356 #, python-format -msgid "" -"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " -"related objects, but your account doesn't have permission to delete the " -"following types of objects:" -msgstr "" -"Eliminar el/la %(object_name)s '%(escaped_object)s' provocaria l'eliminació " -"d'objectes relacionats, però el seu compte no te permisos per a esborrar els " -"tipus d'objecte següents:" +msgid "You may add another %s below." +msgstr "Pot agregar un altre %s abaix." -#: contrib/admin/templates/admin/delete_confirmation.html:21 +#: contrib/admin/views/main.py:289 #, python-format -msgid "" -"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " -"All of the following related items will be deleted:" -msgstr "" -"Està segur de voler esborrar els/les %(object_name)s \"%(escaped_object)s\"? " -"S'esborraran els següents elements relacionats:" +msgid "Add %s" +msgstr "Agregar %s" -#: contrib/admin/templates/admin/delete_confirmation.html:26 -msgid "Yes, I'm sure" -msgstr "Si, estic segur" +#: contrib/admin/views/main.py:335 +#, python-format +msgid "Added %s." +msgstr "Agregat %s." -#: contrib/admin/templates/admin/object_history.html:18 -msgid "Date/time" -msgstr "Data/hora" +#: contrib/admin/views/main.py:335 contrib/admin/views/main.py:337 +#: contrib/admin/views/main.py:339 db/models/manipulators.py:308 +msgid "and" +msgstr "i" -#: contrib/admin/templates/admin/object_history.html:19 -msgid "User" -msgstr "Usuari" +#: contrib/admin/views/main.py:337 +#, python-format +msgid "Changed %s." +msgstr "Modificat %s." -#: contrib/admin/templates/admin/object_history.html:20 -msgid "Action" -msgstr "Acció" +#: contrib/admin/views/main.py:339 +#, python-format +msgid "Deleted %s." +msgstr "Eliminat %s." -#: contrib/admin/templates/admin/object_history.html:26 -msgid "DATE_WITH_TIME_FULL" -msgstr "F j, Y, H:i " +#: contrib/admin/views/main.py:342 +msgid "No fields changed." +msgstr "Cap camp canviat." -#: contrib/admin/templates/admin/object_history.html:36 +#: contrib/admin/views/main.py:345 +#, python-format +msgid "The %(name)s \"%(obj)s\" was changed successfully." +msgstr "S'ha modificat amb èxist el/la %(name)s \"%(obj)s." + +#: contrib/admin/views/main.py:353 +#, python-format msgid "" -"This object doesn't have a change history. It probably wasn't added via this " -"admin site." +"The %(name)s \"%(obj)s\" was added successfully. You may edit it again below." msgstr "" -"Aquest objecte no te historial de canvis. Probablement no va ser afegit " -"utilitzant aquest lloc administratiu." +"S'ha agregat amb èxit el/la %(name)s \"%(obj)s\". Pot editar-lo de nou abaix." -#: contrib/admin/templates/admin/base.html:25 -msgid "Welcome," -msgstr "Benvingut," +#: contrib/admin/views/main.py:391 +#, python-format +msgid "Change %s" +msgstr "Modificar %s" -#: contrib/admin/templates/admin/invalid_setup.html:8 -msgid "" -"Something's wrong with your database installation. Make sure the appropriate " -"database tables have been created, and make sure the database is readable by " -"the appropriate user." -msgstr "" -"Alguna cosa està malament en la instal·lació de la teva base de dades. " -"Assegurat de que s'han creat les taules, i de que la base de dades és " -"llegible per l'usuari apropiat." +#: contrib/admin/views/main.py:476 +#, python-format +msgid "One or more %(fieldname)s in %(name)s: %(obj)s" +msgstr "Un o més %(fieldname)s en %(name)s: %(obj)s" + +#: contrib/admin/views/main.py:481 +#, python-format +msgid "One or more %(fieldname)s in %(name)s:" +msgstr "Un o més %(fieldname)s en %(name)s:" -#: contrib/admin/templates/admin/auth/user/change_password.html:28 +#: contrib/admin/views/main.py:514 #, python-format -msgid "Enter a new password for the user %(username)s." -msgstr "Introdueixi una contrasenya per l'usuari %(username)s" +msgid "The %(name)s \"%(obj)s\" was deleted successfully." +msgstr "El/la %(name)s \"%(obj)s\".ha estat eliminat amb èxit." -#: contrib/admin/templates/admin/auth/user/change_password.html:34 -#: contrib/admin/templates/admin/auth/user/add_form.html:18 -msgid "Password" -msgstr "Contrasenya" +#: contrib/admin/views/main.py:517 +msgid "Are you sure?" +msgstr "Està segur?" -#: contrib/admin/templates/admin/auth/user/change_password.html:39 -#: contrib/admin/templates/admin/auth/user/add_form.html:23 -msgid "Password (again)" -msgstr "Contrasenya (de nou)" +#: contrib/admin/views/main.py:539 +#, python-format +msgid "Change history: %s" +msgstr "Modificar històric: %s" -#: contrib/admin/templates/admin/auth/user/change_password.html:40 -#: contrib/admin/templates/admin/auth/user/add_form.html:24 -msgid "Enter the same password as above, for verification." -msgstr "Introdueixi la mateixa contrasenya que a sobre, per a verificació." +#: contrib/admin/views/main.py:573 +#, python-format +msgid "Select %s" +msgstr "Seleccioni %s" -#: contrib/admin/templates/admin/auth/user/add_form.html:6 -msgid "" -"First, enter a username and password. Then, you'll be able to edit more user " -"options." -msgstr "" -"Primer, entri un usuari i una contrasenya. Després podrà editar més opcions " -"del usuari." +#: contrib/admin/views/main.py:573 +#, python-format +msgid "Select %s to change" +msgstr "Seleccioni %s per modificar" -#: contrib/admin/templates/admin/auth/user/add_form.html:12 -msgid "Username" -msgstr "Usuari" +#: contrib/admin/views/main.py:768 +msgid "Database error" +msgstr "Error de/en la base de dades" -#: contrib/admin/templates/admin_doc/bookmarklets.html:3 -msgid "Bookmarklets" -msgstr "'Bookmarklets'" +#: contrib/admin/views/doc.py:46 contrib/admin/views/doc.py:48 +#: contrib/admin/views/doc.py:50 +msgid "tag:" +msgstr "etiqueta:" -#: contrib/admin/templates/admin_doc/bookmarklets.html:5 -msgid "Documentation bookmarklets" -msgstr "'Bookmarklets' de documentació" +#: contrib/admin/views/doc.py:77 contrib/admin/views/doc.py:79 +#: contrib/admin/views/doc.py:81 +msgid "filter:" +msgstr "filtre:" -#: contrib/admin/templates/admin_doc/bookmarklets.html:9 -msgid "" -"\n" -"

To install bookmarklets, drag the link to your bookmarks\n" -"toolbar, or right-click the link and add it to your bookmarks. Now you can\n" -"select the bookmarklet from any page in the site. Note that some of these\n" -"bookmarklets require you to be viewing the site from a computer designated\n" -"as \"internal\" (talk to your system administrator if you aren't sure if\n" -"your computer is \"internal\").

\n" -msgstr "" -"\n" -"

Per a instalar 'bookmarklets', arrosegui l'enllaç a la " -"seva barra de\n" -"marcadors, o faci click amb el botò dret en l'enllaç i afegeixi'l als " -"marcadors.\n" -"Ara pot escollir el 'bookmarklet' des de qualsevol pàgina del lloc.\n" -"Observi que alguns d'aquests 'bookmarklets' precisen que estigui veient\n" -"el lloc des de un ordinador senyalat com a \"intern\" (parli\n" -"amb el seu administrador de sistemes si no està segur de la condició del " -"seu).

\n" +#: contrib/admin/views/doc.py:135 contrib/admin/views/doc.py:137 +#: contrib/admin/views/doc.py:139 +msgid "view:" +msgstr "vista:" -#: contrib/admin/templates/admin_doc/bookmarklets.html:19 -msgid "Documentation for this page" -msgstr "Documentació d'aquesta pàgina" +#: contrib/admin/views/doc.py:164 +#, python-format +msgid "App %r not found" +msgstr "La aplicació %r no s'ha pogut trobar" -#: contrib/admin/templates/admin_doc/bookmarklets.html:20 -msgid "" -"Jumps you from any page to the documentation for the view that generates " -"that page." -msgstr "" -"El porta des de qualsevol pàgina de la documentació a la vista que la genera." +#: contrib/admin/views/doc.py:171 +#, python-format +msgid "Model %(name)r not found in app %(label)r" +msgstr "El model %(name)r no s'ha trobat en la aplicació %(label)r" -#: contrib/admin/templates/admin_doc/bookmarklets.html:22 -msgid "Show object ID" -msgstr "Mostra el ID de l'objecte" +#: contrib/admin/views/doc.py:183 +#, python-format +msgid "the related `%(label)s.%(type)s` object" +msgstr "el objecte relacionat `%(label)s.%(type)s`" -#: contrib/admin/templates/admin_doc/bookmarklets.html:23 -msgid "" -"Shows the content-type and unique ID for pages that represent a single " -"object." -msgstr "" -"Mostra el 'content-type' (tipus de contingut) i el ID inequívoc de les " -"pàgines que representen un únic objecte." +#: contrib/admin/views/doc.py:183 contrib/admin/views/doc.py:205 +#: contrib/admin/views/doc.py:219 contrib/admin/views/doc.py:224 +msgid "model:" +msgstr "model:" -#: contrib/admin/templates/admin_doc/bookmarklets.html:25 -msgid "Edit this object (current window)" -msgstr "Editar aquest objecte (finestra actual)" +#: contrib/admin/views/doc.py:214 +#, python-format +msgid "related `%(label)s.%(name)s` objects" +msgstr "objectes relacionats `%(label)s.%(name)s`" -#: contrib/admin/templates/admin_doc/bookmarklets.html:26 -msgid "Jumps to the admin page for pages that represent a single object." -msgstr "" -"El porta a la pàgina d'administració de pàgines que representen un únic " -"objecte." +#: contrib/admin/views/doc.py:219 +#, python-format +msgid "all %s" +msgstr "tots %s" -#: contrib/admin/templates/admin_doc/bookmarklets.html:28 -msgid "Edit this object (new window)" -msgstr "Editar aquest objecte (nova finestra)" +#: contrib/admin/views/doc.py:224 +#, python-format +msgid "number of %s" +msgstr "nombre de %s" -#: contrib/admin/templates/admin_doc/bookmarklets.html:29 -msgid "As above, but opens the admin page in a new window." -msgstr "Com abans, però obre la pàgina d'administració en una nova finestra." +#: contrib/admin/views/doc.py:229 +#, python-format +msgid "Fields on %s objects" +msgstr "Camps en objectes %s" -#: contrib/admin/templates/widget/date_time.html:3 -msgid "Date:" -msgstr "Data:" +#: contrib/admin/views/doc.py:291 contrib/admin/views/doc.py:302 +#: contrib/admin/views/doc.py:304 contrib/admin/views/doc.py:310 +#: contrib/admin/views/doc.py:311 contrib/admin/views/doc.py:313 +msgid "Integer" +msgstr "Enter" -#: contrib/admin/templates/widget/date_time.html:4 -msgid "Time:" -msgstr "Hora:" +#: contrib/admin/views/doc.py:292 +msgid "Boolean (Either True or False)" +msgstr "Booleà (Verdader o Fals)" -#: contrib/admin/templates/widget/file.html:2 -msgid "Currently:" -msgstr "Actualment:" +#: contrib/admin/views/doc.py:293 contrib/admin/views/doc.py:312 +#, python-format +msgid "String (up to %(maxlength)s)" +msgstr "Cadena (fins a %(maxlength)s)" -#: contrib/admin/templates/widget/file.html:3 -msgid "Change:" -msgstr "Modificar:" +#: contrib/admin/views/doc.py:294 +msgid "Comma-separated integers" +msgstr "Enters separats per comes" -#: contrib/admin/templates/registration/password_reset_email.html:2 -msgid "You're receiving this e-mail because you requested a password reset" -msgstr "" -"Està rebent aquest missatge degut a que va solicitar un restabliment de " -"contrasenya." +#: contrib/admin/views/doc.py:295 +msgid "Date (without time)" +msgstr "Data (sense hora)" -#: contrib/admin/templates/registration/password_reset_email.html:3 -#, python-format -msgid "for your user account at %(site_name)s" -msgstr "del seu compte d'usuari a %(site_name)s." +#: contrib/admin/views/doc.py:296 +msgid "Date (with time)" +msgstr "Data (amb hora)" -#: contrib/admin/templates/registration/password_reset_email.html:5 -#, python-format -msgid "Your new password is: %(new_password)s" -msgstr "La seva nova contrasenya és: %(new_password)s" +#: contrib/admin/views/doc.py:297 +msgid "Decimal number" +msgstr "Número decimal" -#: contrib/admin/templates/registration/password_reset_email.html:7 -msgid "Feel free to change this password by going to this page:" -msgstr "Sentis lliure de canviar-la en aquesta pàgina:" +#: contrib/admin/views/doc.py:298 +msgid "E-mail address" +msgstr "Adreça de correu electrònic" -#: contrib/admin/templates/registration/password_reset_email.html:11 -msgid "Your username, in case you've forgotten:" -msgstr "El seu nom d'usuari, en cas d'haver-lo oblidat:" +#: contrib/admin/views/doc.py:299 contrib/admin/views/doc.py:300 +#: contrib/admin/views/doc.py:303 +msgid "File path" +msgstr "Ruta del fitxer" -#: contrib/admin/templates/registration/password_reset_email.html:13 -msgid "Thanks for using our site!" -msgstr "Gràcies per fer us del nostre lloc!" +#: contrib/admin/views/doc.py:301 +msgid "Floating point number" +msgstr "Número amb punt de coma flotant" -#: contrib/admin/templates/registration/password_reset_email.html:15 -#, python-format -msgid "The %(site_name)s team" -msgstr "L'equip de %(site_name)s" +#: contrib/admin/views/doc.py:307 +msgid "Boolean (Either True, False or None)" +msgstr "Booleà (Verdader, Fals o 'None' (cap))" -#: contrib/admin/templates/registration/password_reset_form.html:4 -#: contrib/admin/templates/registration/password_reset_form.html:6 -#: contrib/admin/templates/registration/password_reset_form.html:10 -#: contrib/admin/templates/registration/password_reset_done.html:4 -msgid "Password reset" -msgstr "Restablir contrasenya" +#: contrib/admin/views/doc.py:308 +msgid "Relation to parent model" +msgstr "Relació amb el model pare" -#: contrib/admin/templates/registration/password_reset_form.html:12 -msgid "" -"Forgotten your password? Enter your e-mail address below, and we'll reset " -"your password and e-mail the new one to you." -msgstr "" -"Ha oblidat la seva contrasenya? Introdueixi la seva adreça de correu " -"electrònic i crearem una nova que li enviarem per correu." +#: contrib/admin/views/doc.py:309 +msgid "Phone number" +msgstr "Número de telèfon" -#: contrib/admin/templates/registration/password_reset_form.html:16 -msgid "E-mail address:" -msgstr "Adreça de correu electrònic:" +#: contrib/admin/views/doc.py:314 +msgid "Text" +msgstr "Texte" -#: contrib/admin/templates/registration/password_reset_form.html:16 -msgid "Reset my password" -msgstr "Restablir la meva contrasenya" +#: contrib/admin/views/doc.py:315 +msgid "Time" +msgstr "Hora" -#: contrib/admin/templates/registration/password_reset_done.html:6 -#: contrib/admin/templates/registration/password_reset_done.html:10 -msgid "Password reset successful" -msgstr "Contrasenya restaber-ta amb èxit" +#: contrib/admin/views/doc.py:316 contrib/flatpages/models.py:7 +msgid "URL" +msgstr "URL" + +#: contrib/admin/views/doc.py:317 +msgid "U.S. state (two uppercase letters)" +msgstr "Estat dels E.U.A. (dos lletres majúscules)" + +#: contrib/admin/views/doc.py:318 +msgid "XML text" +msgstr "Texte XML" -#: contrib/admin/templates/registration/password_reset_done.html:12 +#: contrib/admin/views/doc.py:344 +#, python-format +msgid "%s does not appear to be a urlpattern object" +msgstr "%s no sembla ser un objecte 'urlpattern'" + +#: contrib/admin/views/decorators.py:10 contrib/auth/forms.py:60 msgid "" -"We've e-mailed a new password to the e-mail address you submitted. You " -"should be receiving it shortly." +"Please enter a correct username and password. Note that both fields are case-" +"sensitive." msgstr "" -"Li hem enviat una contrasenya nova a l'adreça de correu electrònic que ens " -"ha indicat. L'hauria de rebre en breu." - -#: contrib/admin/templates/registration/password_change_form.html:4 -#: contrib/admin/templates/registration/password_change_form.html:6 -#: contrib/admin/templates/registration/password_change_form.html:10 -#: contrib/admin/templates/registration/password_change_done.html:4 -msgid "Password change" -msgstr "Canvi de clau" +"Si us plau, introdueixi un nom d'usuari i contrasenya vàlids. Tingui en " +"compte que tots dos camps son sensibles a majúscules i minúscules." -#: contrib/admin/templates/registration/password_change_form.html:12 +#: contrib/admin/views/decorators.py:62 msgid "" -"Please enter your old password, for security's sake, and then enter your new " -"password twice so we can verify you typed it in correctly." +"Please log in again, because your session has expired. Don't worry: Your " +"submission has been saved." msgstr "" -"Si us plau, introdueixi la seva contrasenya antiga, per seguretat, i tot " -"seguit introdueixi la seva nova contrasenya dues vegades per verificar que " -"l'ha escrit correctament." +"Si us plau, identifiquis de nou doncs la seva sessió ha expirat. No es " +"preocupi, el seu enviament està emmagatzemat." -#: contrib/admin/templates/registration/password_change_form.html:17 -msgid "Old password:" -msgstr "Contrasenya antiga:" +#: contrib/admin/views/decorators.py:69 +msgid "" +"Looks like your browser isn't configured to accept cookies. Please enable " +"cookies, reload this page, and try again." +msgstr "" +"Sembla ser que el seu navegador no està configurat per acceptar " +"'cookies' (galetes). Si us plau, habiliti les 'cookies', recarregui aquesta " +"pàgina i provi-ho de nou. " -#: contrib/admin/templates/registration/password_change_form.html:19 -msgid "New password:" -msgstr "Contrasenya nova:" +#: contrib/admin/views/decorators.py:83 +msgid "Usernames cannot contain the '@' character." +msgstr "Els noms d'usuari no poden contenir el caracter '@'." -#: contrib/admin/templates/registration/password_change_form.html:21 -msgid "Confirm password:" -msgstr "Confirmar contrasenya:" +#: contrib/admin/views/decorators.py:85 +#, python-format +msgid "Your e-mail address is not your username. Try '%s' instead." +msgstr "" +"La seva adreça de correu no és el seu nom d'usuari. Provi '%s' en tot cas." -#: contrib/admin/templates/registration/password_change_form.html:23 -msgid "Change my password" -msgstr "Canviar la meva clau:" +#: contrib/sessions/models.py:68 +msgid "session key" +msgstr "clau de la sessió" -#: contrib/admin/templates/registration/password_change_done.html:6 -#: contrib/admin/templates/registration/password_change_done.html:10 -msgid "Password change successful" -msgstr "Canvi de clau exitò" +#: contrib/sessions/models.py:69 +msgid "session data" +msgstr "dades de la sessió" -#: contrib/admin/templates/registration/password_change_done.html:12 -msgid "Your password was changed." -msgstr "La seva clau ha estat canviada." +#: contrib/sessions/models.py:70 +msgid "expire date" +msgstr "data de caducitat" -#: contrib/admin/templates/registration/logged_out.html:8 -msgid "Thanks for spending some quality time with the Web site today." -msgstr "Gràcies per emprar algun temps de cualitat amb el lloc web avui." +#: contrib/sessions/models.py:74 +msgid "session" +msgstr "sessió" -#: contrib/admin/templates/registration/logged_out.html:10 -msgid "Log in again" -msgstr "Iniciar sessió de nou" +#: contrib/sessions/models.py:75 +msgid "sessions" +msgstr "sessions" -#: contrib/localflavor/fi/forms.py:14 contrib/localflavor/de/forms.py:16 -#: contrib/localflavor/fr/forms.py:17 -msgid "Enter a zip code in the format XXXXX." -msgstr "Introdueixi un codi zip en el format XXXXX." +#: contrib/humanize/templatetags/humanize.py:17 +msgid "th" +msgstr "rt" -#: contrib/localflavor/fi/forms.py:40 contrib/localflavor/fi/forms.py:45 -msgid "Enter a valid Finnish social security number." -msgstr "Introdueixi un número vàlid de la seguretat social finlandesa." +#: contrib/humanize/templatetags/humanize.py:17 +msgid "st" +msgstr "r" -#: contrib/localflavor/jp/forms.py:21 -msgid "Enter a postal code in the format XXXXXXX or XXX-XXXX." -msgstr "Introdueixi un codi postal en el format XXXXXXX o XX-XXXX." +#: contrib/humanize/templatetags/humanize.py:17 +msgid "nd" +msgstr "n" -#: contrib/localflavor/jp/jp_prefectures.py:4 -msgid "Hokkaido" -msgstr "Hokkaido" +#: contrib/humanize/templatetags/humanize.py:17 +msgid "rd" +msgstr "r" -#: contrib/localflavor/jp/jp_prefectures.py:5 -msgid "Aomori" -msgstr "Aomori" +#: contrib/humanize/templatetags/humanize.py:47 +#, python-format +msgid "%(value).1f million" +msgid_plural "%(value).1f million" +msgstr[0] "%(value).1f milió" +msgstr[1] "%(value).1f milions" -#: contrib/localflavor/jp/jp_prefectures.py:6 -msgid "Iwate" -msgstr "Iwate" +#: contrib/humanize/templatetags/humanize.py:50 +#, python-format +msgid "%(value).1f billion" +msgid_plural "%(value).1f billion" +msgstr[0] "%(value).1f bilió" +msgstr[1] "%(value).1f bilions" -#: contrib/localflavor/jp/jp_prefectures.py:7 -msgid "Miyagi" -msgstr "Miyagi" +#: contrib/humanize/templatetags/humanize.py:53 +#, python-format +msgid "%(value).1f trillion" +msgid_plural "%(value).1f trillion" +msgstr[0] "%(value).1f trilió" +msgstr[1] "%(value).1f trilions" -#: contrib/localflavor/jp/jp_prefectures.py:8 -msgid "Akita" -msgstr "Akita" +#: contrib/humanize/templatetags/humanize.py:68 +msgid "one" +msgstr "un" -#: contrib/localflavor/jp/jp_prefectures.py:9 -msgid "Yamagata" -msgstr "Yamagata" +#: contrib/humanize/templatetags/humanize.py:68 +msgid "two" +msgstr "dos" -#: contrib/localflavor/jp/jp_prefectures.py:10 -msgid "Fukushima" -msgstr "Fukushima" +#: contrib/humanize/templatetags/humanize.py:68 +msgid "three" +msgstr "tres" -#: contrib/localflavor/jp/jp_prefectures.py:11 -msgid "Ibaraki" -msgstr "Ibaraki" +#: contrib/humanize/templatetags/humanize.py:68 +msgid "four" +msgstr "cuatre" -#: contrib/localflavor/jp/jp_prefectures.py:12 -msgid "Tochigi" -msgstr "Tochigi" +#: contrib/humanize/templatetags/humanize.py:68 +msgid "five" +msgstr "cinc" -#: contrib/localflavor/jp/jp_prefectures.py:13 -msgid "Gunma" -msgstr "Gunma" +#: contrib/humanize/templatetags/humanize.py:68 +msgid "six" +msgstr "sis" -#: contrib/localflavor/jp/jp_prefectures.py:14 -msgid "Saitama" -msgstr "Saitama" +#: contrib/humanize/templatetags/humanize.py:68 +msgid "seven" +msgstr "set" -#: contrib/localflavor/jp/jp_prefectures.py:15 -msgid "Chiba" -msgstr "Chiba" +#: contrib/humanize/templatetags/humanize.py:68 +msgid "eight" +msgstr "vuit" -#: contrib/localflavor/jp/jp_prefectures.py:16 -msgid "Tokyo" -msgstr "Tokyo" +#: contrib/humanize/templatetags/humanize.py:68 +msgid "nine" +msgstr "nou" -#: contrib/localflavor/jp/jp_prefectures.py:17 -msgid "Kanagawa" -msgstr "Kanagawa" +#: contrib/redirects/models.py:7 +msgid "redirect from" +msgstr "redreçar des de" -#: contrib/localflavor/jp/jp_prefectures.py:18 -msgid "Yamanashi" -msgstr "Yamanashi" +#: contrib/redirects/models.py:8 +msgid "" +"This should be an absolute path, excluding the domain name. Example: '/" +"events/search/'." +msgstr "" +"Aquesta ruta hauria de ser el camí absolut, excloent-ne el nom del domini. " +"Exemple '/events/search/'." -#: contrib/localflavor/jp/jp_prefectures.py:19 -msgid "Nagano" -msgstr "Nagano" +#: contrib/redirects/models.py:9 +msgid "redirect to" +msgstr "redreçar a" -#: contrib/localflavor/jp/jp_prefectures.py:20 -msgid "Niigata" -msgstr "Niigata" +#: contrib/redirects/models.py:10 +msgid "" +"This can be either an absolute path (as above) or a full URL starting with " +"'http://'." +msgstr "" +"Això pot ser bé una ruta absoluta (com abans) o una URL completa que comenci " +"per http:// ." -#: contrib/localflavor/jp/jp_prefectures.py:21 -msgid "Toyama" -msgstr "Toyama" +#: contrib/redirects/models.py:13 +msgid "redirect" +msgstr "redreçament" -#: contrib/localflavor/jp/jp_prefectures.py:22 -msgid "Ishikawa" -msgstr "Ishikawa" +#: contrib/redirects/models.py:14 +msgid "redirects" +msgstr "redreçaments" -#: contrib/localflavor/jp/jp_prefectures.py:23 -msgid "Fukui" -msgstr "Fukui" +#: contrib/flatpages/models.py:8 +msgid "" +"Example: '/about/contact/'. Make sure to have leading and trailing slashes." +msgstr "" +"Exemple: '/about/contact/'. Asseguri's de posar les barres al principi i al " +"final." -#: contrib/localflavor/jp/jp_prefectures.py:24 -msgid "Gifu" -msgstr "Gifu" +#: contrib/flatpages/models.py:9 +msgid "title" +msgstr "tìtol" -#: contrib/localflavor/jp/jp_prefectures.py:25 -msgid "Shizuoka" -msgstr "Shizuoka" +#: contrib/flatpages/models.py:10 +msgid "content" +msgstr "contingut" -#: contrib/localflavor/jp/jp_prefectures.py:26 -msgid "Aichi" -msgstr "Aichi" +#: contrib/flatpages/models.py:11 +msgid "enable comments" +msgstr "habilitar comentaris" -#: contrib/localflavor/jp/jp_prefectures.py:27 -msgid "Mie" -msgstr "Mie" +#: contrib/flatpages/models.py:12 +msgid "template name" +msgstr "nom de la plantilla" -#: contrib/localflavor/jp/jp_prefectures.py:28 -msgid "Shiga" -msgstr "Shiga" +#: contrib/flatpages/models.py:13 +msgid "" +"Example: 'flatpages/contact_page.html'. If this isn't provided, the system " +"will use 'flatpages/default.html'." +msgstr "" +"Exemple: 'flatpages/contact_page.html'. Si no el proporciona, el sistema " +"utilitzarà 'flatpages/defaula.htmlt'." -#: contrib/localflavor/jp/jp_prefectures.py:29 -msgid "Kyoto" -msgstr "Kyoto" +#: contrib/flatpages/models.py:14 +msgid "registration required" +msgstr "s'ha de estar registrat" -#: contrib/localflavor/jp/jp_prefectures.py:30 -msgid "Osaka" -msgstr "Osaka" +#: contrib/flatpages/models.py:14 +msgid "If this is checked, only logged-in users will be able to view the page." +msgstr "Si està marcat, només els usuaris registrats podran veure la pàgina." -#: contrib/localflavor/jp/jp_prefectures.py:31 -msgid "Hyogo" -msgstr "Hyogo" +#: contrib/flatpages/models.py:18 +msgid "flat page" +msgstr "pàgina estàtica" -#: contrib/localflavor/jp/jp_prefectures.py:32 -msgid "Nara" -msgstr "Nara" +#: contrib/flatpages/models.py:19 +msgid "flat pages" +msgstr "pàgines estàtiques" -#: contrib/localflavor/jp/jp_prefectures.py:33 -msgid "Wakayama" -msgstr "Wakayama" +#: contrib/sites/models.py:10 +msgid "domain name" +msgstr "nom del domini" -#: contrib/localflavor/jp/jp_prefectures.py:34 -msgid "Tottori" -msgstr "Tottori" +#: contrib/sites/models.py:11 +msgid "display name" +msgstr "nom per mostrar" -#: contrib/localflavor/jp/jp_prefectures.py:35 -msgid "Shimane" -msgstr "Shimane" +#: contrib/sites/models.py:15 +msgid "site" +msgstr "lloc" -#: contrib/localflavor/jp/jp_prefectures.py:36 -msgid "Okayama" -msgstr "Okayama" +#: contrib/sites/models.py:16 +msgid "sites" +msgstr "llocs" -#: contrib/localflavor/jp/jp_prefectures.py:37 -msgid "Hiroshima" -msgstr "Hiroshima" +#: contrib/auth/views.py:40 +msgid "Logged out" +msgstr "Sessió finalitzada" -#: contrib/localflavor/jp/jp_prefectures.py:38 -msgid "Yamaguchi" -msgstr "Yamaguchi" +#: contrib/auth/models.py:44 contrib/auth/models.py:64 +msgid "name" +msgstr "nom" -#: contrib/localflavor/jp/jp_prefectures.py:39 -msgid "Tokushima" -msgstr "Tokushima" +#: contrib/auth/models.py:46 +msgid "codename" +msgstr "nom en clau" -#: contrib/localflavor/jp/jp_prefectures.py:40 -msgid "Kagawa" -msgstr "Kagawa" +#: contrib/auth/models.py:49 +msgid "permission" +msgstr "permís" -#: contrib/localflavor/jp/jp_prefectures.py:41 -msgid "Ehime" -msgstr "Ehime" +#: contrib/auth/models.py:50 contrib/auth/models.py:65 +msgid "permissions" +msgstr "permissos" -#: contrib/localflavor/jp/jp_prefectures.py:42 -msgid "Kochi" -msgstr "Kochi" +#: contrib/auth/models.py:68 +msgid "group" +msgstr "grup" -#: contrib/localflavor/jp/jp_prefectures.py:43 -msgid "Fukuoka" -msgstr "Fukuoka" +#: contrib/auth/models.py:69 contrib/auth/models.py:109 +msgid "groups" +msgstr "grups" -#: contrib/localflavor/jp/jp_prefectures.py:44 -msgid "Saga" -msgstr "Saga" +#: contrib/auth/models.py:99 +msgid "username" +msgstr "nom d'usuari" -#: contrib/localflavor/jp/jp_prefectures.py:45 -msgid "Nagasaki" -msgstr "Nagasaki" +#: contrib/auth/models.py:99 +msgid "" +"Required. 30 characters or fewer. Alphanumeric characters only (letters, " +"digits and underscores)." +msgstr "" +"Requerit. 30 o menys caracters. Només caracters alfanumèrics (lletres, " +"dígits i guions baixos)." -#: contrib/localflavor/jp/jp_prefectures.py:46 -msgid "Kumamoto" -msgstr "Kumamoto" +#: contrib/auth/models.py:100 +msgid "first name" +msgstr "nom propi" -#: contrib/localflavor/jp/jp_prefectures.py:47 -msgid "Oita" -msgstr "Oita" +#: contrib/auth/models.py:101 +msgid "last name" +msgstr "cognoms" -#: contrib/localflavor/jp/jp_prefectures.py:48 -msgid "Miyazaki" -msgstr "Miyazaki" +#: contrib/auth/models.py:102 +msgid "e-mail address" +msgstr "adreça de correu electrònic" -#: contrib/localflavor/jp/jp_prefectures.py:49 -msgid "Kagoshima" -msgstr "Kagoshima" +#: contrib/auth/models.py:103 +msgid "password" +msgstr "contrasenya" -#: contrib/localflavor/jp/jp_prefectures.py:50 -msgid "Okinawa" -msgstr "Okinawa" +#: contrib/auth/models.py:103 +msgid "" +"Use '[algo]$[salt]$[hexdigest]' or use the change " +"password form." +msgstr "" +"Utilitzi '[algo]$[salt]$[hexdigest]' o el formulari de " +"canvi de contrasenya." -#: contrib/localflavor/it/forms.py:16 -msgid "Enter a valid zip code." -msgstr "Introdueixi un codi zip vàlid." +#: contrib/auth/models.py:104 +msgid "staff status" +msgstr "és membre del personal" -#: contrib/localflavor/it/forms.py:41 -msgid "Enter a valid Social Security number." -msgstr "Introdueixi un número valid de la Seguretat Social." +#: contrib/auth/models.py:104 +msgid "Designates whether the user can log into this admin site." +msgstr "Indica si l'usuari pot entrar en el lloc administratiu." -#: contrib/localflavor/it/forms.py:68 -msgid "Enter a valid VAT number." -msgstr "Introdueixi un número de IVA (VAT) vàlid." +#: contrib/auth/models.py:105 +msgid "active" +msgstr "actiu" -#: contrib/localflavor/de/forms.py:60 +#: contrib/auth/models.py:105 msgid "" -"Enter a valid German identity card number in XXXXXXXXXXX-XXXXXXX-XXXXXXX-X " -"format." +"Designates whether this user can log into the Django admin. Unselect this " +"instead of deleting accounts." msgstr "" -"Introdueixi un número de tarjeta d'identificació alemany vàlid en el format " -"XXXXXXXXXXX-XXXXXXX-XXXXXXX-X." - -#: contrib/localflavor/de/de_states.py:5 -msgid "Baden-Wuerttemberg" -msgstr "Baden-Wuerttemberg" +"Designa si aquest usuari pot iniciar sessió a la interfície administrativa " +"Djano. Deselecciona-ho enlloc de esborrar comptes d'usuari." -#: contrib/localflavor/de/de_states.py:6 -msgid "Bavaria" -msgstr "Bavaria" +#: contrib/auth/models.py:106 +msgid "superuser status" +msgstr "estat de superusuari" -#: contrib/localflavor/de/de_states.py:7 -msgid "Berlin" -msgstr "Berlin" +#: contrib/auth/models.py:106 +msgid "" +"Designates that this user has all permissions without explicitly assigning " +"them." +msgstr "" +"Designa que aquest usuari té tots els permisos sense assignar-los " +"explícitament." -#: contrib/localflavor/de/de_states.py:8 -msgid "Brandenburg" -msgstr "Brandenburg" +#: contrib/auth/models.py:107 +msgid "last login" +msgstr "últim inici de sessió" -#: contrib/localflavor/de/de_states.py:9 -msgid "Bremen" -msgstr "Bremen" +#: contrib/auth/models.py:108 +msgid "date joined" +msgstr "data de creació" -#: contrib/localflavor/de/de_states.py:10 -msgid "Hamburg" -msgstr "Hamburg" +#: contrib/auth/models.py:110 +msgid "" +"In addition to the permissions manually assigned, this user will also get " +"all permissions granted to each group he/she is in." +msgstr "" +"Junt amb els permissos asignats manualment, aquest usuari tindrà, també, els " +"permissos dels grups dels que sigui membre." -#: contrib/localflavor/de/de_states.py:11 -msgid "Hessen" -msgstr "Hessen" +#: contrib/auth/models.py:111 +msgid "user permissions" +msgstr "permissos de l'usuari" -#: contrib/localflavor/de/de_states.py:12 -msgid "Mecklenburg-Western Pomerania" -msgstr "Mecklenburg-Western Pomerania" +#: contrib/auth/models.py:115 +msgid "user" +msgstr "usuari" -#: contrib/localflavor/de/de_states.py:13 -msgid "Lower Saxony" -msgstr "Lower Saxony" +#: contrib/auth/models.py:116 +msgid "users" +msgstr "usuaris" -#: contrib/localflavor/de/de_states.py:14 -msgid "North Rhine-Westphalia" -msgstr "North Rhine-Westphalia" +#: contrib/auth/models.py:122 +msgid "Personal info" +msgstr "Informaciò personal" -#: contrib/localflavor/de/de_states.py:15 -msgid "Rhineland-Palatinate" -msgstr "Rhineland-Palatinate" +#: contrib/auth/models.py:123 +msgid "Permissions" +msgstr "permissos" -#: contrib/localflavor/de/de_states.py:16 -msgid "Saarland" -msgstr "Saarland" +#: contrib/auth/models.py:124 +msgid "Important dates" +msgstr "Dates importants" -#: contrib/localflavor/de/de_states.py:17 -msgid "Saxony" -msgstr "Saxony" +#: contrib/auth/models.py:125 +msgid "Groups" +msgstr "Grups" -#: contrib/localflavor/de/de_states.py:18 -msgid "Saxony-Anhalt" -msgstr "Saxony-Anhalt" +#: contrib/auth/models.py:269 +msgid "message" +msgstr "missatge" -#: contrib/localflavor/de/de_states.py:19 -msgid "Schleswig-Holstein" -msgstr "Schleswig-Holstein" +#: contrib/auth/models.py:282 +msgid "AnonymousUser" +msgstr "AnonymousUser" -#: contrib/localflavor/de/de_states.py:20 -msgid "Thuringia" -msgstr "Thuringia" +#: contrib/auth/forms.py:17 contrib/auth/forms.py:138 +msgid "The two password fields didn't match." +msgstr "Els dos camps de contrasenya no coincideixen." -#: contrib/localflavor/us/forms.py:18 -msgid "Enter a zip code in the format XXXXX or XXXXX-XXXX." -msgstr "Introdueixi un codi zip en el format XXXXX o XXXXX-XXXX." +#: contrib/auth/forms.py:25 +msgid "A user with that username already exists." +msgstr "Ja existeix un usuari amb aquest nom." -#: contrib/localflavor/us/forms.py:51 -msgid "Enter a valid U.S. Social Security number in XXX-XX-XXXX format." +#: contrib/auth/forms.py:53 +msgid "" +"Your Web browser doesn't appear to have cookies enabled. Cookies are " +"required for logging in." msgstr "" -"Introdueixi un número vàlid de la Seguretat Social dels E.U.A. en el format " -"XXX-XX-XXXX." +"El seu navegador no sembla tenir les 'cookies' (galetes) activades. Aquestes " +"són necessàries per iniciar la sessió." -#: contrib/localflavor/ch/forms.py:18 contrib/localflavor/no/forms.py:15 -msgid "Enter a zip code in the format XXXX." -msgstr "Introdueixi un codi zip en el format XXXX." +#: contrib/auth/forms.py:62 +msgid "This account is inactive." +msgstr "Aquest compte està inactiu" -#: contrib/localflavor/ch/forms.py:90 +#: contrib/auth/forms.py:85 msgid "" -"Enter a valid Swiss identity or passport card number in X1234567<0 or " -"1234567890 format." +"That e-mail address doesn't have an associated user account. Are you sure " +"you've registered?" msgstr "" -"Introdueixi un número de identificació o de passaport Suïssos en els formats " -"1234567890 o X1234567<0." +"Aquesta adreça de correu no té associada cap compte d'usuari. Està segur de " +"que s'ha registrat?" + +#: contrib/auth/forms.py:117 +msgid "The two 'new password' fields didn't match." +msgstr "Els dos camps de nova contrasenya no coincideixen." + +#: contrib/auth/forms.py:124 +msgid "Your old password was entered incorrectly. Please enter it again." +msgstr "" +"La seva antiga contrasenya no és correcte. Si el plau, introdueixi-la de nou." + +#: contrib/contenttypes/models.py:36 +msgid "python model class name" +msgstr "nom de la classe del model en python" + +#: contrib/contenttypes/models.py:39 +msgid "content type" +msgstr "tipus de contingut" + +#: contrib/contenttypes/models.py:40 +msgid "content types" +msgstr "tipus de continguts" #: contrib/localflavor/ch/ch_states.py:5 msgid "Aargau" @@ -1935,47 +1994,58 @@ msgstr "Zug" msgid "Zurich" msgstr "Zurich" -#: contrib/localflavor/au/forms.py:18 -msgid "Enter a 4 digit post code." -msgstr "Introdueixi un codi postal de 4 dígits." +#: contrib/localflavor/ch/forms.py:18 contrib/localflavor/no/forms.py:15 +msgid "Enter a zip code in the format XXXX." +msgstr "Introdueixi un codi zip en el format XXXX." -#: contrib/localflavor/is_/forms.py:16 +#: contrib/localflavor/ch/forms.py:90 msgid "" -"Enter a valid Icelandic identification number. The format is XXXXXX-XXXX." +"Enter a valid Swiss identity or passport card number in X1234567<0 or " +"1234567890 format." msgstr "" -"Introdueixi un número de identificació d'Islàndia. El format és XXXXXX-XXXX." +"Introdueixi un número de identificació o de passaport Suïssos en els formats " +"1234567890 o X1234567<0." -#: contrib/localflavor/is_/forms.py:30 -msgid "The Icelandic identification number is not valid." -msgstr "El número de identificació d'Islàndia no és vàlid." +#: contrib/localflavor/it/forms.py:16 +msgid "Enter a valid zip code." +msgstr "Introdueixi un codi zip vàlid." -#: contrib/localflavor/br/forms.py:18 -msgid "Enter a zip code in the format XXXXX-XXX." -msgstr "Introdueixi un codi zip en el format XXXXX-XXX." +#: contrib/localflavor/it/forms.py:41 +msgid "Enter a valid Social Security number." +msgstr "Introdueixi un número valid de la Seguretat Social." -#: contrib/localflavor/br/forms.py:30 -msgid "Phone numbers must be in XX-XXXX-XXXX format." -msgstr "El número de telèfon ha de ser en el format XX-XXXX-XXXX." +#: contrib/localflavor/it/forms.py:68 +msgid "Enter a valid VAT number." +msgstr "Introdueixi un número de IVA (VAT) vàlid." -#: contrib/localflavor/br/forms.py:72 -msgid "This field requires only numbers." -msgstr "Aquest camps requereix només números." +#: contrib/localflavor/uk/forms.py:18 +msgid "Enter a postcode. A space is required between the two postcode parts." +msgstr "" +"Introdueixi un codi postal. És necessari un espai entre les dues parts del " +"codi postal." -#: contrib/localflavor/br/forms.py:74 -msgid "This field requires at most 11 digits or 14 characters." -msgstr "Aquest camp requereix com a màxim 11 dígits o 14 caracters." +#: contrib/localflavor/us/forms.py:18 +msgid "Enter a zip code in the format XXXXX or XXXXX-XXXX." +msgstr "Introdueixi un codi zip en el format XXXXX o XXXXX-XXXX." -#: contrib/localflavor/br/forms.py:84 -msgid "Invalid CPF number." -msgstr "Número CPF invàlid." +#: contrib/localflavor/us/forms.py:51 +msgid "Enter a valid U.S. Social Security number in XXX-XX-XXXX format." +msgstr "" +"Introdueixi un número vàlid de la Seguretat Social dels E.U.A. en el format " +"XXX-XX-XXXX." -#: contrib/localflavor/br/forms.py:106 -msgid "This field requires at least 14 digits" -msgstr "Aquest camp requereix almenys 14 dígits." +#: contrib/localflavor/fi/forms.py:14 contrib/localflavor/de/forms.py:16 +#: contrib/localflavor/fr/forms.py:17 +msgid "Enter a zip code in the format XXXXX." +msgstr "Introdueixi un codi zip en el format XXXXX." -#: contrib/localflavor/br/forms.py:116 -msgid "Invalid CNPJ number." -msgstr "Número CNPJ invàlid." +#: contrib/localflavor/fi/forms.py:40 contrib/localflavor/fi/forms.py:45 +msgid "Enter a valid Finnish social security number." +msgstr "Introdueixi un número vàlid de la seguretat social finlandesa." + +#: contrib/localflavor/no/forms.py:36 +msgid "Enter a valid Norwegian social security number." +msgstr "Introdueixi un número de la seguretat social Noruega vàlid." #: contrib/localflavor/cl/forms.py:21 msgid "Enter valid a Chilean RUT. The format is XX.XXX.XXX-X." @@ -1985,1014 +2055,980 @@ msgstr "Introdueixi un RUT Xilè vàlid. El format és XX.XXX.XXX-X" msgid "Enter valid a Chilean RUT" msgstr "Introdueixi un RUT Xilè vàlid." -#: contrib/localflavor/no/forms.py:36 -msgid "Enter a valid Norwegian social security number." -msgstr "Introdueixi un número de la seguretat social Noruega vàlid." +#: contrib/localflavor/de/de_states.py:5 +msgid "Baden-Wuerttemberg" +msgstr "Baden-Wuerttemberg" -#: contrib/localflavor/uk/forms.py:18 -msgid "Enter a postcode. A space is required between the two postcode parts." -msgstr "" -"Introdueixi un codi postal. És necessari un espai entre les dues parts del " -"codi postal." +#: contrib/localflavor/de/de_states.py:6 +msgid "Bavaria" +msgstr "Bavaria" -#: contrib/sessions/models.py:68 -msgid "session key" -msgstr "clau de la sessió" +#: contrib/localflavor/de/de_states.py:7 +msgid "Berlin" +msgstr "Berlin" -#: contrib/sessions/models.py:69 -msgid "session data" -msgstr "dades de la sessió" +#: contrib/localflavor/de/de_states.py:8 +msgid "Brandenburg" +msgstr "Brandenburg" -#: contrib/sessions/models.py:70 -msgid "expire date" -msgstr "data de caducitat" +#: contrib/localflavor/de/de_states.py:9 +msgid "Bremen" +msgstr "Bremen" -#: contrib/sessions/models.py:74 -msgid "session" -msgstr "sessió" +#: contrib/localflavor/de/de_states.py:10 +msgid "Hamburg" +msgstr "Hamburg" -#: contrib/sessions/models.py:75 -msgid "sessions" -msgstr "sessions" +#: contrib/localflavor/de/de_states.py:11 +msgid "Hessen" +msgstr "Hessen" -#: contrib/flatpages/models.py:8 -msgid "" -"Example: '/about/contact/'. Make sure to have leading and trailing slashes." -msgstr "" -"Exemple: '/about/contact/'. Asseguri's de posar les barres al principi i al " -"final." +#: contrib/localflavor/de/de_states.py:12 +msgid "Mecklenburg-Western Pomerania" +msgstr "Mecklenburg-Western Pomerania" -#: contrib/flatpages/models.py:9 -msgid "title" -msgstr "tìtol" +#: contrib/localflavor/de/de_states.py:13 +msgid "Lower Saxony" +msgstr "Lower Saxony" -#: contrib/flatpages/models.py:10 -msgid "content" -msgstr "contingut" +#: contrib/localflavor/de/de_states.py:14 +msgid "North Rhine-Westphalia" +msgstr "North Rhine-Westphalia" -#: contrib/flatpages/models.py:11 -msgid "enable comments" -msgstr "habilitar comentaris" +#: contrib/localflavor/de/de_states.py:15 +msgid "Rhineland-Palatinate" +msgstr "Rhineland-Palatinate" -#: contrib/flatpages/models.py:12 -msgid "template name" -msgstr "nom de la plantilla" +#: contrib/localflavor/de/de_states.py:16 +msgid "Saarland" +msgstr "Saarland" -#: contrib/flatpages/models.py:13 -msgid "" -"Example: 'flatpages/contact_page.html'. If this isn't provided, the system " -"will use 'flatpages/default.html'." +#: contrib/localflavor/de/de_states.py:17 +msgid "Saxony" +msgstr "Saxony" + +#: contrib/localflavor/de/de_states.py:18 +msgid "Saxony-Anhalt" +msgstr "Saxony-Anhalt" + +#: contrib/localflavor/de/de_states.py:19 +msgid "Schleswig-Holstein" +msgstr "Schleswig-Holstein" + +#: contrib/localflavor/de/de_states.py:20 +msgid "Thuringia" +msgstr "Thuringia" + +#: contrib/localflavor/de/forms.py:60 +msgid "" +"Enter a valid German identity card number in XXXXXXXXXXX-XXXXXXX-XXXXXXX-X " +"format." msgstr "" -"Exemple: 'flatpages/contact_page.html'. Si no el proporciona, el sistema " -"utilitzarà 'flatpages/defaula.htmlt'." +"Introdueixi un número de tarjeta d'identificació alemany vàlid en el format " +"XXXXXXXXXXX-XXXXXXX-XXXXXXX-X." -#: contrib/flatpages/models.py:14 -msgid "registration required" -msgstr "s'ha de estar registrat" +#: contrib/localflavor/au/forms.py:18 +msgid "Enter a 4 digit post code." +msgstr "Introdueixi un codi postal de 4 dígits." -#: contrib/flatpages/models.py:14 -msgid "If this is checked, only logged-in users will be able to view the page." -msgstr "Si està marcat, només els usuaris registrats podran veure la pàgina." +#: contrib/localflavor/jp/jp_prefectures.py:4 +msgid "Hokkaido" +msgstr "Hokkaido" -#: contrib/flatpages/models.py:18 -msgid "flat page" -msgstr "pàgina estàtica" +#: contrib/localflavor/jp/jp_prefectures.py:5 +msgid "Aomori" +msgstr "Aomori" -#: contrib/flatpages/models.py:19 -msgid "flat pages" -msgstr "pàgines estàtiques" +#: contrib/localflavor/jp/jp_prefectures.py:6 +msgid "Iwate" +msgstr "Iwate" -#: contrib/humanize/templatetags/humanize.py:17 -msgid "th" -msgstr "" +#: contrib/localflavor/jp/jp_prefectures.py:7 +msgid "Miyagi" +msgstr "Miyagi" -#: contrib/humanize/templatetags/humanize.py:17 -msgid "st" -msgstr "r" +#: contrib/localflavor/jp/jp_prefectures.py:8 +msgid "Akita" +msgstr "Akita" -#: contrib/humanize/templatetags/humanize.py:17 -msgid "nd" -msgstr "n" +#: contrib/localflavor/jp/jp_prefectures.py:9 +msgid "Yamagata" +msgstr "Yamagata" -#: contrib/humanize/templatetags/humanize.py:17 -msgid "rd" -msgstr "r" +#: contrib/localflavor/jp/jp_prefectures.py:10 +msgid "Fukushima" +msgstr "Fukushima" -#: contrib/humanize/templatetags/humanize.py:47 -#, python-format -msgid "%(value).1f million" -msgid_plural "%(value).1f million" -msgstr[0] "%(value).1f milió" -msgstr[1] "%(value).1f milions" +#: contrib/localflavor/jp/jp_prefectures.py:11 +msgid "Ibaraki" +msgstr "Ibaraki" -#: contrib/humanize/templatetags/humanize.py:50 -#, python-format -msgid "%(value).1f billion" -msgid_plural "%(value).1f billion" -msgstr[0] "%(value).1f bilió" -msgstr[1] "%(value).1f bilions" +#: contrib/localflavor/jp/jp_prefectures.py:12 +msgid "Tochigi" +msgstr "Tochigi" -#: contrib/humanize/templatetags/humanize.py:53 -#, python-format -msgid "%(value).1f trillion" -msgid_plural "%(value).1f trillion" -msgstr[0] "%(value).1f trilió" -msgstr[1] "%(value).1f trilions" +#: contrib/localflavor/jp/jp_prefectures.py:13 +msgid "Gunma" +msgstr "Gunma" -#: contrib/humanize/templatetags/humanize.py:68 -msgid "one" -msgstr "un" +#: contrib/localflavor/jp/jp_prefectures.py:14 +msgid "Saitama" +msgstr "Saitama" -#: contrib/humanize/templatetags/humanize.py:68 -msgid "two" -msgstr "dos" +#: contrib/localflavor/jp/jp_prefectures.py:15 +msgid "Chiba" +msgstr "Chiba" -#: contrib/humanize/templatetags/humanize.py:68 -msgid "three" -msgstr "tres" +#: contrib/localflavor/jp/jp_prefectures.py:16 +msgid "Tokyo" +msgstr "Tokyo" -#: contrib/humanize/templatetags/humanize.py:68 -msgid "four" -msgstr "cuatre" +#: contrib/localflavor/jp/jp_prefectures.py:17 +msgid "Kanagawa" +msgstr "Kanagawa" -#: contrib/humanize/templatetags/humanize.py:68 -msgid "five" -msgstr "cinc" +#: contrib/localflavor/jp/jp_prefectures.py:18 +msgid "Yamanashi" +msgstr "Yamanashi" -#: contrib/humanize/templatetags/humanize.py:68 -msgid "six" -msgstr "sis" +#: contrib/localflavor/jp/jp_prefectures.py:19 +msgid "Nagano" +msgstr "Nagano" -#: contrib/humanize/templatetags/humanize.py:68 -msgid "seven" -msgstr "set" +#: contrib/localflavor/jp/jp_prefectures.py:20 +msgid "Niigata" +msgstr "Niigata" -#: contrib/humanize/templatetags/humanize.py:68 -msgid "eight" -msgstr "vuit" +#: contrib/localflavor/jp/jp_prefectures.py:21 +msgid "Toyama" +msgstr "Toyama" -#: contrib/humanize/templatetags/humanize.py:68 -msgid "nine" -msgstr "nou" +#: contrib/localflavor/jp/jp_prefectures.py:22 +msgid "Ishikawa" +msgstr "Ishikawa" -#: contrib/contenttypes/models.py:36 -msgid "python model class name" -msgstr "nom de la classe del model en python" +#: contrib/localflavor/jp/jp_prefectures.py:23 +msgid "Fukui" +msgstr "Fukui" -#: contrib/contenttypes/models.py:39 -msgid "content type" -msgstr "tipus de contingut" +#: contrib/localflavor/jp/jp_prefectures.py:24 +msgid "Gifu" +msgstr "Gifu" -#: contrib/contenttypes/models.py:40 -msgid "content types" -msgstr "tipus de continguts" +#: contrib/localflavor/jp/jp_prefectures.py:25 +msgid "Shizuoka" +msgstr "Shizuoka" -#: contrib/sites/models.py:10 -msgid "domain name" -msgstr "nom del domini" +#: contrib/localflavor/jp/jp_prefectures.py:26 +msgid "Aichi" +msgstr "Aichi" -#: contrib/sites/models.py:11 -msgid "display name" -msgstr "nom per mostrar" +#: contrib/localflavor/jp/jp_prefectures.py:27 +msgid "Mie" +msgstr "Mie" -#: contrib/sites/models.py:15 -msgid "site" -msgstr "lloc" +#: contrib/localflavor/jp/jp_prefectures.py:28 +msgid "Shiga" +msgstr "Shiga" -#: contrib/sites/models.py:16 -msgid "sites" -msgstr "llocs" +#: contrib/localflavor/jp/jp_prefectures.py:29 +msgid "Kyoto" +msgstr "Kyoto" -#: contrib/auth/forms.py:17 contrib/auth/forms.py:138 -msgid "The two password fields didn't match." -msgstr "Els dos camps de contrasenya no coincideixen." +#: contrib/localflavor/jp/jp_prefectures.py:30 +msgid "Osaka" +msgstr "Osaka" -#: contrib/auth/forms.py:25 -msgid "A user with that username already exists." -msgstr "Ja existeix un usuari amb aquest nom." +#: contrib/localflavor/jp/jp_prefectures.py:31 +msgid "Hyogo" +msgstr "Hyogo" -#: contrib/auth/forms.py:53 -msgid "" -"Your Web browser doesn't appear to have cookies enabled. Cookies are " -"required for logging in." -msgstr "" -"El seu navegador no sembla tenir les 'cookies' (galetes) activades. Aquestes " -"són necessàries per iniciar la sessió." +#: contrib/localflavor/jp/jp_prefectures.py:32 +msgid "Nara" +msgstr "Nara" -#: contrib/auth/forms.py:62 -msgid "This account is inactive." -msgstr "Aquest compte està inactiu" +#: contrib/localflavor/jp/jp_prefectures.py:33 +msgid "Wakayama" +msgstr "Wakayama" -#: contrib/auth/forms.py:85 -msgid "" -"That e-mail address doesn't have an associated user account. Are you sure " -"you've registered?" -msgstr "" +#: contrib/localflavor/jp/jp_prefectures.py:34 +msgid "Tottori" +msgstr "Tottori" -#: contrib/auth/forms.py:117 -msgid "The two 'new password' fields didn't match." -msgstr "Els dos camps de nova contrasenya no coincideixen." +#: contrib/localflavor/jp/jp_prefectures.py:35 +msgid "Shimane" +msgstr "Shimane" -#: contrib/auth/forms.py:124 -msgid "Your old password was entered incorrectly. Please enter it again." -msgstr "" -"La seva antiga contrasenya no és correcte. Si el plau, introdueixi-la de nou." +#: contrib/localflavor/jp/jp_prefectures.py:36 +msgid "Okayama" +msgstr "Okayama" -#: contrib/auth/views.py:40 -msgid "Logged out" -msgstr "Sessió finalitzada" +#: contrib/localflavor/jp/jp_prefectures.py:37 +msgid "Hiroshima" +msgstr "Hiroshima" -#: contrib/auth/models.py:44 contrib/auth/models.py:64 -msgid "name" -msgstr "nom" +#: contrib/localflavor/jp/jp_prefectures.py:38 +msgid "Yamaguchi" +msgstr "Yamaguchi" -#: contrib/auth/models.py:46 -msgid "codename" -msgstr "nom en clau" +#: contrib/localflavor/jp/jp_prefectures.py:39 +msgid "Tokushima" +msgstr "Tokushima" -#: contrib/auth/models.py:49 -msgid "permission" -msgstr "permís" +#: contrib/localflavor/jp/jp_prefectures.py:40 +msgid "Kagawa" +msgstr "Kagawa" -#: contrib/auth/models.py:50 contrib/auth/models.py:65 -msgid "permissions" -msgstr "permissos" +#: contrib/localflavor/jp/jp_prefectures.py:41 +msgid "Ehime" +msgstr "Ehime" -#: contrib/auth/models.py:68 -msgid "group" -msgstr "grup" +#: contrib/localflavor/jp/jp_prefectures.py:42 +msgid "Kochi" +msgstr "Kochi" -#: contrib/auth/models.py:69 contrib/auth/models.py:109 -msgid "groups" -msgstr "grups" +#: contrib/localflavor/jp/jp_prefectures.py:43 +msgid "Fukuoka" +msgstr "Fukuoka" -#: contrib/auth/models.py:99 -msgid "username" -msgstr "nom d'usuari" +#: contrib/localflavor/jp/jp_prefectures.py:44 +msgid "Saga" +msgstr "Saga" -#: contrib/auth/models.py:99 -msgid "" -"Required. 30 characters or fewer. Alphanumeric characters only (letters, " -"digits and underscores)." -msgstr "" -"Requerit. 30 o menys caracters. Només caracters alfanumèrics (lletres, " -"dígits i guions baixos)." +#: contrib/localflavor/jp/jp_prefectures.py:45 +msgid "Nagasaki" +msgstr "Nagasaki" -#: contrib/auth/models.py:100 -msgid "first name" -msgstr "nom propi" +#: contrib/localflavor/jp/jp_prefectures.py:46 +msgid "Kumamoto" +msgstr "Kumamoto" -#: contrib/auth/models.py:101 -msgid "last name" -msgstr "cognoms" +#: contrib/localflavor/jp/jp_prefectures.py:47 +msgid "Oita" +msgstr "Oita" -#: contrib/auth/models.py:102 -msgid "e-mail address" -msgstr "adreça de correu electrònic" +#: contrib/localflavor/jp/jp_prefectures.py:48 +msgid "Miyazaki" +msgstr "Miyazaki" -#: contrib/auth/models.py:103 -msgid "password" -msgstr "contrasenya" +#: contrib/localflavor/jp/jp_prefectures.py:49 +msgid "Kagoshima" +msgstr "Kagoshima" -#: contrib/auth/models.py:103 -msgid "" -"Use '[algo]$[salt]$[hexdigest]' or use the change " -"password form." -msgstr "" -"Utilitzi '[algo]$[salt]$[hexdigest]' o el formulari de " -"canvi de contrasenya." +#: contrib/localflavor/jp/jp_prefectures.py:50 +msgid "Okinawa" +msgstr "Okinawa" -#: contrib/auth/models.py:104 -msgid "staff status" -msgstr "és membre del personal" +#: contrib/localflavor/jp/forms.py:21 +msgid "Enter a postal code in the format XXXXXXX or XXX-XXXX." +msgstr "Introdueixi un codi postal en el format XXXXXXX o XX-XXXX." -#: contrib/auth/models.py:104 -msgid "Designates whether the user can log into this admin site." -msgstr "Indica si l'usuari pot entrar en el lloc administratiu." +#: contrib/localflavor/br/forms.py:18 +msgid "Enter a zip code in the format XXXXX-XXX." +msgstr "Introdueixi un codi zip en el format XXXXX-XXX." -#: contrib/auth/models.py:105 -msgid "active" -msgstr "actiu" +#: contrib/localflavor/br/forms.py:30 +msgid "Phone numbers must be in XX-XXXX-XXXX format." +msgstr "El número de telèfon ha de ser en el format XX-XXXX-XXXX." -#: contrib/auth/models.py:105 -msgid "" -"Designates whether this user can log into the Django admin. Unselect this " -"instead of deleting accounts." -msgstr "" -"Designa si aquest usuari pot iniciar sessió a la interfície administrativa " -"Djano. Deselecciona-ho enlloc de esborrar comptes d'usuari." +#: contrib/localflavor/br/forms.py:72 +msgid "This field requires only numbers." +msgstr "Aquest camps requereix només números." -#: contrib/auth/models.py:106 -msgid "superuser status" -msgstr "estat de superusuari" +#: contrib/localflavor/br/forms.py:74 +msgid "This field requires at most 11 digits or 14 characters." +msgstr "Aquest camp requereix com a màxim 11 dígits o 14 caracters." -#: contrib/auth/models.py:106 -msgid "" -"Designates that this user has all permissions without explicitly assigning " -"them." -msgstr "" -"Designa que aquest usuari té tots els permisos sense assignar-los " -"explícitament." +#: contrib/localflavor/br/forms.py:84 +msgid "Invalid CPF number." +msgstr "Número CPF invàlid." -#: contrib/auth/models.py:107 -msgid "last login" -msgstr "últim inici de sessió" +#: contrib/localflavor/br/forms.py:106 +msgid "This field requires at least 14 digits" +msgstr "Aquest camp requereix almenys 14 dígits." -#: contrib/auth/models.py:108 -msgid "date joined" -msgstr "data de creació" +#: contrib/localflavor/br/forms.py:116 +msgid "Invalid CNPJ number." +msgstr "Número CNPJ invàlid." -#: contrib/auth/models.py:110 +#: contrib/localflavor/is_/forms.py:16 msgid "" -"In addition to the permissions manually assigned, this user will also get " -"all permissions granted to each group he/she is in." +"Enter a valid Icelandic identification number. The format is XXXXXX-XXXX." msgstr "" -"Junt amb els permissos asignats manualment, aquest usuari tindrà, també, els " -"permissos dels grups dels que sigui membre." - -#: contrib/auth/models.py:111 -msgid "user permissions" -msgstr "permissos de l'usuari" - -#: contrib/auth/models.py:115 -msgid "user" -msgstr "usuari" +"Introdueixi un número de identificació d'Islàndia. El format és XXXXXX-XXXX." -#: contrib/auth/models.py:116 -msgid "users" -msgstr "usuaris" +#: contrib/localflavor/is_/forms.py:30 +msgid "The Icelandic identification number is not valid." +msgstr "El número de identificació d'Islàndia no és vàlid." -#: contrib/auth/models.py:122 -msgid "Personal info" -msgstr "Informaciò personal" +#: core/validators.py:65 +msgid "This value must contain only letters, numbers and underscores." +msgstr "Aquest valor ha de contenir només números, guions, i guions baixos." -#: contrib/auth/models.py:123 -msgid "Permissions" -msgstr "permissos" +#: core/validators.py:69 +msgid "" +"This value must contain only letters, numbers, underscores, dashes or " +"slashes." +msgstr "" +"Aquest valor ha de contenir només lletres, números, guions, guions baixos, i " +"barres (/)." -#: contrib/auth/models.py:124 -msgid "Important dates" -msgstr "Dates importants" +#: core/validators.py:73 +msgid "This value must contain only letters, numbers, underscores or hyphens." +msgstr "" +"Aquest valor ha de contenir només lletres, números, guions o guions baixos" -#: contrib/auth/models.py:125 -msgid "Groups" -msgstr "Grups" +#: core/validators.py:77 +msgid "Uppercase letters are not allowed here." +msgstr "No es permeten majúscules aquí." -#: contrib/auth/models.py:269 -msgid "message" -msgstr "missatge" +#: core/validators.py:81 +msgid "Lowercase letters are not allowed here." +msgstr "No es permeten minúscules aquí." -#: newforms/models.py:173 newforms/fields.py:366 -msgid "Select a valid choice. That choice is not one of the available choices." -msgstr "" -"Esculli una opció vàlida; Aquesta opció no és una de les opcions disponibles." +#: core/validators.py:88 +msgid "Enter only digits separated by commas." +msgstr "Introdueixi només dígits separats per comes." -#: newforms/models.py:186 newforms/fields.py:82 newforms/fields.py:378 -#: newforms/fields.py:454 newforms/fields.py:465 -#: db/models/fields/__init__.py:117 db/models/fields/__init__.py:274 -#: db/models/fields/__init__.py:612 db/models/fields/__init__.py:623 -#: oldforms/__init__.py:357 -msgid "This field is required." -msgstr "Aquest camp és obligatori." +#: core/validators.py:100 +msgid "Enter valid e-mail addresses separated by commas." +msgstr "Introdueixi adreces de correu electrònic vàlides separades per comes." -#: newforms/models.py:190 newforms/fields.py:382 newforms/fields.py:458 -msgid "Enter a list of values." -msgstr "Introdueixi una llista de valors." +#: core/validators.py:104 +msgid "Please enter a valid IP address." +msgstr "Per favor introdueixi una adreça IP vàlida." -#: newforms/models.py:196 newforms/fields.py:391 -#, python-format -msgid "Select a valid choice. %s is not one of the available choices." -msgstr "Esculli una opció vàlida; %s' no és una de les opcions vàlides." +#: core/validators.py:108 +msgid "Empty values are not allowed here." +msgstr "No s'admeten valor buits." -#: newforms/fields.py:105 newforms/fields.py:258 -#, python-format -msgid "Ensure this value has at most %d characters." -msgstr "Asseguris de que el seu texte té com a màxim %d caracters." +#: core/validators.py:112 +msgid "Non-numeric characters aren't allowed here." +msgstr "No s'admeten caracters no numèrics." -#: newforms/fields.py:107 newforms/fields.py:260 -#, python-format -msgid "Ensure this value has at least %d characters." -msgstr "Asseguris de que el seu texte té almenys %d caracters." +#: core/validators.py:116 +msgid "This value can't be comprised solely of digits." +msgstr "Aquest valor no pot contenir només dígits." -#: newforms/fields.py:130 core/validators.py:120 -msgid "Enter a whole number." -msgstr "Introdueixi un número sencer." +#: core/validators.py:125 +msgid "Only alphabetical characters are allowed here." +msgstr "Només s'admeted caracters alfabètics aquí." -#: newforms/fields.py:132 -#, python-format -msgid "Ensure this value is less than or equal to %s." -msgstr "Aquest valor ha de ser menor o igual a %s." +#: core/validators.py:140 +msgid "Year must be 1900 or later." +msgstr "L'any ha de ser posterior al 1900" -#: newforms/fields.py:134 +#: core/validators.py:144 #, python-format -msgid "Ensure this value is greater than or equal to %s." -msgstr "Asseguris de que aquest valor sigui superior o igual a %s." - -#: newforms/fields.py:167 -msgid "Enter a valid date." -msgstr "Introdueixi una data vàlida." +msgid "Invalid date: %s" +msgstr "Data invàlida: %s" -#: newforms/fields.py:194 -msgid "Enter a valid time." -msgstr "Introdueixi una hora vàlida." +#: core/validators.py:149 db/models/fields/__init__.py:463 +msgid "Enter a valid date in YYYY-MM-DD format." +msgstr "Introdueixi una data vàlida en el forma AAAA-MM-DD." -#: newforms/fields.py:230 -msgid "Enter a valid date/time." -msgstr "Introdueixi una data/hora vàlides." +#: core/validators.py:154 +msgid "Enter a valid time in HH:MM format." +msgstr "Introdueixi una hora vàlida en el format HH:MM." -#: newforms/fields.py:244 -msgid "Enter a valid value." -msgstr "Introdueixi un valor vàlid." +#: core/validators.py:158 db/models/fields/__init__.py:532 +msgid "Enter a valid date/time in YYYY-MM-DD HH:MM format." +msgstr "Introdueixi un data/hora vàlida en format YYYY-MM-DD HH:MM." -#: newforms/fields.py:273 core/validators.py:162 -msgid "Enter a valid e-mail address." -msgstr "Introdueixi una adreça de correu vàlida." +#: core/validators.py:175 core/validators.py:453 oldforms/__init__.py:672 +msgid "No file was submitted. Check the encoding type on the form." +msgstr "" +"No s'ha enviat cap fitxer. Comprovi el tipus de codificació del formulari." -#: newforms/fields.py:291 newforms/fields.py:313 -msgid "Enter a valid URL." -msgstr "Introdueixi una URL vàlida." +#: core/validators.py:179 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" +"Envii una imatge vàilda. El fitxer que ha enviat no era una imatge o estaba " +"corrupte." -#: newforms/fields.py:315 -msgid "This URL appears to be a broken link." -msgstr "Aquesta URL sembla ser un enllaç trencat." +#: core/validators.py:186 +#, python-format +msgid "The URL %s does not point to a valid image." +msgstr "La URL %s no apunta una imatge vàlida." -#: db/models/manipulators.py:307 +#: core/validators.py:190 #, python-format -msgid "%(object)s with this %(type)s already exists for the given %(field)s." -msgstr "Ja existeix un %(object)s del tipus %(type)s amb aquest %(field)s." +msgid "Phone numbers must be in XXX-XXX-XXXX format. \"%s\" is invalid." +msgstr "" +"El números de telèfon han de guardar-se en el format XXX-XXX-XXXX. \"%s\" no " +"és vàlid." -#: db/models/fields/related.py:53 +#: core/validators.py:198 #, python-format -msgid "Please enter a valid %s." -msgstr "Si us plau, introdueixi un %s vàlid." +msgid "The URL %s does not point to a valid QuickTime video." +msgstr "La URL %s no apunta a un video QuickTime vàlid." -#: db/models/fields/related.py:642 -msgid "Separate multiple IDs with commas." -msgstr "Separi múltiples IDs amb comes." +#: core/validators.py:202 +msgid "A valid URL is required." +msgstr "Es precisa d'una URL vàlida." -#: db/models/fields/related.py:644 +#: core/validators.py:216 +#, python-format msgid "" -"Hold down \"Control\", or \"Command\" on a Mac, to select more than one." -msgstr "Premi \"Control\" o \"Command\" en un Mac per escollir més d'un." +"Valid HTML is required. Specific errors are:\n" +"%s" +msgstr "" +"Es precisa HTML vàlid. Els errors específics sòn:\n" +"%s" -#: db/models/fields/related.py:691 +#: core/validators.py:223 #, python-format -msgid "Please enter valid %(self)s IDs. The value %(value)r is invalid." -msgid_plural "" -"Please enter valid %(self)s IDs. The values %(value)r are invalid." -msgstr[0] "" -"Si us plau, introdueixi IDs de %(self)s vàlids. El valor %(value)r és " -"invàlid." -msgstr[1] "" -"Si us plau, introdueixi IDs de %(self)s vàlids. Els valors %(value)r són " -"invàlids." +msgid "Badly formed XML: %s" +msgstr "XML incorrectament formatejat: %s" -#: db/models/fields/__init__.py:42 +#: core/validators.py:240 #, python-format -msgid "%(optname)s with this %(fieldname)s already exists." -msgstr "Ja existeix %(optname)s amb auqest %(fieldname)s." - -#: db/models/fields/__init__.py:369 -msgid "This value must be an integer." -msgstr "Aquest valor ha de ser un enter." +msgid "Invalid URL: %s" +msgstr "URL invalida: %s" -#: db/models/fields/__init__.py:404 -msgid "This value must be either True or False." -msgstr "Aquest valor ha de ser True (Veritat) o False (Fals)" +#: core/validators.py:245 core/validators.py:247 +#, python-format +msgid "The URL %s is a broken link." +msgstr "La URL %sés un enllaç trencat." -#: db/models/fields/__init__.py:425 -msgid "This field cannot be null." -msgstr "Aquest camp no pot ser null (estar buit)." +#: core/validators.py:253 +msgid "Enter a valid U.S. state abbreviation." +msgstr "Introdueixi una abreviatura vàlida d'estat d'els E.U.A.." -#: db/models/fields/__init__.py:459 core/validators.py:148 -msgid "Enter a valid date in YYYY-MM-DD format." -msgstr "Introdueixi una data vàlida en el forma AAAA-MM-DD." +#: core/validators.py:267 +#, python-format +msgid "Watch your mouth! The word %s is not allowed here." +msgid_plural "Watch your mouth! The words %s are not allowed here." +msgstr[0] "Vigili la seva boca! Aquí no admetem la paraula: %s." +msgstr[1] "Vigili la seva boca! Aquí no admetem les paraules: %s." -#: db/models/fields/__init__.py:528 core/validators.py:157 -msgid "Enter a valid date/time in YYYY-MM-DD HH:MM format." -msgstr "Introdueixi un data/hora vàlida en format YYYY-MM-DD HH:MM." +#: core/validators.py:274 +#, python-format +msgid "This field must match the '%s' field." +msgstr "Aquest camp ha de concordar amb el camp '%s'." -#: db/models/fields/__init__.py:632 -msgid "Enter a valid filename." -msgstr "Introdueixi un nom de fitxer vàlid." +#: core/validators.py:293 +msgid "Please enter something for at least one field." +msgstr "Si us plau, introdueixi alguna cosa alemnys en un camp." -#: db/models/fields/__init__.py:753 -msgid "This value must be either None, True or False." -msgstr "Aquest valor ha de ser None (Cap), True (Veritat) o False (Fals)" +#: core/validators.py:302 core/validators.py:313 +msgid "Please enter both fields or leave them both empty." +msgstr "Si us plau, ompli els dos camps o deixi'ls tots dos en blanc." -#: views/generic/create_update.py:43 +#: core/validators.py:321 #, python-format -msgid "The %(verbose_name)s was created successfully." -msgstr "El/La %(verbose_name)s s'ha creat amb èxit." +msgid "This field must be given if %(field)s is %(value)s" +msgstr "S'ha de proporcionar aquest camps si %(field)s és %(value)s" -#: views/generic/create_update.py:117 +#: core/validators.py:334 #, python-format -msgid "The %(verbose_name)s was updated successfully." -msgstr "El/La %(verbose_name)s s'ha actualtzat amb èxit." +msgid "This field must be given if %(field)s is not %(value)s" +msgstr "S'ha de proporcionar aquest camps si %(field)s no és %(value)s" -#: views/generic/create_update.py:184 -#, python-format -msgid "The %(verbose_name)s was deleted." -msgstr "El %(verbose_name)s s'ha eliminat." +#: core/validators.py:353 +msgid "Duplicate values are not allowed." +msgstr "No s'admeten valors duplicats." -#: oldforms/__init__.py:392 +#: core/validators.py:368 #, python-format -msgid "Ensure your text is less than %s character." -msgid_plural "Ensure your text is less than %s characters." -msgstr[0] "Asseguris de que el seu texte té menys de %s caracter." -msgstr[1] "Asseguris de que el seu texte té menys de %s caracters." - -#: oldforms/__init__.py:397 -msgid "Line breaks are not allowed here." -msgstr "No es permeten salts de línia." +msgid "This value must be between %(lower)s and %(upper)s." +msgstr "Aquest valor ha de estar comprés entre %(lower)s i %(upper)s." -#: oldforms/__init__.py:498 oldforms/__init__.py:571 oldforms/__init__.py:610 +#: core/validators.py:370 #, python-format -msgid "Select a valid choice; '%(data)s' is not in %(choices)s." -msgstr "Esculli una opció vàlida; %(data)s' no està dintre de %(choices)s." +msgid "This value must be at least %s." +msgstr "Aquest valor ha de ser com a mínim %s." -#: oldforms/__init__.py:672 core/validators.py:174 core/validators.py:445 -msgid "No file was submitted. Check the encoding type on the form." -msgstr "" -"No s'ha enviat cap fitxer. Comprovi el tipus de codificació del formulari." +#: core/validators.py:372 +#, python-format +msgid "This value must be no more than %s." +msgstr "Aquest valor ha de ser com a màxim %s." -#: oldforms/__init__.py:674 -msgid "The submitted file is empty." -msgstr "El fitxer enviat està buit." +#: core/validators.py:408 +#, python-format +msgid "This value must be a power of %s." +msgstr "Aquest valor ha de ser una potència de %s." -#: oldforms/__init__.py:730 -msgid "Enter a whole number between -32,768 and 32,767." -msgstr "Introdueixi un número enter entre -32,768 i 32,767." +#: core/validators.py:417 +msgid "Please enter a valid decimal number." +msgstr "Si us plau, introdueixi un número decimal vàlid." -#: oldforms/__init__.py:740 -msgid "Enter a positive number." -msgstr "Introdueixi un número positiu." +#: core/validators.py:423 +#, python-format +msgid "Please enter a valid decimal number with at most %s total digit." +msgid_plural "" +"Please enter a valid decimal number with at most %s total digits." +msgstr[0] "" +"Si us plau, introdueixi un número decimal vàlid amb no més de %s digit." +msgstr[1] "" +"Si us plau, introdueixi un número decimal vàlid amb no més de %s digits." -#: oldforms/__init__.py:750 -msgid "Enter a whole number between 0 and 32,767." -msgstr "Introdueixi un número entre 0 i 32,767." +#: core/validators.py:426 +#, python-format +msgid "" +"Please enter a valid decimal number with a whole part of at most %s digit." +msgid_plural "" +"Please enter a valid decimal number with a whole part of at most %s digits." +msgstr[0] "" +"Si us plau, introdueixi un número decimal vàlid amb la part entera amb com a " +"màxim %s dígit." +msgstr[1] "" +"Si us plau, introdueixi un número decimal vàlid amb la part entera amb com a " +"màxim %s dígits." -#: conf/global_settings.py:39 -msgid "Arabic" -msgstr "Arabic" +#: core/validators.py:429 +#, python-format +msgid "Please enter a valid decimal number with at most %s decimal place." +msgid_plural "" +"Please enter a valid decimal number with at most %s decimal places." +msgstr[0] "" +"Si us plau, introdueixi un número decimal vàlid amb no més de %s dígit en la " +"part decimal." +msgstr[1] "" +"Si us plau, introdueixi un número decimal vàlid amb no més de %s dígits en " +"la part decimal." -#: conf/global_settings.py:40 -msgid "Bengali" -msgstr "Bengalí" +#: core/validators.py:437 +msgid "Please enter a valid floating point number." +msgstr "Si us plau, introdueixi un número amb punt de coma flotant vàlid." -#: conf/global_settings.py:41 -msgid "Bulgarian" -msgstr "Bulgar" +#: core/validators.py:446 +#, python-format +msgid "Make sure your uploaded file is at least %s bytes big." +msgstr "Asseguris de que el fitxer que ha enviat té, com a mínim, %s bytes." -#: conf/global_settings.py:42 -msgid "Catalan" -msgstr "Català" +#: core/validators.py:447 +#, python-format +msgid "Make sure your uploaded file is at most %s bytes big." +msgstr "Asseguris de que el fitxer que ha enviat té, com a màxim %s bytes." -#: conf/global_settings.py:43 -msgid "Czech" -msgstr "Txec" +#: core/validators.py:464 +msgid "The format for this field is wrong." +msgstr "El format per aquest camp és incorrecte." -#: conf/global_settings.py:44 -msgid "Welsh" -msgstr "Galès" +#: core/validators.py:479 +msgid "This field is invalid." +msgstr "El camp no és vàlid." -#: conf/global_settings.py:45 -msgid "Danish" -msgstr "Danès" +#: core/validators.py:515 +#, python-format +msgid "Could not retrieve anything from %s." +msgstr "No s'ha pogut obtenir res de %s." -#: conf/global_settings.py:46 -msgid "German" -msgstr "Alemany" +#: core/validators.py:518 +#, python-format +msgid "" +"The URL %(url)s returned the invalid Content-Type header '%(contenttype)s'." +msgstr "" +"La URL %(url)s ha va tornar la capcelera Content-Type '%(contenttype)s', que " +"no és vàlida." -#: conf/global_settings.py:47 -msgid "Greek" -msgstr "Grec" +#: core/validators.py:551 +#, python-format +msgid "" +"Please close the unclosed %(tag)s tag from line %(line)s. (Line starts with " +"\"%(start)s\".)" +msgstr "" +"Si us plau, tanqui l'etiqueta %(tag)s des de la línia %(line)s. (La línia " +"comença amb \"%(start)s\".)" -#: conf/global_settings.py:48 -msgid "English" -msgstr "Anglès" +#: core/validators.py:555 +#, python-format +msgid "" +"Some text starting on line %(line)s is not allowed in that context. (Line " +"starts with \"%(start)s\".)" +msgstr "" +"Part del text que comença en la línia %(line)s no està permès en aquest " +"context. (La línia comença per \"%(start)s\".)" -#: conf/global_settings.py:49 -msgid "Spanish" -msgstr "Espanyol" +#: core/validators.py:560 +#, python-format +msgid "" +"\"%(attr)s\" on line %(line)s is an invalid attribute. (Line starts with \"%" +"(start)s\".)" +msgstr "" +"El \"%(attr)s\" de la línia %(line)s no és un atribut vàlid. (La línia " +"comença per \"%(start)s\".)" -#: conf/global_settings.py:50 -msgid "Argentinean Spanish" -msgstr "Castellà Argentí" +#: core/validators.py:565 +#, python-format +msgid "" +"\"<%(tag)s>\" on line %(line)s is an invalid tag. (Line starts with \"%" +"(start)s\".)" +msgstr "" +"La \"<%(tag)s>\" de la línia %(line)s no és una etiqueta vàlida. (La línia " +"comença per \"%(start)s\".)" -#: conf/global_settings.py:51 -msgid "Finnish" -msgstr "Finlandès" +#: core/validators.py:569 +#, python-format +msgid "" +"A tag on line %(line)s is missing one or more required attributes. (Line " +"starts with \"%(start)s\".)" +msgstr "" +"Una etiqueta de la línia %(line)s li falta un o més atributs requerits.(La " +"línia comença per \"%(start)s\".)" -#: conf/global_settings.py:52 -msgid "French" -msgstr "Francès" +#: core/validators.py:574 +#, python-format +msgid "" +"The \"%(attr)s\" attribute on line %(line)s has an invalid value. (Line " +"starts with \"%(start)s\".)" +msgstr "" +"L'atribut \"%(attr)s\" de la línia %(line)s té un valor que no és vàlid. (La " +"línia comença per \"%(start)s\".)" -#: conf/global_settings.py:53 -msgid "Galician" -msgstr "Galleg" +#: utils/dates.py:6 +msgid "Monday" +msgstr "Dilluns" -#: conf/global_settings.py:54 -msgid "Hungarian" -msgstr "Húngar" +#: utils/dates.py:6 +msgid "Tuesday" +msgstr "Dimarts" -#: conf/global_settings.py:55 -msgid "Hebrew" -msgstr "Hebreu" +#: utils/dates.py:6 +msgid "Wednesday" +msgstr "Dimecres" -#: conf/global_settings.py:56 -msgid "Icelandic" -msgstr "Islandès" +#: utils/dates.py:6 +msgid "Thursday" +msgstr "Dijous" -#: conf/global_settings.py:57 -msgid "Italian" -msgstr "Italià" +#: utils/dates.py:6 +msgid "Friday" +msgstr "Divendres" -#: conf/global_settings.py:58 -msgid "Japanese" -msgstr "Japonès" +#: utils/dates.py:7 +msgid "Saturday" +msgstr "Dissabte" -#: conf/global_settings.py:59 -msgid "Korean" -msgstr "Coreà" +#: utils/dates.py:7 +msgid "Sunday" +msgstr "Diumenge" -#: conf/global_settings.py:60 -msgid "Kannada" -msgstr "" +#: utils/dates.py:14 +msgid "January" +msgstr "Gener" -#: conf/global_settings.py:61 -msgid "Latvian" -msgstr "" +#: utils/dates.py:14 +msgid "February" +msgstr "Febrer" -#: conf/global_settings.py:62 -msgid "Macedonian" -msgstr "Macedoni" +#: utils/dates.py:14 utils/dates.py:27 +msgid "March" +msgstr "Març" -#: conf/global_settings.py:63 -msgid "Dutch" -msgstr "Holandès" +#: utils/dates.py:14 utils/dates.py:27 +msgid "April" +msgstr "Abril" -#: conf/global_settings.py:64 -msgid "Norwegian" -msgstr "Norueg" +#: utils/dates.py:14 utils/dates.py:27 +msgid "May" +msgstr "Maig" -#: conf/global_settings.py:65 -msgid "Polish" -msgstr "Polac" +#: utils/dates.py:14 utils/dates.py:27 +msgid "June" +msgstr "Juny" -#: conf/global_settings.py:66 -msgid "Portugese" -msgstr "Portuguès" +#: utils/dates.py:15 utils/dates.py:27 +msgid "July" +msgstr "Juliol" -#: conf/global_settings.py:67 -msgid "Brazilian" -msgstr "Brasileny" +#: utils/dates.py:15 +msgid "August" +msgstr "Agost" -#: conf/global_settings.py:68 -msgid "Romanian" -msgstr "Rumanès" +#: utils/dates.py:15 +msgid "September" +msgstr "Setembre" -#: conf/global_settings.py:69 -msgid "Russian" -msgstr "Rús" +#: utils/dates.py:15 +msgid "October" +msgstr "Octubre" -#: conf/global_settings.py:70 -msgid "Slovak" -msgstr "Eslovac" +#: utils/dates.py:15 +msgid "November" +msgstr "Novembre" -#: conf/global_settings.py:71 -msgid "Slovenian" -msgstr "Esloveni" +#: utils/dates.py:16 +msgid "December" +msgstr "Desembre" -#: conf/global_settings.py:72 -msgid "Serbian" -msgstr "Serbi" +#: utils/dates.py:19 +msgid "jan" +msgstr "gen" -#: conf/global_settings.py:73 -msgid "Swedish" -msgstr "Suec" +#: utils/dates.py:19 +msgid "feb" +msgstr "feb" -#: conf/global_settings.py:74 -msgid "Tamil" -msgstr "" +#: utils/dates.py:19 +msgid "mar" +msgstr "mar" -#: conf/global_settings.py:75 -msgid "Telugu" -msgstr "" +#: utils/dates.py:19 +msgid "apr" +msgstr "abr" -#: conf/global_settings.py:76 -msgid "Turkish" -msgstr "Turc" +#: utils/dates.py:19 +msgid "may" +msgstr "mai" -#: conf/global_settings.py:77 -msgid "Ukrainian" -msgstr "Ucranià" +#: utils/dates.py:19 +msgid "jun" +msgstr "jun" -#: conf/global_settings.py:78 -msgid "Simplified Chinese" -msgstr "Xinés simplificat" +#: utils/dates.py:20 +msgid "jul" +msgstr "jul" -#: conf/global_settings.py:79 -msgid "Traditional Chinese" -msgstr "Xinés tradicional" +#: utils/dates.py:20 +msgid "aug" +msgstr "ago" -#: core/validators.py:64 -msgid "This value must contain only letters, numbers and underscores." -msgstr "Aquest valor ha de contenir només números, guions, i guions baixos." +#: utils/dates.py:20 +msgid "sep" +msgstr "set" -#: core/validators.py:68 -msgid "" -"This value must contain only letters, numbers, underscores, dashes or " -"slashes." -msgstr "" -"Aquest valor ha de contenir només lletres, números, guions, guions baixos, i " -"barres (/)." +#: utils/dates.py:20 +msgid "oct" +msgstr "oct" -#: core/validators.py:72 -msgid "This value must contain only letters, numbers, underscores or hyphens." -msgstr "" -"Aquest valor ha de contenir només lletres, números, guions o guions baixos" +#: utils/dates.py:20 +msgid "nov" +msgstr "nov" -#: core/validators.py:76 -msgid "Uppercase letters are not allowed here." -msgstr "No es permeten majúscules aquí." +#: utils/dates.py:20 +msgid "dec" +msgstr "des" -#: core/validators.py:80 -msgid "Lowercase letters are not allowed here." -msgstr "No es permeten minúscules aquí." +#: utils/dates.py:27 +msgid "Jan." +msgstr "Gen." -#: core/validators.py:87 -msgid "Enter only digits separated by commas." -msgstr "Introdueixi només dígits separats per comes." +#: utils/dates.py:27 +msgid "Feb." +msgstr "Feb." -#: core/validators.py:99 -msgid "Enter valid e-mail addresses separated by commas." -msgstr "Introdueixi adreces de correu electrònic vàlides separades per comes." +#: utils/dates.py:28 +msgid "Aug." +msgstr "Ago." -#: core/validators.py:103 -msgid "Please enter a valid IP address." -msgstr "Per favor introdueixi una adreça IP vàlida." +#: utils/dates.py:28 +msgid "Sept." +msgstr "Set." -#: core/validators.py:107 -msgid "Empty values are not allowed here." -msgstr "No s'admeten valor buits." +#: utils/dates.py:28 +msgid "Oct." +msgstr "Oct." -#: core/validators.py:111 -msgid "Non-numeric characters aren't allowed here." -msgstr "No s'admeten caracters no numèrics." +#: utils/dates.py:28 +msgid "Nov." +msgstr "Nov." -#: core/validators.py:115 -msgid "This value can't be comprised solely of digits." -msgstr "Aquest valor no pot contenir només dígits." +#: utils/dates.py:28 +msgid "Dec." +msgstr "Des." -#: core/validators.py:124 -msgid "Only alphabetical characters are allowed here." -msgstr "Només s'admeted caracters alfabètics aquí." +#: utils/timesince.py:12 +msgid "year" +msgid_plural "years" +msgstr[0] "any" +msgstr[1] "anys" -#: core/validators.py:139 -msgid "Year must be 1900 or later." -msgstr "L'any ha de ser posterior al 1900" +#: utils/timesince.py:13 +msgid "month" +msgid_plural "months" +msgstr[0] "mes" +msgstr[1] "mesos" -#: core/validators.py:143 -#, python-format -msgid "Invalid date: %s" -msgstr "Data invàlida: %s" +#: utils/timesince.py:14 +msgid "week" +msgid_plural "weeks" +msgstr[0] "setmana" +msgstr[1] "setmanes" -#: core/validators.py:153 -msgid "Enter a valid time in HH:MM format." -msgstr "Introdueixi una hora vàlida en el format HH:MM." +#: utils/timesince.py:15 +msgid "day" +msgid_plural "days" +msgstr[0] "dia" +msgstr[1] "dies" -#: core/validators.py:178 -msgid "" -"Upload a valid image. The file you uploaded was either not an image or a " -"corrupted image." -msgstr "" -"Envii una imatge vàilda. El fitxer que ha enviat no era una imatge o estaba " -"corrupte." +#: utils/timesince.py:16 +msgid "hour" +msgid_plural "hours" +msgstr[0] "hora" +msgstr[1] "hores" -#: core/validators.py:185 +#: utils/timesince.py:17 +msgid "minute" +msgid_plural "minutes" +msgstr[0] "minut" +msgstr[1] "minuts" + +#: utils/timesince.py:40 #, python-format -msgid "The URL %s does not point to a valid image." -msgstr "La URL %s no apunta una imatge vàlida." +msgid "%d milliseconds" +msgstr "%d milisegons" -#: core/validators.py:189 +#: utils/timesince.py:41 #, python-format -msgid "Phone numbers must be in XXX-XXX-XXXX format. \"%s\" is invalid." -msgstr "" -"El números de telèfon han de guardar-se en el format XXX-XXX-XXXX. \"%s\" no " -"és vàlid." +msgid "%(number)d %(type)s" +msgstr "%(number)d %(type)s" -#: core/validators.py:197 +#: utils/timesince.py:47 #, python-format -msgid "The URL %s does not point to a valid QuickTime video." -msgstr "La URL %s no apunta a un video QuickTime vàlid." +msgid ", %(number)d %(type)s" +msgstr ", %(number)d %(type)s" -#: core/validators.py:201 -msgid "A valid URL is required." -msgstr "Es precisa d'una URL vàlida." +#: utils/dateformat.py:40 +msgid "p.m." +msgstr "p.m." -#: core/validators.py:215 -#, python-format -msgid "" -"Valid HTML is required. Specific errors are:\n" -"%s" -msgstr "" -"Es precisa HTML vàlid. Els errors específics sòn:\n" -"%s" +#: utils/dateformat.py:41 +msgid "a.m." +msgstr "a.m." -#: core/validators.py:222 -#, python-format -msgid "Badly formed XML: %s" -msgstr "XML incorrectament formatejat: %s" +#: utils/dateformat.py:46 +msgid "PM" +msgstr "PM" -#: core/validators.py:239 -#, python-format -msgid "Invalid URL: %s" -msgstr "URL invalida: %s" +#: utils/dateformat.py:47 +msgid "AM" +msgstr "AM" -#: core/validators.py:244 core/validators.py:246 -#, python-format -msgid "The URL %s is a broken link." -msgstr "La URL %sés un enllaç trencat." +#: utils/dateformat.py:95 +msgid "midnight" +msgstr "mitja nit" -#: core/validators.py:252 -msgid "Enter a valid U.S. state abbreviation." -msgstr "Introdueixi una abreviatura vàlida d'estat d'els E.U.A.." +#: utils/dateformat.py:97 +msgid "noon" +msgstr "mig dia" -#: core/validators.py:266 -#, python-format -msgid "Watch your mouth! The word %s is not allowed here." -msgid_plural "Watch your mouth! The words %s are not allowed here." -msgstr[0] "Vigili la seva boca! Aquí no admetem la paraula: %s." -msgstr[1] "Vigili la seva boca! Aquí no admetem les paraules: %s." +#: utils/translation/trans_real.py:358 +msgid "DATE_FORMAT" +msgstr "F j, Y" -#: core/validators.py:273 -#, python-format -msgid "This field must match the '%s' field." -msgstr "Aquest camp ha de concordar amb el camp '%s'." +#: utils/translation/trans_real.py:359 +msgid "DATETIME_FORMAT" +msgstr "F j, Y, H:i" -#: core/validators.py:292 -msgid "Please enter something for at least one field." -msgstr "Si us plau, introdueixi alguna cosa alemnys en un camp." +#: utils/translation/trans_real.py:360 +msgid "TIME_FORMAT" +msgstr "H:i" -#: core/validators.py:301 core/validators.py:312 -msgid "Please enter both fields or leave them both empty." -msgstr "Si us plau, ompli els dos camps o deixi'ls tots dos en blanc." +#: utils/translation/trans_real.py:376 +msgid "YEAR_MONTH_FORMAT" +msgstr "j de/d' F del Y" -#: core/validators.py:320 -#, python-format -msgid "This field must be given if %(field)s is %(value)s" -msgstr "S'ha de proporcionar aquest camps si %(field)s és %(value)s" +#: utils/translation/trans_real.py:377 +msgid "MONTH_DAY_FORMAT" +msgstr "j de/d' F del Y" -#: core/validators.py:333 +#: oldforms/__init__.py:392 #, python-format -msgid "This field must be given if %(field)s is not %(value)s" -msgstr "S'ha de proporcionar aquest camps si %(field)s no és %(value)s" +msgid "Ensure your text is less than %s character." +msgid_plural "Ensure your text is less than %s characters." +msgstr[0] "Asseguris de que el seu texte té menys de %s caracter." +msgstr[1] "Asseguris de que el seu texte té menys de %s caracters." -#: core/validators.py:352 -msgid "Duplicate values are not allowed." -msgstr "No s'admeten valors duplicats." +#: oldforms/__init__.py:397 +msgid "Line breaks are not allowed here." +msgstr "No es permeten salts de línia." -#: core/validators.py:367 +#: oldforms/__init__.py:498 oldforms/__init__.py:571 oldforms/__init__.py:610 #, python-format -msgid "This value must be between %(lower)s and %(upper)s." -msgstr "Aquest valor ha de estar comprés entre %(lower)s i %(upper)s." +msgid "Select a valid choice; '%(data)s' is not in %(choices)s." +msgstr "Esculli una opció vàlida; %(data)s' no està dintre de %(choices)s." -#: core/validators.py:369 -#, python-format -msgid "This value must be at least %s." -msgstr "Aquest valor ha de ser com a mínim %s." +#: oldforms/__init__.py:674 +msgid "The submitted file is empty." +msgstr "El fitxer enviat està buit." -#: core/validators.py:371 -#, python-format -msgid "This value must be no more than %s." -msgstr "Aquest valor ha de ser com a màxim %s." +#: oldforms/__init__.py:730 +msgid "Enter a whole number between -32,768 and 32,767." +msgstr "Introdueixi un número enter entre -32,768 i 32,767." -#: core/validators.py:407 -#, python-format -msgid "This value must be a power of %s." -msgstr "Aquest valor ha de ser una potència de %s." +#: oldforms/__init__.py:740 +msgid "Enter a positive number." +msgstr "Introdueixi un número positiu." -#: core/validators.py:418 -msgid "Please enter a valid decimal number." -msgstr "Si us plau, introdueixi un número decimal vàlid." +#: oldforms/__init__.py:750 +msgid "Enter a whole number between 0 and 32,767." +msgstr "Introdueixi un número entre 0 i 32,767." -#: core/validators.py:422 +#: views/generic/create_update.py:43 #, python-format -msgid "Please enter a valid decimal number with at most %s total digit." -msgid_plural "" -"Please enter a valid decimal number with at most %s total digits." -msgstr[0] "" -"Si us plau, introdueixi un número decimal vàlid amb no més de %s digit." -msgstr[1] "" -"Si us plau, introdueixi un número decimal vàlid amb no més de %s digits." +msgid "The %(verbose_name)s was created successfully." +msgstr "El/La %(verbose_name)s s'ha creat amb èxit." -#: core/validators.py:425 +#: views/generic/create_update.py:117 #, python-format -msgid "" -"Please enter a valid decimal number with a whole part of at most %s digit." -msgid_plural "" -"Please enter a valid decimal number with a whole part of at most %s digits." -msgstr[0] "" -"Si us plau, introdueixi un número decimal vàlid amb la part entera amb com a " -"màxim %s dígit." -msgstr[1] "" -"Si us plau, introdueixi un número decimal vàlid amb la part entera amb com a " -"màxim %s dígits." +msgid "The %(verbose_name)s was updated successfully." +msgstr "El/La %(verbose_name)s s'ha actualtzat amb èxit." -#: core/validators.py:428 +#: views/generic/create_update.py:184 #, python-format -msgid "Please enter a valid decimal number with at most %s decimal place." -msgid_plural "" -"Please enter a valid decimal number with at most %s decimal places." -msgstr[0] "" -"Si us plau, introdueixi un número decimal vàlid amb no més de %s dígit en la " -"part decimal." -msgstr[1] "" -"Si us plau, introdueixi un número decimal vàlid amb no més de %s dígits en " -"la part decimal." +msgid "The %(verbose_name)s was deleted." +msgstr "El %(verbose_name)s s'ha eliminat." -#: core/validators.py:438 +#: db/models/manipulators.py:307 #, python-format -msgid "Make sure your uploaded file is at least %s bytes big." -msgstr "Asseguris de que el fitxer que ha enviat té, com a mínim, %s bytes." +msgid "%(object)s with this %(type)s already exists for the given %(field)s." +msgstr "Ja existeix un %(object)s del tipus %(type)s amb aquest %(field)s." -#: core/validators.py:439 +#: db/models/fields/__init__.py:46 #, python-format -msgid "Make sure your uploaded file is at most %s bytes big." -msgstr "Asseguris de que el fitxer que ha enviat té, com a màxim %s bytes." +msgid "%(optname)s with this %(fieldname)s already exists." +msgstr "Ja existeix %(optname)s amb auqest %(fieldname)s." -#: core/validators.py:456 -msgid "The format for this field is wrong." -msgstr "El format per aquest camp és incorrecte." +#: db/models/fields/__init__.py:373 +msgid "This value must be an integer." +msgstr "Aquest valor ha de ser un enter." -#: core/validators.py:471 -msgid "This field is invalid." -msgstr "El camp no és vàlid." +#: db/models/fields/__init__.py:408 +msgid "This value must be either True or False." +msgstr "Aquest valor ha de ser True (Veritat) o False (Fals)" -#: core/validators.py:507 -#, python-format -msgid "Could not retrieve anything from %s." -msgstr "No s'ha pogut obtenir res de %s." +#: db/models/fields/__init__.py:429 +msgid "This field cannot be null." +msgstr "Aquest camp no pot ser null (estar buit)." -#: core/validators.py:510 -#, python-format -msgid "" -"The URL %(url)s returned the invalid Content-Type header '%(contenttype)s'." -msgstr "" -"La URL %(url)s ha va tornar la capcelera Content-Type '%(contenttype)s', que " -"no és vàlida." +#: db/models/fields/__init__.py:592 +msgid "This value must be a decimal number." +msgstr "Aquest valor ha de ser un número decimal." -#: core/validators.py:543 -#, python-format -msgid "" -"Please close the unclosed %(tag)s tag from line %(line)s. (Line starts with " -"\"%(start)s\".)" -msgstr "" -"Si us plau, tanqui l'etiqueta %(tag)s des de la línia %(line)s. (La línia " -"comença amb \"%(start)s\".)" +#: db/models/fields/__init__.py:695 +msgid "Enter a valid filename." +msgstr "Introdueixi un nom de fitxer vàlid." -#: core/validators.py:547 -#, python-format -msgid "" -"Some text starting on line %(line)s is not allowed in that context. (Line " -"starts with \"%(start)s\".)" -msgstr "" -"Part del text que comença en la línia %(line)s no està permès en aquest " -"context. (La línia comença per \"%(start)s\".)" +#: db/models/fields/__init__.py:818 +msgid "This value must be either None, True or False." +msgstr "Aquest valor ha de ser None (Cap), True (Veritat) o False (Fals)" -#: core/validators.py:552 +#: db/models/fields/related.py:53 #, python-format -msgid "" -"\"%(attr)s\" on line %(line)s is an invalid attribute. (Line starts with \"%" -"(start)s\".)" -msgstr "" -"El \"%(attr)s\" de la línia %(line)s no és un atribut vàlid. (La línia " -"comença per \"%(start)s\".)" +msgid "Please enter a valid %s." +msgstr "Si us plau, introdueixi un %s vàlid." -#: core/validators.py:557 -#, python-format -msgid "" -"\"<%(tag)s>\" on line %(line)s is an invalid tag. (Line starts with \"%" -"(start)s\".)" -msgstr "" -"La \"<%(tag)s>\" de la línia %(line)s no és una etiqueta vàlida. (La línia " -"comença per \"%(start)s\".)" +#: db/models/fields/related.py:642 +msgid "Separate multiple IDs with commas." +msgstr "Separi múltiples IDs amb comes." -#: core/validators.py:561 -#, python-format +#: db/models/fields/related.py:644 msgid "" -"A tag on line %(line)s is missing one or more required attributes. (Line " -"starts with \"%(start)s\".)" -msgstr "" -"Una etiqueta de la línia %(line)s li falta un o més atributs requerits.(La " -"línia comença per \"%(start)s\".)" +"Hold down \"Control\", or \"Command\" on a Mac, to select more than one." +msgstr "Premi \"Control\" o \"Command\" en un Mac per escollir més d'un." -#: core/validators.py:566 +#: db/models/fields/related.py:691 #, python-format -msgid "" -"The \"%(attr)s\" attribute on line %(line)s has an invalid value. (Line " -"starts with \"%(start)s\".)" -msgstr "" -"L'atribut \"%(attr)s\" de la línia %(line)s té un valor que no és vàlid. (La " -"línia comença per \"%(start)s\".)" +msgid "Please enter valid %(self)s IDs. The value %(value)r is invalid." +msgid_plural "" +"Please enter valid %(self)s IDs. The values %(value)r are invalid." +msgstr[0] "" +"Si us plau, introdueixi IDs de %(self)s vàlids. El valor %(value)r és " +"invàlid." +msgstr[1] "" +"Si us plau, introdueixi IDs de %(self)s vàlids. Els valors %(value)r són " +"invàlids." diff --git a/django/conf/locale/ca/LC_MESSAGES/djangojs.mo b/django/conf/locale/ca/LC_MESSAGES/djangojs.mo index d12c8f8d7a..a5b908df1f 100644 Binary files a/django/conf/locale/ca/LC_MESSAGES/djangojs.mo and b/django/conf/locale/ca/LC_MESSAGES/djangojs.mo differ diff --git a/django/conf/locale/ca/LC_MESSAGES/djangojs.po b/django/conf/locale/ca/LC_MESSAGES/djangojs.po index ed7231988b..8167253005 100644 --- a/django/conf/locale/ca/LC_MESSAGES/djangojs.po +++ b/django/conf/locale/ca/LC_MESSAGES/djangojs.po @@ -113,8 +113,8 @@ msgstr "Demà" #: contrib/admin/media/js/admin/CollapsedFieldsets.js:34 #: contrib/admin/media/js/admin/CollapsedFieldsets.js:72 msgid "Show" -msgstr "" +msgstr "Mostrar" #: contrib/admin/media/js/admin/CollapsedFieldsets.js:63 msgid "Hide" -msgstr "" +msgstr "Ocultar" diff --git a/django/conf/locale/de/LC_MESSAGES/django.mo b/django/conf/locale/de/LC_MESSAGES/django.mo index 5f2eee4f33..d1981a679d 100644 Binary files a/django/conf/locale/de/LC_MESSAGES/django.mo and b/django/conf/locale/de/LC_MESSAGES/django.mo differ diff --git a/django/conf/locale/de/LC_MESSAGES/django.po b/django/conf/locale/de/LC_MESSAGES/django.po index 52b70bda00..3ccbefe249 100644 --- a/django/conf/locale/de/LC_MESSAGES/django.po +++ b/django/conf/locale/de/LC_MESSAGES/django.po @@ -481,8 +481,8 @@ msgid "" "database tables have been created, and make sure the database is readable by " "the appropriate user." msgstr "" -"Etwas stimmt nicht mit der Datenbankkonfiguration. Bitte sicherstellen, das " -"die richtigen Datenbanktabellen angelegt wurden und bitte sicherstellen, das " +"Etwas stimmt nicht mit der Datenbankkonfiguration. Bitte sicherstellen, dass " +"die richtigen Datenbanktabellen angelegt wurden und " "die Datenbank vom verwendeten Datenbankbenutzer auch lesbar ist." #: contrib/admin/templates/admin/login.html:17 diff --git a/django/conf/project_template/settings.py b/django/conf/project_template/settings.py index cadb5146b7..36039d7e98 100644 --- a/django/conf/project_template/settings.py +++ b/django/conf/project_template/settings.py @@ -38,8 +38,9 @@ USE_I18N = True # Example: "/home/media/media.lawrence.com/" MEDIA_ROOT = '' -# URL that handles the media served from MEDIA_ROOT. -# Example: "http://media.lawrence.com" +# URL that handles the media served from MEDIA_ROOT. Make sure to use a +# trailing slash if there is a path component (optional in other cases). +# Examples: "http://media.lawrence.com", "http://example.com/media/" MEDIA_URL = '' # URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a diff --git a/django/conf/urls/defaults.py b/django/conf/urls/defaults.py index 1e87ea708a..26cdd3e1ff 100644 --- a/django/conf/urls/defaults.py +++ b/django/conf/urls/defaults.py @@ -1,4 +1,5 @@ from django.core.urlresolvers import RegexURLPattern, RegexURLResolver +from django.core.exceptions import ImproperlyConfigured __all__ = ['handler404', 'handler500', 'include', 'patterns', 'url'] @@ -22,7 +23,10 @@ def url(regex, view, kwargs=None, name=None, prefix=''): # For include(...) processing. return RegexURLResolver(regex, view[0], kwargs) else: - if prefix and isinstance(view, basestring): - view = prefix + '.' + view + if isinstance(view, basestring): + if not view: + raise ImproperlyConfigured('Empty URL pattern view name not permitted (for pattern %r)' % regex) + if prefix: + view = prefix + '.' + view return RegexURLPattern(regex, view, kwargs, name) diff --git a/django/contrib/comments/feeds.py b/django/contrib/comments/feeds.py index 34cf3d9cef..1ad638469b 100644 --- a/django/contrib/comments/feeds.py +++ b/django/contrib/comments/feeds.py @@ -23,16 +23,19 @@ class LatestFreeCommentsFeed(Feed): self._site = Site.objects.get_current() return "Latest comments on %s" % self._site.name + def get_query_set(self): + return self.comments_class.objects.filter(site__pk=settings.SITE_ID, is_public=True) + def items(self): - return self.comments_class.objects.filter(site__pk=settings.SITE_ID, is_public=True)[:40] + return self.get_query_set()[:40] class LatestCommentsFeed(LatestFreeCommentsFeed): """Feed of latest free comments on the current site""" comments_class = Comment - def items(self): - qs = LatestFreeCommentsFeed.items(self) + def get_query_set(self): + qs = super(LatestCommentsFeed, self).get_query_set() qs = qs.filter(is_removed=False) if settings.COMMENTS_BANNED_USERS_GROUP: where = ['user_id NOT IN (SELECT user_id FROM auth_users_group WHERE group_id = %s)'] diff --git a/django/core/context_processors.py b/django/core/context_processors.py index f4b288dfc4..3c826b1a7d 100644 --- a/django/core/context_processors.py +++ b/django/core/context_processors.py @@ -42,6 +42,13 @@ def i18n(request): return context_extras +def media(request): + """ + Adds media-related context variables to the context. + + """ + return {'MEDIA_URL': settings.MEDIA_URL} + def request(request): return {'request': request} diff --git a/django/core/management.py b/django/core/management.py index 26a06792b2..a273d275c1 100644 --- a/django/core/management.py +++ b/django/core/management.py @@ -1141,7 +1141,7 @@ def validate(outfile=sys.stdout, silent_success=False): return outfile.write('%s error%s found.\n' % (num_errors, num_errors != 1 and 's' or '')) except ImproperlyConfigured: - outfile.write("Skipping validation because things aren't configured properly.") + outfile.write("Skipping validation because things aren't configured properly.\n") validate.args = '' def _check_for_validation_errors(app=None): diff --git a/django/core/serializers/json.py b/django/core/serializers/json.py index 55804b8316..fa2dca7295 100644 --- a/django/core/serializers/json.py +++ b/django/core/serializers/json.py @@ -21,6 +21,8 @@ class Serializer(PythonSerializer): Convert a queryset to JSON. """ def end_serialization(self): + self.options.pop('stream', None) + self.options.pop('fields', None) simplejson.dump(self.objects, self.stream, cls=DjangoJSONEncoder, **self.options) def getvalue(self): diff --git a/django/core/serializers/python.py b/django/core/serializers/python.py index 66dbbff335..5fbb3163f7 100644 --- a/django/core/serializers/python.py +++ b/django/core/serializers/python.py @@ -37,7 +37,12 @@ class Serializer(base.Serializer): def handle_fk_field(self, obj, field): related = getattr(obj, field.name) if related is not None: - related = getattr(related, field.rel.field_name) + if field.rel.field_name == related._meta.pk.name: + # Related to remote object via primary key + related = related._get_pk_val() + else: + # Related to remote object via other field + related = getattr(related, field.rel.field_name) self._current[field.name] = related def handle_m2m_field(self, obj, field): diff --git a/django/core/serializers/pyyaml.py b/django/core/serializers/pyyaml.py index fa3dec984e..d3444280c5 100644 --- a/django/core/serializers/pyyaml.py +++ b/django/core/serializers/pyyaml.py @@ -18,6 +18,8 @@ class Serializer(PythonSerializer): Convert a queryset to YAML. """ def end_serialization(self): + self.options.pop('stream', None) + self.options.pop('fields', None) yaml.dump(self.objects, self.stream, **self.options) def getvalue(self): diff --git a/django/core/serializers/xml_serializer.py b/django/core/serializers/xml_serializer.py index 633001f5f0..3e4a6f3e79 100644 --- a/django/core/serializers/xml_serializer.py +++ b/django/core/serializers/xml_serializer.py @@ -82,7 +82,13 @@ class Serializer(base.Serializer): self._start_relational_field(field) related = getattr(obj, field.name) if related is not None: - self.xml.characters(str(getattr(related, field.rel.field_name))) + if field.rel.field_name == related._meta.pk.name: + # Related to remote object via primary key + related = related._get_pk_val() + else: + # Related to remote object via other field + related = getattr(related, field.rel.field_name) + self.xml.characters(str(related)) else: self.xml.addQuickElement("None") self.xml.endElement("field") diff --git a/django/db/backends/dummy/base.py b/django/db/backends/dummy/base.py index 6a909016fc..d0ec897407 100644 --- a/django/db/backends/dummy/base.py +++ b/django/db/backends/dummy/base.py @@ -12,6 +12,9 @@ from django.core.exceptions import ImproperlyConfigured def complain(*args, **kwargs): raise ImproperlyConfigured, "You haven't set the DATABASE_ENGINE setting yet." +def ignore(*args, **kwargs): + pass + class DatabaseError(Exception): pass @@ -21,7 +24,7 @@ class IntegrityError(DatabaseError): class DatabaseWrapper: cursor = complain _commit = complain - _rollback = complain + _rollback = ignore def __init__(self, **kwargs): pass diff --git a/django/db/models/query.py b/django/db/models/query.py index 08a7901d6f..a6e702be18 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -554,9 +554,8 @@ class QuerySet(object): class ValuesQuerySet(QuerySet): def __init__(self, *args, **kwargs): super(ValuesQuerySet, self).__init__(*args, **kwargs) - # select_related and select aren't supported in values(). + # select_related isn't supported in values(). self._select_related = False - self._select = {} def iterator(self): try: @@ -566,13 +565,28 @@ class ValuesQuerySet(QuerySet): # self._fields is a list of field names to fetch. if self._fields: - columns = [self.model._meta.get_field(f, many_to_many=False).column for f in self._fields] + #columns = [self.model._meta.get_field(f, many_to_many=False).column for f in self._fields] + if not self._select: + columns = [self.model._meta.get_field(f, many_to_many=False).column for f in self._fields] + else: + columns = [] + for f in self._fields: + if f in [field.name for field in self.model._meta.fields]: + columns.append( self.model._meta.get_field(f, many_to_many=False).column ) + elif not self._select.has_key( f ): + raise FieldDoesNotExist, '%s has no field named %r' % ( self.model._meta.object_name, f ) + field_names = self._fields else: # Default to all fields. columns = [f.column for f in self.model._meta.fields] field_names = [f.attname for f in self.model._meta.fields] select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns] + + # Add any additional SELECTs. + if self._select: + select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), backend.quote_name(s[0])) for s in self._select.items()]) + cursor = connection.cursor() cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) while 1: diff --git a/django/middleware/common.py b/django/middleware/common.py index 2c72c9a583..5f671dff7d 100644 --- a/django/middleware/common.py +++ b/django/middleware/common.py @@ -75,7 +75,7 @@ class CommonMiddleware(object): # Use ETags, if requested. if settings.USE_ETAGS: etag = md5.new(response.content).hexdigest() - if request.META.get('HTTP_IF_NONE_MATCH') == etag: + if response.status_code >= 200 and response.status_code < 300 and request.META.get('HTTP_IF_NONE_MATCH') == etag: response = http.HttpResponseNotModified() else: response['ETag'] = etag diff --git a/django/newforms/forms.py b/django/newforms/forms.py index 952530bbd5..902ad616ed 100644 --- a/django/newforms/forms.py +++ b/django/newforms/forms.py @@ -63,7 +63,7 @@ class BaseForm(StrAndUnicode): self.auto_id = auto_id self.prefix = prefix self.initial = initial or {} - self.__errors = None # Stores the errors after clean() has been called. + self._errors = None # Stores the errors after clean() has been called. # The base_fields class attribute is the *class-wide* definition of # fields. Because a particular *instance* of the class might want to @@ -87,12 +87,12 @@ class BaseForm(StrAndUnicode): raise KeyError('Key %r not found in Form' % name) return BoundField(self, field, name) - def _errors(self): + def _get_errors(self): "Returns an ErrorDict for self.data" - if self.__errors is None: + if self._errors is None: self.full_clean() - return self.__errors - errors = property(_errors) + return self._errors + errors = property(_get_errors) def is_valid(self): """ @@ -189,11 +189,11 @@ class BaseForm(StrAndUnicode): def full_clean(self): """ - Cleans all of self.data and populates self.__errors and self.cleaned_data. + Cleans all of self.data and populates self._errors and + self.cleaned_data. """ - errors = ErrorDict() + self._errors = ErrorDict() if not self.is_bound: # Stop further processing. - self.__errors = errors return self.cleaned_data = {} for name, field in self.fields.items(): @@ -206,16 +206,17 @@ class BaseForm(StrAndUnicode): self.cleaned_data[name] = value if hasattr(self, 'clean_%s' % name): value = getattr(self, 'clean_%s' % name)() - self.cleaned_data[name] = value + self.cleaned_data[name] = value except ValidationError, e: - errors[name] = e.messages + self._errors[name] = e.messages + if name in self.cleaned_data: + del self.cleaned_data[name] try: self.cleaned_data = self.clean() except ValidationError, e: - errors[NON_FIELD_ERRORS] = e.messages - if errors: + self._errors[NON_FIELD_ERRORS] = e.messages + if self._errors: delattr(self, 'cleaned_data') - self.__errors = errors def clean(self): """ diff --git a/django/oldforms/__init__.py b/django/oldforms/__init__.py index eed73ab156..5814eef7ff 100644 --- a/django/oldforms/__init__.py +++ b/django/oldforms/__init__.py @@ -782,7 +782,7 @@ class DecimalField(TextField): try: import decimal except ImportError: - from django.utils import decimal + from django.utils import _decimal as decimal try: return decimal.Decimal(data) except decimal.InvalidOperation, e: diff --git a/django/template/__init__.py b/django/template/__init__.py index 9811a5649d..4f2ddfc8b3 100644 --- a/django/template/__init__.py +++ b/django/template/__init__.py @@ -479,7 +479,7 @@ class TokenParser(object): while i < len(subject) and subject[i] != c: i += 1 if i >= len(subject): - raise TemplateSyntaxError, "Searching for value. Unexpected end of string in column %d: %s" % subject + raise TemplateSyntaxError, "Searching for value. Unexpected end of string in column %d: %s" % (i, subject) i += 1 s = subject[p:i] while i < len(subject) and subject[i] in (' ', '\t'): diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py index 1ebb01442e..371b57e430 100644 --- a/django/template/defaulttags.py +++ b/django/template/defaulttags.py @@ -237,7 +237,7 @@ class RegroupNode(Node): return '' output = [] # list of dictionaries in the format {'grouper': 'key', 'list': [list of contents]} for obj in obj_list: - grouper = self.expression.resolve(Context({'var': obj}), True) + grouper = self.expression.resolve(obj, True) # TODO: Is this a sensible way to determine equality? if output and repr(output[-1]['grouper']) == repr(grouper): output[-1]['list'].append(obj) @@ -847,7 +847,7 @@ def regroup(parser, token): if lastbits_reversed[1][::-1] != 'as': raise TemplateSyntaxError, "next-to-last argument to 'regroup' tag must be 'as'" - expression = parser.compile_filter('var.%s' % lastbits_reversed[2][::-1]) + expression = parser.compile_filter(lastbits_reversed[2][::-1]) var_name = lastbits_reversed[0][::-1] return RegroupNode(target, expression, var_name) diff --git a/django/test/_doctest.py b/django/test/_doctest.py new file mode 100644 index 0000000000..8777a2cbba --- /dev/null +++ b/django/test/_doctest.py @@ -0,0 +1,2674 @@ +# This is a slightly modified version of the doctest.py that shipped with Python 2.4 +# It incorporates changes that have been submitted the the Python ticket tracker +# as ticket #1521051. These changes allow for a DoctestRunner and Doctest base +# class to be specified when constructing a DoctestSuite. + +# 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 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 = '' +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 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 ('' % + (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 +###################################################################### + +class DocTestParser: + """ + A class used to parse strings containing doctest examples. + """ + # This regular expression is used to find doctest examples in a + # string. It defines three groups: `source` is the source code + # (including leading indentation and prompts); `indent` is the + # indentation of the first (PS1) line of the source code; and + # `want` is the expected output (including leading indentation). + _EXAMPLE_RE = re.compile(r''' + # Source consists of a PS1 line followed by zero or more PS2 lines. + (?P + (?:^(?P [ ]*) >>> .*) # PS1 line + (?:\n [ ]* \.\.\. .*)*) # PS2 lines + \n? + # Want consists of any non-blank lines that do not start with PS1. + (?P (?:(?![ ]*$) # Not a blank line + (?![ ]*>>>) # Not a line starting with PS1 + .*$\n? # But any other line + )*) + ''', re.MULTILINE | re.VERBOSE) + + # A regular expression for handling `want` strings that contain + # expected exceptions. It divides `want` into three pieces: + # - the traceback header line (`hdr`) + # - the traceback stack (`stack`) + # - the exception message (`msg`), as generated by + # traceback.format_exception_only() + # `msg` may have multiple lines. We assume/require that the + # exception message is the first non-indented line starting with a word + # character following the traceback header line. + _EXCEPTION_RE = re.compile(r""" + # Grab the traceback header. Different versions of Python have + # said different things on the first traceback line. + ^(?P Traceback\ \( + (?: most\ recent\ call\ last + | innermost\ last + ) \) : + ) + \s* $ # toss trailing whitespace on the header. + (?P .*?) # don't blink: absorb stuff until... + ^ (?P \w+ .*) # a line *starts* with alphanum. + """, re.VERBOSE | re.MULTILINE | re.DOTALL) + + # A callable returning a true value iff its argument is a blank line + # or contains a single comment. + _IS_BLANK_OR_COMMENT = re.compile(r'^[ ]*(#.*)?$').match + + def parse(self, string, name=''): + """ + Divide the given string into examples and intervening text, + and return them as a list of alternating Examples and strings. + Line numbers for the Examples are 0-based. The optional + argument `name` is a name identifying this string, and is only + used for error messages. + """ + string = string.expandtabs() + # If all lines begin with the same indentation, then strip it. + min_indent = self._min_indent(string) + if min_indent > 0: + string = '\n'.join([l[min_indent:] for l in string.split('\n')]) + + output = [] + charno, lineno = 0, 0 + # Find all doctest examples in the string: + for m in self._EXAMPLE_RE.finditer(string): + # Add the pre-example text to `output`. + output.append(string[charno:m.start()]) + # Update lineno (lines before this example) + lineno += string.count('\n', charno, m.start()) + # Extract info from the regexp match. + (source, options, want, exc_msg) = \ + self._parse_example(m, name, lineno) + # Create an Example, and add it to the list. + if not self._IS_BLANK_OR_COMMENT(source): + output.append( Example(source, want, exc_msg, + lineno=lineno, + indent=min_indent+len(m.group('indent')), + options=options) ) + # Update lineno (lines inside this example) + lineno += string.count('\n', m.start(), m.end()) + # Update charno. + charno = m.end() + # Add any remaining post-example text to `output`. + output.append(string[charno:]) + return output + + def get_doctest(self, string, globs, name, filename, lineno): + """ + Extract all doctest examples from the given string, and + collect them into a `DocTest` object. + + `globs`, `name`, `filename`, and `lineno` are attributes for + the new `DocTest` object. See the documentation for `DocTest` + for more information. + """ + return DocTest(self.get_examples(string, name), globs, + name, filename, lineno, string) + + def get_examples(self, string, name=''): + """ + Extract all doctest examples from the given string, and return + them as a list of `Example` objects. Line numbers are + 0-based, because it's most common in doctests that nothing + interesting appears on the same line as opening triple-quote, + and so the first interesting line is called \"line 1\" then. + + The optional argument `name` is a name identifying this + string, and is only used for error messages. + """ + return [x for x in self.parse(string, name) + if isinstance(x, Example)] + + def _parse_example(self, m, name, lineno): + """ + Given a regular expression match from `_EXAMPLE_RE` (`m`), + return a pair `(source, want)`, where `source` is the matched + example's source code (with prompts and indentation stripped); + and `want` is the example's expected output (with indentation + stripped). + + `name` is the string's name, and `lineno` is the line number + where the example starts; both are used for error messages. + """ + # Get the example's indentation level. + indent = len(m.group('indent')) + + # Divide source into lines; check that they're properly + # indented; and then strip their indentation & prompts. + source_lines = m.group('source').split('\n') + self._check_prompt_blank(source_lines, indent, name, lineno) + self._check_prefix(source_lines[1:], ' '*indent + '.', name, lineno) + source = '\n'.join([sl[indent+4:] for sl in source_lines]) + + # Divide want into lines; check that it's properly indented; and + # then strip the indentation. Spaces before the last newline should + # be preserved, so plain rstrip() isn't good enough. + want = m.group('want') + want_lines = want.split('\n') + if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]): + del want_lines[-1] # forget final newline & spaces after it + self._check_prefix(want_lines, ' '*indent, name, + lineno + len(source_lines)) + want = '\n'.join([wl[indent:] for wl in want_lines]) + + # If `want` contains a traceback message, then extract it. + m = self._EXCEPTION_RE.match(want) + if m: + exc_msg = m.group('msg') + else: + exc_msg = None + + # Extract options from the source. + options = self._find_options(source, name, lineno) + + return source, options, want, exc_msg + + # This regular expression looks for option directives in the + # source code of an example. Option directives are comments + # starting with "doctest:". Warning: this may give false + # positives for string-literals that contain the string + # "#doctest:". Eliminating these false positives would require + # actually parsing the string; but we limit them by ignoring any + # line containing "#doctest:" that is *followed* by a quote mark. + _OPTION_DIRECTIVE_RE = re.compile(r'#\s*doctest:\s*([^\n\'"]*)$', + re.MULTILINE) + + def _find_options(self, source, name, lineno): + """ + Return a dictionary containing option overrides extracted from + option directives in the given source string. + + `name` is the string's name, and `lineno` is the line number + where the example starts; both are used for error messages. + """ + options = {} + # (note: with the current regexp, this will match at most once:) + for m in self._OPTION_DIRECTIVE_RE.finditer(source): + option_strings = m.group(1).replace(',', ' ').split() + for option in option_strings: + if (option[0] not in '+-' or + option[1:] not in OPTIONFLAGS_BY_NAME): + raise ValueError('line %r of the doctest for %s ' + 'has an invalid option: %r' % + (lineno+1, name, option)) + flag = OPTIONFLAGS_BY_NAME[option[1:]] + options[flag] = (option[0] == '+') + if options and self._IS_BLANK_OR_COMMENT(source): + raise ValueError('line %r of the doctest for %s has an option ' + 'directive on a line with no example: %r' % + (lineno, name, source)) + return options + + # This regular expression finds the indentation of every non-blank + # line in a string. + _INDENT_RE = re.compile('^([ ]*)(?=\S)', re.MULTILINE) + + def _min_indent(self, s): + "Return the minimum indentation of any non-blank line in `s`" + indents = [len(indent) for indent in self._INDENT_RE.findall(s)] + if len(indents) > 0: + return min(indents) + else: + return 0 + + def _check_prompt_blank(self, lines, indent, name, lineno): + """ + Given the lines of a source string (including prompts and + leading indentation), check to make sure that every prompt is + followed by a space character. If any line is not followed by + a space character, then raise ValueError. + """ + for i, line in enumerate(lines): + if len(line) >= indent+4 and line[indent+3] != ' ': + raise ValueError('line %r of the docstring for %s ' + 'lacks blank after %s: %r' % + (lineno+i+1, name, + line[indent:indent+3], line)) + + def _check_prefix(self, lines, prefix, name, lineno): + """ + Check that every line in the given list starts with the given + prefix; if any line does not, then raise a ValueError. + """ + for i, line in enumerate(lines): + if line and not line.startswith(prefix): + raise ValueError('line %r of the docstring for %s has ' + 'inconsistent leading whitespace: %r' % + (lineno+i+1, name, line)) + + +###################################################################### +## 4. DocTest Finder +###################################################################### + +class DocTestFinder: + """ + A class used to extract the DocTests that are relevant to a given + object, from its docstring and the docstrings of its contained + objects. Doctests can currently be extracted from the following + object types: modules, functions, classes, methods, staticmethods, + classmethods, and properties. + """ + + def __init__(self, verbose=False, parser=DocTestParser(), + recurse=True, _namefilter=None, exclude_empty=True): + """ + Create a new doctest finder. + + The optional argument `parser` specifies a class or + function that should be used to create new DocTest objects (or + objects that implement the same interface as DocTest). The + signature for this factory function should match the signature + of the DocTest constructor. + + If the optional argument `recurse` is false, then `find` will + only examine the given object, and not any contained objects. + + If the optional argument `exclude_empty` is false, then `find` + will include tests for objects with empty docstrings. + """ + self._parser = parser + self._verbose = verbose + self._recurse = recurse + self._exclude_empty = exclude_empty + # _namefilter is undocumented, and exists only for temporary backward- + # compatibility support of testmod's deprecated isprivate mess. + self._namefilter = _namefilter + + def find(self, obj, name=None, module=None, globs=None, + extraglobs=None): + """ + Return a list of the DocTests that are defined by the given + object's docstring, or by any of its contained objects' + docstrings. + + The optional parameter `module` is the module that contains + the given object. If the module is not specified or is None, then + the test finder will attempt to automatically determine the + correct module. The object's module is used: + + - As a default namespace, if `globs` is not specified. + - To prevent the DocTestFinder from extracting DocTests + from objects that are imported from other modules. + - To find the name of the file containing the object. + - To help find the line number of the object within its + file. + + Contained objects whose module does not match `module` are ignored. + + If `module` is False, no attempt to find the module will be made. + This is obscure, of use mostly in tests: if `module` is False, or + is None but cannot be found automatically, then all objects are + considered to belong to the (non-existent) module, so all contained + objects will (recursively) be searched for doctests. + + The globals for each DocTest is formed by combining `globs` + and `extraglobs` (bindings in `extraglobs` override bindings + in `globs`). A new copy of the globals dictionary is created + for each DocTest. If `globs` is not specified, then it + defaults to the module's `__dict__`, if specified, or {} + otherwise. If `extraglobs` is not specified, then it defaults + to {}. + + """ + # If name was not specified, then extract it from the object. + if name is None: + name = getattr(obj, '__name__', None) + if name is None: + raise ValueError("DocTestFinder.find: name must be given " + "when obj.__name__ doesn't exist: %r" % + (type(obj),)) + + # Find the module that contains the given object (if obj is + # a module, then module=obj.). Note: this may fail, in which + # case module will be None. + if module is False: + module = None + elif module is None: + module = inspect.getmodule(obj) + + # Read the module's source code. This is used by + # DocTestFinder._find_lineno to find the line number for a + # given object's docstring. + try: + file = inspect.getsourcefile(obj) or inspect.getfile(obj) + source_lines = linecache.getlines(file) + if not source_lines: + source_lines = None + except TypeError: + source_lines = None + + # Initialize globals, and merge in extraglobs. + if globs is None: + if module is None: + globs = {} + else: + globs = module.__dict__.copy() + else: + globs = globs.copy() + if extraglobs is not None: + globs.update(extraglobs) + + # Recursively explore `obj`, extracting DocTests. + tests = [] + self._find(tests, obj, name, module, source_lines, globs, {}) + return tests + + def _filter(self, obj, prefix, base): + """ + Return true if the given object should not be examined. + """ + return (self._namefilter is not None and + self._namefilter(prefix, base)) + + def _from_module(self, module, object): + """ + Return true if the given object is defined in the given + module. + """ + if module is None: + return True + elif inspect.isfunction(object): + return module.__dict__ is object.func_globals + elif inspect.isclass(object): + return module.__name__ == object.__module__ + elif inspect.getmodule(object) is not None: + return module is inspect.getmodule(object) + elif hasattr(object, '__module__'): + return module.__name__ == object.__module__ + elif isinstance(object, property): + return True # [XX] no way not be sure. + else: + raise ValueError("object must be a class or function") + + def _find(self, tests, obj, name, module, source_lines, globs, seen): + """ + Find tests for the given object and any contained objects, and + add them to `tests`. + """ + if self._verbose: + print 'Finding tests in %s' % name + + # If we've already processed this object, then ignore it. + if id(obj) in seen: + return + seen[id(obj)] = 1 + + # Find a test for this object, and add it to the list of tests. + test = self._get_test(obj, name, module, globs, source_lines) + if test is not None: + tests.append(test) + + # Look for tests in a module's contained objects. + if inspect.ismodule(obj) and self._recurse: + for valname, val in obj.__dict__.items(): + # Check if this contained object should be ignored. + if self._filter(val, name, valname): + continue + valname = '%s.%s' % (name, valname) + # Recurse to functions & classes. + if ((inspect.isfunction(val) or inspect.isclass(val)) and + self._from_module(module, val)): + self._find(tests, val, valname, module, source_lines, + globs, seen) + + # Look for tests in a module's __test__ dictionary. + if inspect.ismodule(obj) and self._recurse: + for valname, val in getattr(obj, '__test__', {}).items(): + if not isinstance(valname, basestring): + raise ValueError("DocTestFinder.find: __test__ keys " + "must be strings: %r" % + (type(valname),)) + if not (inspect.isfunction(val) or inspect.isclass(val) or + inspect.ismethod(val) or inspect.ismodule(val) or + isinstance(val, basestring)): + raise ValueError("DocTestFinder.find: __test__ values " + "must be strings, functions, methods, " + "classes, or modules: %r" % + (type(val),)) + valname = '%s.__test__.%s' % (name, valname) + self._find(tests, val, valname, module, source_lines, + globs, seen) + + # Look for tests in a class's contained objects. + if inspect.isclass(obj) and self._recurse: + for valname, val in obj.__dict__.items(): + # Check if this contained object should be ignored. + if self._filter(val, name, valname): + continue + # Special handling for staticmethod/classmethod. + if isinstance(val, staticmethod): + val = getattr(obj, valname) + if isinstance(val, classmethod): + val = getattr(obj, valname).im_func + + # Recurse to methods, properties, and nested classes. + if ((inspect.isfunction(val) or inspect.isclass(val) or + isinstance(val, property)) and + self._from_module(module, val)): + valname = '%s.%s' % (name, valname) + self._find(tests, val, valname, module, source_lines, + globs, seen) + + def _get_test(self, obj, name, module, globs, source_lines): + """ + Return a DocTest for the given object, if it defines a docstring; + otherwise, return None. + """ + # Extract the object's docstring. If it doesn't have one, + # then return None (no test for this object). + if isinstance(obj, basestring): + docstring = obj + else: + try: + if obj.__doc__ is None: + docstring = '' + else: + docstring = obj.__doc__ + if not isinstance(docstring, basestring): + docstring = str(docstring) + except (TypeError, AttributeError): + docstring = '' + + # Find the docstring's location in the file. + lineno = self._find_lineno(obj, source_lines) + + # Don't bother if the docstring is empty. + if self._exclude_empty and not docstring: + return None + + # Return a DocTest for this object. + if module is None: + filename = None + else: + filename = getattr(module, '__file__', module.__name__) + if filename[-4:] in (".pyc", ".pyo"): + filename = filename[:-1] + return self._parser.get_doctest(docstring, globs, name, + filename, lineno) + + def _find_lineno(self, obj, source_lines): + """ + Return a line number of the given object's docstring. Note: + this method assumes that the object has a docstring. + """ + lineno = None + + # Find the line number for modules. + if inspect.ismodule(obj): + lineno = 0 + + # Find the line number for classes. + # Note: this could be fooled if a class is defined multiple + # times in a single file. + if inspect.isclass(obj): + if source_lines is None: + return None + pat = re.compile(r'^\s*class\s*%s\b' % + getattr(obj, '__name__', '-')) + for i, line in enumerate(source_lines): + if pat.match(line): + lineno = i + break + + # Find the line number for functions & methods. + if inspect.ismethod(obj): obj = obj.im_func + if inspect.isfunction(obj): obj = obj.func_code + if inspect.istraceback(obj): obj = obj.tb_frame + if inspect.isframe(obj): obj = obj.f_code + if inspect.iscode(obj): + lineno = getattr(obj, 'co_firstlineno', None)-1 + + # Find the line number where the docstring starts. Assume + # that it's the first line that begins with a quote mark. + # Note: this could be fooled by a multiline function + # signature, where a continuation line begins with a quote + # mark. + if lineno is not None: + if source_lines is None: + return lineno+1 + pat = re.compile('(^|.*:)\s*\w*("|\')') + for lineno in range(lineno, len(source_lines)): + if pat.match(source_lines[lineno]): + return lineno + + # We couldn't find the line number. + return None + +###################################################################### +## 5. DocTest Runner +###################################################################### + +class DocTestRunner: + """ + A class used to run DocTest test cases, and accumulate statistics. + The `run` method is used to process a single DocTest case. It + returns a tuple `(f, t)`, where `t` is the number of test cases + tried, and `f` is the number of test cases that failed. + + >>> tests = DocTestFinder().find(_TestClass) + >>> runner = DocTestRunner(verbose=False) + >>> for test in tests: + ... print runner.run(test) + (0, 2) + (0, 1) + (0, 2) + (0, 2) + + The `summarize` method prints a summary of all the test cases that + have been run by the runner, and returns an aggregated `(f, t)` + tuple: + + >>> runner.summarize(verbose=1) + 4 items passed all tests: + 2 tests in _TestClass + 2 tests in _TestClass.__init__ + 2 tests in _TestClass.get + 1 tests in _TestClass.square + 7 tests in 4 items. + 7 passed and 0 failed. + Test passed. + (0, 7) + + The aggregated number of tried examples and failed examples is + also available via the `tries` and `failures` attributes: + + >>> runner.tries + 7 + >>> runner.failures + 0 + + The comparison between expected outputs and actual outputs is done + by an `OutputChecker`. This comparison may be customized with a + number of option flags; see the documentation for `testmod` for + more information. If the option flags are insufficient, then the + comparison may also be customized by passing a subclass of + `OutputChecker` to the constructor. + + The test runner's display output can be controlled in two ways. + First, an output function (`out) can be passed to + `TestRunner.run`; this function will be called with strings that + should be displayed. It defaults to `sys.stdout.write`. If + capturing the output is not sufficient, then the display output + can be also customized by subclassing DocTestRunner, and + overriding the methods `report_start`, `report_success`, + `report_unexpected_exception`, and `report_failure`. + """ + # This divider string is used to separate failure messages, and to + # separate sections of the summary. + DIVIDER = "*" * 70 + + def __init__(self, checker=None, verbose=None, optionflags=0): + """ + Create a new test runner. + + Optional keyword arg `checker` is the `OutputChecker` that + should be used to compare the expected outputs and actual + outputs of doctest examples. + + Optional keyword arg 'verbose' prints lots of stuff if true, + only failures if false; by default, it's true iff '-v' is in + sys.argv. + + Optional argument `optionflags` can be used to control how the + test runner compares expected output to actual output, and how + it displays failures. See the documentation for `testmod` for + more information. + """ + self._checker = checker or OutputChecker() + if verbose is None: + verbose = '-v' in sys.argv + self._verbose = verbose + self.optionflags = optionflags + self.original_optionflags = optionflags + + # Keep track of the examples we've run. + self.tries = 0 + self.failures = 0 + self._name2ft = {} + + # Create a fake output target for capturing doctest output. + self._fakeout = _SpoofOut() + + #///////////////////////////////////////////////////////////////// + # Reporting methods + #///////////////////////////////////////////////////////////////// + + def report_start(self, out, test, example): + """ + Report that the test runner is about to process the given + example. (Only displays a message if verbose=True) + """ + if self._verbose: + if example.want: + out('Trying:\n' + _indent(example.source) + + 'Expecting:\n' + _indent(example.want)) + else: + out('Trying:\n' + _indent(example.source) + + 'Expecting nothing\n') + + def report_success(self, out, test, example, got): + """ + Report that the given example ran successfully. (Only + displays a message if verbose=True) + """ + if self._verbose: + out("ok\n") + + def report_failure(self, out, test, example, got): + """ + Report that the given example failed. + """ + out(self._failure_header(test, example) + + self._checker.output_difference(example, got, self.optionflags)) + + def report_unexpected_exception(self, out, test, example, exc_info): + """ + Report that the given example raised an unexpected exception. + """ + out(self._failure_header(test, example) + + 'Exception raised:\n' + _indent(_exception_traceback(exc_info))) + + def _failure_header(self, test, example): + out = [self.DIVIDER] + if test.filename: + if test.lineno is not None and example.lineno is not None: + lineno = test.lineno + example.lineno + 1 + else: + lineno = '?' + out.append('File "%s", line %s, in %s' % + (test.filename, lineno, test.name)) + else: + out.append('Line %s, in %s' % (example.lineno+1, test.name)) + out.append('Failed example:') + source = example.source + out.append(_indent(source)) + return '\n'.join(out) + + #///////////////////////////////////////////////////////////////// + # DocTest Running + #///////////////////////////////////////////////////////////////// + + def __run(self, test, compileflags, out): + """ + Run the examples in `test`. Write the outcome of each example + with one of the `DocTestRunner.report_*` methods, using the + writer function `out`. `compileflags` is the set of compiler + flags that should be used to execute examples. Return a tuple + `(f, t)`, where `t` is the number of examples tried, and `f` + is the number of examples that failed. The examples are run + in the namespace `test.globs`. + """ + # Keep track of the number of failures and tries. + failures = tries = 0 + + # Save the option flags (since option directives can be used + # to modify them). + original_optionflags = self.optionflags + + SUCCESS, FAILURE, BOOM = range(3) # `outcome` state + + check = self._checker.check_output + + # Process each example. + for examplenum, example in enumerate(test.examples): + + # If REPORT_ONLY_FIRST_FAILURE is set, then suppress + # reporting after the first failure. + quiet = (self.optionflags & REPORT_ONLY_FIRST_FAILURE and + failures > 0) + + # Merge in the example's options. + self.optionflags = original_optionflags + if example.options: + for (optionflag, val) in example.options.items(): + if val: + self.optionflags |= optionflag + else: + self.optionflags &= ~optionflag + + # Record that we started this example. + tries += 1 + if not quiet: + self.report_start(out, test, example) + + # Use a special filename for compile(), so we can retrieve + # the source code during interactive debugging (see + # __patched_linecache_getlines). + filename = '' % (test.name, examplenum) + + # Run the example in the given context (globs), and record + # any exception that gets raised. (But don't intercept + # keyboard interrupts.) + try: + # Don't blink! This is where the user's code gets run. + exec compile(example.source, filename, "single", + compileflags, 1) in test.globs + self.debugger.set_continue() # ==== Example Finished ==== + exception = None + except KeyboardInterrupt: + raise + except: + exception = sys.exc_info() + self.debugger.set_continue() # ==== Example Finished ==== + + got = self._fakeout.getvalue() # the actual output + self._fakeout.truncate(0) + outcome = FAILURE # guilty until proved innocent or insane + + # If the example executed without raising any exceptions, + # verify its output. + if exception is None: + if check(example.want, got, self.optionflags): + outcome = SUCCESS + + # The example raised an exception: check if it was expected. + else: + exc_info = sys.exc_info() + exc_msg = traceback.format_exception_only(*exc_info[:2])[-1] + if not quiet: + got += _exception_traceback(exc_info) + + # If `example.exc_msg` is None, then we weren't expecting + # an exception. + if example.exc_msg is None: + outcome = BOOM + + # We expected an exception: see whether it matches. + elif check(example.exc_msg, exc_msg, self.optionflags): + outcome = SUCCESS + + # Another chance if they didn't care about the detail. + elif self.optionflags & IGNORE_EXCEPTION_DETAIL: + m1 = re.match(r'[^:]*:', example.exc_msg) + m2 = re.match(r'[^:]*:', exc_msg) + if m1 and m2 and check(m1.group(0), m2.group(0), + self.optionflags): + outcome = SUCCESS + + # Report the outcome. + if outcome is SUCCESS: + if not quiet: + self.report_success(out, test, example, got) + elif outcome is FAILURE: + if not quiet: + self.report_failure(out, test, example, got) + failures += 1 + elif outcome is BOOM: + if not quiet: + self.report_unexpected_exception(out, test, example, + exc_info) + failures += 1 + else: + assert False, ("unknown outcome", outcome) + + # Restore the option flags (in case they were modified) + self.optionflags = original_optionflags + + # Record and return the number of failures and tries. + self.__record_outcome(test, failures, tries) + return failures, tries + + def __record_outcome(self, test, f, t): + """ + Record the fact that the given DocTest (`test`) generated `f` + failures out of `t` tried examples. + """ + f2, t2 = self._name2ft.get(test.name, (0,0)) + self._name2ft[test.name] = (f+f2, t+t2) + self.failures += f + self.tries += t + + __LINECACHE_FILENAME_RE = re.compile(r'[\w\.]+)' + r'\[(?P\d+)\]>$') + def __patched_linecache_getlines(self, filename, module_globals=None): + m = self.__LINECACHE_FILENAME_RE.match(filename) + if m and m.group('name') == self.test.name: + example = self.test.examples[int(m.group('examplenum'))] + return example.source.splitlines(True) + else: + if sys.version_info < (2, 5, 0): + return self.save_linecache_getlines(filename) + else: + return self.save_linecache_getlines(filename, module_globals) + + def run(self, test, compileflags=None, out=None, clear_globs=True): + """ + Run the examples in `test`, and display the results using the + writer function `out`. + + The examples are run in the namespace `test.globs`. If + `clear_globs` is true (the default), then this namespace will + be cleared after the test runs, to help with garbage + collection. If you would like to examine the namespace after + the test completes, then use `clear_globs=False`. + + `compileflags` gives the set of flags that should be used by + the Python compiler when running the examples. If not + specified, then it will default to the set of future-import + flags that apply to `globs`. + + The output of each example is checked using + `DocTestRunner.check_output`, and the results are formatted by + the `DocTestRunner.report_*` methods. + """ + self.test = test + + if compileflags is None: + compileflags = _extract_future_flags(test.globs) + + save_stdout = sys.stdout + if out is None: + out = save_stdout.write + sys.stdout = self._fakeout + + # Patch pdb.set_trace to restore sys.stdout during interactive + # debugging (so it's not still redirected to self._fakeout). + # Note that the interactive output will go to *our* + # save_stdout, even if that's not the real sys.stdout; this + # allows us to write test cases for the set_trace behavior. + save_set_trace = pdb.set_trace + self.debugger = _OutputRedirectingPdb(save_stdout) + self.debugger.reset() + pdb.set_trace = self.debugger.set_trace + + # Patch linecache.getlines, so we can see the example's source + # when we're inside the debugger. + self.save_linecache_getlines = linecache.getlines + linecache.getlines = self.__patched_linecache_getlines + + try: + return self.__run(test, compileflags, out) + finally: + sys.stdout = save_stdout + pdb.set_trace = save_set_trace + linecache.getlines = self.save_linecache_getlines + if clear_globs: + test.globs.clear() + + #///////////////////////////////////////////////////////////////// + # Summarization + #///////////////////////////////////////////////////////////////// + def summarize(self, verbose=None): + """ + Print a summary of all the test cases that have been run by + this DocTestRunner, and return a tuple `(f, t)`, where `f` is + the total number of failed examples, and `t` is the total + number of tried examples. + + The optional `verbose` argument controls how detailed the + summary is. If the verbosity is not specified, then the + DocTestRunner's verbosity is used. + """ + if verbose is None: + verbose = self._verbose + notests = [] + passed = [] + failed = [] + totalt = totalf = 0 + for x in self._name2ft.items(): + name, (f, t) = x + assert f <= t + totalt += t + totalf += f + if t == 0: + notests.append(name) + elif f == 0: + passed.append( (name, t) ) + else: + failed.append(x) + if verbose: + if notests: + print len(notests), "items had no tests:" + notests.sort() + for thing in notests: + print " ", thing + if passed: + print len(passed), "items passed all tests:" + passed.sort() + for thing, count in passed: + print " %3d tests in %s" % (count, thing) + if failed: + print self.DIVIDER + print len(failed), "items had failures:" + failed.sort() + for thing, (f, t) in failed: + print " %3d of %3d in %s" % (f, t, thing) + if verbose: + print totalt, "tests in", len(self._name2ft), "items." + print totalt - totalf, "passed and", totalf, "failed." + if totalf: + print "***Test Failed***", totalf, "failures." + elif verbose: + print "Test passed." + return totalf, totalt + + #///////////////////////////////////////////////////////////////// + # Backward compatibility cruft to maintain doctest.master. + #///////////////////////////////////////////////////////////////// + def merge(self, other): + d = self._name2ft + for name, (f, t) in other._name2ft.items(): + if name in d: + print "*** DocTestRunner.merge: '" + name + "' in both" \ + " testers; summing outcomes." + f2, t2 = d[name] + f = f + f2 + t = t + t2 + d[name] = f, t + +class OutputChecker: + """ + A class used to check the whether the actual output from a doctest + example matches the expected output. `OutputChecker` defines two + methods: `check_output`, which compares a given pair of outputs, + and returns true if they match; and `output_difference`, which + returns a string describing the differences between two outputs. + """ + def check_output(self, want, got, optionflags): + """ + Return True iff the actual output from an example (`got`) + matches the expected output (`want`). These strings are + always considered to match if they are identical; but + depending on what option flags the test runner is using, + several non-exact match types are also possible. See the + documentation for `TestRunner` for more information about + option flags. + """ + # Handle the common case first, for efficiency: + # if they're string-identical, always return true. + if got == want: + return True + + # The values True and False replaced 1 and 0 as the return + # value for boolean comparisons in Python 2.3. + if not (optionflags & DONT_ACCEPT_TRUE_FOR_1): + if (got,want) == ("True\n", "1\n"): + return True + if (got,want) == ("False\n", "0\n"): + return True + + # can be used as a special sequence to signify a + # blank line, unless the DONT_ACCEPT_BLANKLINE flag is used. + if not (optionflags & DONT_ACCEPT_BLANKLINE): + # Replace in want with a blank line. + want = re.sub('(?m)^%s\s*?$' % re.escape(BLANKLINE_MARKER), + '', want) + # If a line in got contains only spaces, then remove the + # spaces. + got = re.sub('(?m)^\s*?$', '', got) + if got == want: + return True + + # This flag causes doctest to ignore any differences in the + # contents of whitespace strings. Note that this can be used + # in conjunction with the ELLIPSIS flag. + if optionflags & NORMALIZE_WHITESPACE: + got = ' '.join(got.split()) + want = ' '.join(want.split()) + if got == want: + return True + + # The ELLIPSIS flag says to let the sequence "..." in `want` + # match any substring in `got`. + if optionflags & ELLIPSIS: + if _ellipsis_match(want, got): + return True + + # We didn't find any match; return false. + return False + + # Should we do a fancy diff? + def _do_a_fancy_diff(self, want, got, optionflags): + # Not unless they asked for a fancy diff. + if not optionflags & (REPORT_UDIFF | + REPORT_CDIFF | + REPORT_NDIFF): + return False + + # If expected output uses ellipsis, a meaningful fancy diff is + # too hard ... or maybe not. In two real-life failures Tim saw, + # a diff was a major help anyway, so this is commented out. + # [todo] _ellipsis_match() knows which pieces do and don't match, + # and could be the basis for a kick-ass diff in this case. + ##if optionflags & ELLIPSIS and ELLIPSIS_MARKER in want: + ## return False + + # ndiff does intraline difference marking, so can be useful even + # for 1-line differences. + if optionflags & REPORT_NDIFF: + return True + + # The other diff types need at least a few lines to be helpful. + return want.count('\n') > 2 and got.count('\n') > 2 + + def output_difference(self, example, got, optionflags): + """ + Return a string describing the differences between the + expected output for a given example (`example`) and the actual + output (`got`). `optionflags` is the set of option flags used + to compare `want` and `got`. + """ + want = example.want + # If s are being used, then replace blank lines + # with in the actual output string. + if not (optionflags & DONT_ACCEPT_BLANKLINE): + got = re.sub('(?m)^[ ]*(?=\n)', BLANKLINE_MARKER, got) + + # Check if we should use diff. + if self._do_a_fancy_diff(want, got, optionflags): + # Split want & got into lines. + want_lines = want.splitlines(True) # True == keep line ends + got_lines = got.splitlines(True) + # Use difflib to find their differences. + if optionflags & REPORT_UDIFF: + diff = difflib.unified_diff(want_lines, got_lines, n=2) + diff = list(diff)[2:] # strip the diff header + kind = 'unified diff with -expected +actual' + elif optionflags & REPORT_CDIFF: + diff = difflib.context_diff(want_lines, got_lines, n=2) + diff = list(diff)[2:] # strip the diff header + kind = 'context diff with expected followed by actual' + elif optionflags & REPORT_NDIFF: + engine = difflib.Differ(charjunk=difflib.IS_CHARACTER_JUNK) + diff = list(engine.compare(want_lines, got_lines)) + kind = 'ndiff with -expected +actual' + else: + assert 0, 'Bad diff option' + # Remove trailing whitespace on diff output. + diff = [line.rstrip() + '\n' for line in diff] + return 'Differences (%s):\n' % kind + _indent(''.join(diff)) + + # If we're not using diff, then simply list the expected + # output followed by the actual output. + if want and got: + return 'Expected:\n%sGot:\n%s' % (_indent(want), _indent(got)) + elif want: + return 'Expected:\n%sGot nothing\n' % _indent(want) + elif got: + return 'Expected nothing\nGot:\n%s' % _indent(got) + else: + return 'Expected nothing\nGot nothing\n' + +class DocTestFailure(Exception): + """A DocTest example has failed in debugging mode. + + The exception instance has variables: + + - test: the DocTest object being run + + - excample: the Example object that failed + + - got: the actual output + """ + def __init__(self, test, example, got): + self.test = test + self.example = example + self.got = got + + def __str__(self): + return str(self.test) + +class UnexpectedException(Exception): + """A DocTest example has encountered an unexpected exception + + The exception instance has variables: + + - test: the DocTest object being run + + - excample: the Example object that failed + + - exc_info: the exception info + """ + def __init__(self, test, example, exc_info): + self.test = test + self.example = example + self.exc_info = exc_info + + def __str__(self): + return str(self.test) + +class DebugRunner(DocTestRunner): + r"""Run doc tests but raise an exception as soon as there is a failure. + + If an unexpected exception occurs, an UnexpectedException is raised. + It contains the test, the example, and the original exception: + + >>> runner = DebugRunner(verbose=False) + >>> test = DocTestParser().get_doctest('>>> raise KeyError\n42', + ... {}, 'foo', 'foo.py', 0) + >>> try: + ... runner.run(test) + ... except UnexpectedException, failure: + ... pass + + >>> failure.test is test + True + + >>> failure.example.want + '42\n' + + >>> exc_info = failure.exc_info + >>> raise exc_info[0], exc_info[1], exc_info[2] + Traceback (most recent call last): + ... + KeyError + + We wrap the original exception to give the calling application + access to the test and example information. + + If the output doesn't match, then a DocTestFailure is raised: + + >>> test = DocTestParser().get_doctest(''' + ... >>> x = 1 + ... >>> x + ... 2 + ... ''', {}, 'foo', 'foo.py', 0) + + >>> try: + ... runner.run(test) + ... except DocTestFailure, failure: + ... pass + + DocTestFailure objects provide access to the test: + + >>> failure.test is test + True + + As well as to the example: + + >>> failure.example.want + '2\n' + + and the actual output: + + >>> failure.got + '1\n' + + If a failure or error occurs, the globals are left intact: + + >>> del test.globs['__builtins__'] + >>> test.globs + {'x': 1} + + >>> test = DocTestParser().get_doctest(''' + ... >>> x = 2 + ... >>> raise KeyError + ... ''', {}, 'foo', 'foo.py', 0) + + >>> runner.run(test) + Traceback (most recent call last): + ... + UnexpectedException: + + >>> del test.globs['__builtins__'] + >>> test.globs + {'x': 2} + + But the globals are cleared if there is no error: + + >>> test = DocTestParser().get_doctest(''' + ... >>> x = 2 + ... ''', {}, 'foo', 'foo.py', 0) + + >>> runner.run(test) + (0, 1) + + >>> test.globs + {} + + """ + + def run(self, test, compileflags=None, out=None, clear_globs=True): + r = DocTestRunner.run(self, test, compileflags, out, False) + if clear_globs: + test.globs.clear() + return r + + def report_unexpected_exception(self, out, test, example, exc_info): + raise UnexpectedException(test, example, exc_info) + + def report_failure(self, out, test, example, got): + raise DocTestFailure(test, example, got) + +###################################################################### +## 6. Test Functions +###################################################################### +# These should be backwards compatible. + +# For backward compatibility, a global instance of a DocTestRunner +# class, updated by testmod. +master = None + +def testmod(m=None, name=None, globs=None, verbose=None, isprivate=None, + report=True, optionflags=0, extraglobs=None, + raise_on_error=False, exclude_empty=False): + """m=None, name=None, globs=None, verbose=None, isprivate=None, + report=True, optionflags=0, extraglobs=None, raise_on_error=False, + exclude_empty=False + + Test examples in docstrings in functions and classes reachable + from module m (or the current module if m is not supplied), starting + with m.__doc__. Unless isprivate is specified, private names + are not skipped. + + Also test examples reachable from dict m.__test__ if it exists and is + not None. m.__test__ maps names to functions, classes and strings; + function and class docstrings are tested even if the name is private; + strings are tested directly, as if they were docstrings. + + Return (#failures, #tests). + + See doctest.__doc__ for an overview. + + Optional keyword arg "name" gives the name of the module; by default + use m.__name__. + + Optional keyword arg "globs" gives a dict to be used as the globals + when executing examples; by default, use m.__dict__. A copy of this + dict is actually used for each docstring, so that each docstring's + examples start with a clean slate. + + Optional keyword arg "extraglobs" gives a dictionary that should be + merged into the globals that are used to execute examples. By + default, no extra globals are used. This is new in 2.4. + + Optional keyword arg "verbose" prints lots of stuff if true, prints + only failures if false; by default, it's true iff "-v" is in sys.argv. + + Optional keyword arg "report" prints a summary at the end when true, + else prints nothing at the end. In verbose mode, the summary is + detailed, else very brief (in fact, empty if all tests passed). + + Optional keyword arg "optionflags" or's together module constants, + and defaults to 0. This is new in 2.3. Possible values (see the + docs for details): + + DONT_ACCEPT_TRUE_FOR_1 + DONT_ACCEPT_BLANKLINE + NORMALIZE_WHITESPACE + ELLIPSIS + IGNORE_EXCEPTION_DETAIL + REPORT_UDIFF + REPORT_CDIFF + REPORT_NDIFF + REPORT_ONLY_FIRST_FAILURE + + Optional keyword arg "raise_on_error" raises an exception on the + first unexpected exception or failure. This allows failures to be + post-mortem debugged. + + Deprecated in Python 2.4: + Optional keyword arg "isprivate" specifies a function used to + determine whether a name is private. The default function is + treat all functions as public. Optionally, "isprivate" can be + set to doctest.is_private to skip over functions marked as private + using the underscore naming convention; see its docs for details. + + Advanced tomfoolery: testmod runs methods of a local instance of + class doctest.Tester, then merges the results into (or creates) + global Tester instance doctest.master. Methods of doctest.master + can be called directly too, if you want to do something unusual. + Passing report=0 to testmod is especially useful then, to delay + displaying a summary. Invoke doctest.master.summarize(verbose) + when you're done fiddling. + """ + global master + + if isprivate is not None: + warnings.warn("the isprivate argument is deprecated; " + "examine DocTestFinder.find() lists instead", + DeprecationWarning) + + # If no module was given, then use __main__. + if m is None: + # DWA - m will still be None if this wasn't invoked from the command + # line, in which case the following TypeError is about as good an error + # as we should expect + m = sys.modules.get('__main__') + + # Check that we were actually given a module. + if not inspect.ismodule(m): + raise TypeError("testmod: module required; %r" % (m,)) + + # If no name was given, then use the module's name. + if name is None: + name = m.__name__ + + # Find, parse, and run all tests in the given module. + finder = DocTestFinder(_namefilter=isprivate, exclude_empty=exclude_empty) + + if raise_on_error: + runner = DebugRunner(verbose=verbose, optionflags=optionflags) + else: + runner = DocTestRunner(verbose=verbose, optionflags=optionflags) + + for test in finder.find(m, name, globs=globs, extraglobs=extraglobs): + runner.run(test) + + if report: + runner.summarize() + + if master is None: + master = runner + else: + master.merge(runner) + + return runner.failures, runner.tries + +def testfile(filename, module_relative=True, name=None, package=None, + globs=None, verbose=None, report=True, optionflags=0, + extraglobs=None, raise_on_error=False, parser=DocTestParser()): + """ + Test examples in the given file. Return (#failures, #tests). + + Optional keyword arg "module_relative" specifies how filenames + should be interpreted: + + - If "module_relative" is True (the default), then "filename" + specifies a module-relative path. By default, this path is + relative to the calling module's directory; but if the + "package" argument is specified, then it is relative to that + package. To ensure os-independence, "filename" should use + "/" characters to separate path segments, and should not + be an absolute path (i.e., it may not begin with "/"). + + - If "module_relative" is False, then "filename" specifies an + os-specific path. The path may be absolute or relative (to + the current working directory). + + Optional keyword arg "name" gives the name of the test; by default + use the file's basename. + + Optional keyword argument "package" is a Python package or the + name of a Python package whose directory should be used as the + base directory for a module relative filename. If no package is + specified, then the calling module's directory is used as the base + directory for module relative filenames. It is an error to + specify "package" if "module_relative" is False. + + Optional keyword arg "globs" gives a dict to be used as the globals + when executing examples; by default, use {}. A copy of this dict + is actually used for each docstring, so that each docstring's + examples start with a clean slate. + + Optional keyword arg "extraglobs" gives a dictionary that should be + merged into the globals that are used to execute examples. By + default, no extra globals are used. + + Optional keyword arg "verbose" prints lots of stuff if true, prints + only failures if false; by default, it's true iff "-v" is in sys.argv. + + Optional keyword arg "report" prints a summary at the end when true, + else prints nothing at the end. In verbose mode, the summary is + detailed, else very brief (in fact, empty if all tests passed). + + Optional keyword arg "optionflags" or's together module constants, + and defaults to 0. Possible values (see the docs for details): + + DONT_ACCEPT_TRUE_FOR_1 + DONT_ACCEPT_BLANKLINE + NORMALIZE_WHITESPACE + ELLIPSIS + IGNORE_EXCEPTION_DETAIL + REPORT_UDIFF + REPORT_CDIFF + REPORT_NDIFF + REPORT_ONLY_FIRST_FAILURE + + Optional keyword arg "raise_on_error" raises an exception on the + first unexpected exception or failure. This allows failures to be + post-mortem debugged. + + Optional keyword arg "parser" specifies a DocTestParser (or + subclass) that should be used to extract tests from the files. + + Advanced tomfoolery: testmod runs methods of a local instance of + class doctest.Tester, then merges the results into (or creates) + global Tester instance doctest.master. Methods of doctest.master + can be called directly too, if you want to do something unusual. + Passing report=0 to testmod is especially useful then, to delay + displaying a summary. Invoke doctest.master.summarize(verbose) + when you're done fiddling. + """ + global master + + if package and not module_relative: + raise ValueError("Package may only be specified for module-" + "relative paths.") + + # Relativize the path + if module_relative: + package = _normalize_module(package) + filename = _module_relative_path(package, filename) + + # If no name was given, then use the file's name. + if name is None: + name = os.path.basename(filename) + + # Assemble the globals. + if globs is None: + globs = {} + else: + globs = globs.copy() + if extraglobs is not None: + globs.update(extraglobs) + + if raise_on_error: + runner = DebugRunner(verbose=verbose, optionflags=optionflags) + else: + runner = DocTestRunner(verbose=verbose, optionflags=optionflags) + + # Read the file, convert it to a test, and run it. + s = open(filename).read() + test = parser.get_doctest(s, globs, name, filename, 0) + runner.run(test) + + if report: + runner.summarize() + + if master is None: + master = runner + else: + master.merge(runner) + + return runner.failures, runner.tries + +def run_docstring_examples(f, globs, verbose=False, name="NoName", + compileflags=None, optionflags=0): + """ + Test examples in the given object's docstring (`f`), using `globs` + as globals. Optional argument `name` is used in failure messages. + If the optional argument `verbose` is true, then generate output + even if there are no failures. + + `compileflags` gives the set of flags that should be used by the + Python compiler when running the examples. If not specified, then + it will default to the set of future-import flags that apply to + `globs`. + + Optional keyword arg `optionflags` specifies options for the + testing and output. See the documentation for `testmod` for more + information. + """ + # Find, parse, and run all tests in the given module. + finder = DocTestFinder(verbose=verbose, recurse=False) + runner = DocTestRunner(verbose=verbose, optionflags=optionflags) + for test in finder.find(f, name, globs=globs): + runner.run(test, compileflags=compileflags) + +###################################################################### +## 7. Tester +###################################################################### +# This is provided only for backwards compatibility. It's not +# actually used in any way. + +class Tester: + def __init__(self, mod=None, globs=None, verbose=None, + isprivate=None, optionflags=0): + + warnings.warn("class Tester is deprecated; " + "use class doctest.DocTestRunner instead", + DeprecationWarning, stacklevel=2) + if mod is None and globs is None: + raise TypeError("Tester.__init__: must specify mod or globs") + if mod is not None and not inspect.ismodule(mod): + raise TypeError("Tester.__init__: mod must be a module; %r" % + (mod,)) + if globs is None: + globs = mod.__dict__ + self.globs = globs + + self.verbose = verbose + self.isprivate = isprivate + self.optionflags = optionflags + self.testfinder = DocTestFinder(_namefilter=isprivate) + self.testrunner = DocTestRunner(verbose=verbose, + optionflags=optionflags) + + def runstring(self, s, name): + test = DocTestParser().get_doctest(s, self.globs, name, None, None) + if self.verbose: + print "Running string", name + (f,t) = self.testrunner.run(test) + if self.verbose: + print f, "of", t, "examples failed in string", name + return (f,t) + + def rundoc(self, object, name=None, module=None): + f = t = 0 + tests = self.testfinder.find(object, name, module=module, + globs=self.globs) + for test in tests: + (f2, t2) = self.testrunner.run(test) + (f,t) = (f+f2, t+t2) + return (f,t) + + def rundict(self, d, name, module=None): + import new + m = new.module(name) + m.__dict__.update(d) + if module is None: + module = False + return self.rundoc(m, name, module) + + def run__test__(self, d, name): + import new + m = new.module(name) + m.__test__ = d + return self.rundoc(m, name) + + def summarize(self, verbose=None): + return self.testrunner.summarize(verbose) + + def merge(self, other): + self.testrunner.merge(other.testrunner) + +###################################################################### +## 8. Unittest Support +###################################################################### + +_unittest_reportflags = 0 + +def set_unittest_reportflags(flags): + """Sets the unittest option flags. + + The old flag is returned so that a runner could restore the old + value if it wished to: + + >>> old = _unittest_reportflags + >>> set_unittest_reportflags(REPORT_NDIFF | + ... REPORT_ONLY_FIRST_FAILURE) == old + True + + >>> import doctest + >>> doctest._unittest_reportflags == (REPORT_NDIFF | + ... REPORT_ONLY_FIRST_FAILURE) + True + + Only reporting flags can be set: + + >>> set_unittest_reportflags(ELLIPSIS) + Traceback (most recent call last): + ... + ValueError: ('Only reporting flags allowed', 8) + + >>> set_unittest_reportflags(old) == (REPORT_NDIFF | + ... REPORT_ONLY_FIRST_FAILURE) + True + """ + global _unittest_reportflags + + if (flags & REPORTING_FLAGS) != flags: + raise ValueError("Only reporting flags allowed", flags) + old = _unittest_reportflags + _unittest_reportflags = flags + return old + + +class DocTestCase(unittest.TestCase): + + def __init__(self, test, optionflags=0, setUp=None, tearDown=None, + checker=None, runner=DocTestRunner): + + unittest.TestCase.__init__(self) + self._dt_optionflags = optionflags + self._dt_checker = checker + self._dt_test = test + self._dt_setUp = setUp + self._dt_tearDown = tearDown + self._dt_runner = runner + + def setUp(self): + test = self._dt_test + + if self._dt_setUp is not None: + self._dt_setUp(test) + + def tearDown(self): + test = self._dt_test + + if self._dt_tearDown is not None: + self._dt_tearDown(test) + + test.globs.clear() + + def runTest(self): + test = self._dt_test + old = sys.stdout + new = StringIO() + optionflags = self._dt_optionflags + + if not (optionflags & REPORTING_FLAGS): + # The option flags don't include any reporting flags, + # so add the default reporting flags + optionflags |= _unittest_reportflags + + runner = self._dt_runner(optionflags=optionflags, + checker=self._dt_checker, verbose=False) + + try: + runner.DIVIDER = "-"*70 + failures, tries = runner.run( + test, out=new.write, clear_globs=False) + finally: + sys.stdout = old + + if failures: + raise self.failureException(self.format_failure(new.getvalue())) + + def format_failure(self, err): + test = self._dt_test + if test.lineno is None: + lineno = 'unknown line number' + else: + lineno = '%s' % test.lineno + lname = '.'.join(test.name.split('.')[-1:]) + return ('Failed doctest test for %s\n' + ' File "%s", line %s, in %s\n\n%s' + % (test.name, test.filename, lineno, lname, err) + ) + + def debug(self): + r"""Run the test case without results and without catching exceptions + + The unit test framework includes a debug method on test cases + and test suites to support post-mortem debugging. The test code + is run in such a way that errors are not caught. This way a + caller can catch the errors and initiate post-mortem debugging. + + The DocTestCase provides a debug method that raises + UnexpectedException errors if there is an unexepcted + exception: + + >>> test = DocTestParser().get_doctest('>>> raise KeyError\n42', + ... {}, 'foo', 'foo.py', 0) + >>> case = DocTestCase(test) + >>> try: + ... case.debug() + ... except UnexpectedException, failure: + ... pass + + The UnexpectedException contains the test, the example, and + the original exception: + + >>> failure.test is test + True + + >>> failure.example.want + '42\n' + + >>> exc_info = failure.exc_info + >>> raise exc_info[0], exc_info[1], exc_info[2] + Traceback (most recent call last): + ... + KeyError + + If the output doesn't match, then a DocTestFailure is raised: + + >>> test = DocTestParser().get_doctest(''' + ... >>> x = 1 + ... >>> x + ... 2 + ... ''', {}, 'foo', 'foo.py', 0) + >>> case = DocTestCase(test) + + >>> try: + ... case.debug() + ... except DocTestFailure, failure: + ... pass + + DocTestFailure objects provide access to the test: + + >>> failure.test is test + True + + As well as to the example: + + >>> failure.example.want + '2\n' + + and the actual output: + + >>> failure.got + '1\n' + + """ + + self.setUp() + runner = DebugRunner(optionflags=self._dt_optionflags, + checker=self._dt_checker, verbose=False) + runner.run(self._dt_test) + self.tearDown() + + def id(self): + return self._dt_test.name + + def __repr__(self): + name = self._dt_test.name.split('.') + return "%s (%s)" % (name[-1], '.'.join(name[:-1])) + + __str__ = __repr__ + + def shortDescription(self): + return "Doctest: " + self._dt_test.name + +def DocTestSuite(module=None, globs=None, extraglobs=None, test_finder=None, + test_class=DocTestCase, **options): + """ + Convert doctest tests for a module to a unittest test suite. + + This converts each documentation string in a module that + contains doctest tests to a unittest test case. If any of the + tests in a doc string fail, then the test case fails. An exception + is raised showing the name of the file containing the test and a + (sometimes approximate) line number. + + The `module` argument provides the module to be tested. The argument + can be either a module or a module name. + + If no argument is given, the calling module is used. + + A number of options may be provided as keyword arguments: + + setUp + A set-up function. This is called before running the + tests in each file. The setUp function will be passed a DocTest + object. The setUp function can access the test globals as the + globs attribute of the test passed. + + tearDown + A tear-down function. This is called after running the + tests in each file. The tearDown function will be passed a DocTest + object. The tearDown function can access the test globals as the + globs attribute of the test passed. + + globs + A dictionary containing initial global variables for the tests. + + optionflags + A set of doctest option flags expressed as an integer. + """ + + if test_finder is None: + test_finder = DocTestFinder() + + module = _normalize_module(module) + tests = test_finder.find(module, globs=globs, extraglobs=extraglobs) + if globs is None: + globs = module.__dict__ + if not tests: + # Why do we want to do this? Because it reveals a bug that might + # otherwise be hidden. + raise ValueError(module, "has no tests") + + tests.sort() + suite = unittest.TestSuite() + for test in tests: + if len(test.examples) == 0: + continue + if not test.filename: + filename = module.__file__ + if filename[-4:] in (".pyc", ".pyo"): + filename = filename[:-1] + test.filename = filename + suite.addTest(test_class(test, **options)) + + return suite + +class DocFileCase(DocTestCase): + + def id(self): + return '_'.join(self._dt_test.name.split('.')) + + def __repr__(self): + return self._dt_test.filename + __str__ = __repr__ + + def format_failure(self, err): + return ('Failed doctest test for %s\n File "%s", line 0\n\n%s' + % (self._dt_test.name, self._dt_test.filename, err) + ) + +def DocFileTest(path, module_relative=True, package=None, + globs=None, parser=DocTestParser(), **options): + if globs is None: + globs = {} + + if package and not module_relative: + raise ValueError("Package may only be specified for module-" + "relative paths.") + + # Relativize the path. + if module_relative: + package = _normalize_module(package) + path = _module_relative_path(package, path) + + # Find the file and read it. + name = os.path.basename(path) + doc = open(path).read() + + # Convert it to a test, and wrap it in a DocFileCase. + test = parser.get_doctest(doc, globs, name, path, 0) + return DocFileCase(test, **options) + +def DocFileSuite(*paths, **kw): + """A unittest suite for one or more doctest files. + + The path to each doctest file is given as a string; the + interpretation of that string depends on the keyword argument + "module_relative". + + A number of options may be provided as keyword arguments: + + module_relative + If "module_relative" is True, then the given file paths are + interpreted as os-independent module-relative paths. By + default, these paths are relative to the calling module's + directory; but if the "package" argument is specified, then + they are relative to that package. To ensure os-independence, + "filename" should use "/" characters to separate path + segments, and may not be an absolute path (i.e., it may not + begin with "/"). + + If "module_relative" is False, then the given file paths are + interpreted as os-specific paths. These paths may be absolute + or relative (to the current working directory). + + package + A Python package or the name of a Python package whose directory + should be used as the base directory for module relative paths. + If "package" is not specified, then the calling module's + directory is used as the base directory for module relative + filenames. It is an error to specify "package" if + "module_relative" is False. + + setUp + A set-up function. This is called before running the + tests in each file. The setUp function will be passed a DocTest + object. The setUp function can access the test globals as the + globs attribute of the test passed. + + tearDown + A tear-down function. This is called after running the + tests in each file. The tearDown function will be passed a DocTest + object. The tearDown function can access the test globals as the + globs attribute of the test passed. + + globs + A dictionary containing initial global variables for the tests. + + optionflags + A set of doctest option flags expressed as an integer. + + parser + A DocTestParser (or subclass) that should be used to extract + tests from the files. + """ + suite = unittest.TestSuite() + + # We do this here so that _normalize_module is called at the right + # level. If it were called in DocFileTest, then this function + # would be the caller and we might guess the package incorrectly. + if kw.get('module_relative', True): + kw['package'] = _normalize_module(kw.get('package')) + + for path in paths: + suite.addTest(DocFileTest(path, **kw)) + + return suite + +###################################################################### +## 9. Debugging Support +###################################################################### + +def script_from_examples(s): + r"""Extract script from text with examples. + + Converts text with examples to a Python script. Example input is + converted to regular code. Example output and all other words + are converted to comments: + + >>> text = ''' + ... Here are examples of simple math. + ... + ... Python has super accurate integer addition + ... + ... >>> 2 + 2 + ... 5 + ... + ... And very friendly error messages: + ... + ... >>> 1/0 + ... To Infinity + ... And + ... Beyond + ... + ... You can use logic if you want: + ... + ... >>> if 0: + ... ... blah + ... ... blah + ... ... + ... + ... Ho hum + ... ''' + + >>> print script_from_examples(text) + # Here are examples of simple math. + # + # Python has super accurate integer addition + # + 2 + 2 + # Expected: + ## 5 + # + # And very friendly error messages: + # + 1/0 + # Expected: + ## To Infinity + ## And + ## Beyond + # + # You can use logic if you want: + # + if 0: + blah + blah + # + # Ho hum + """ + output = [] + for piece in DocTestParser().parse(s): + if isinstance(piece, Example): + # Add the example's source code (strip trailing NL) + output.append(piece.source[:-1]) + # Add the expected output: + want = piece.want + if want: + output.append('# Expected:') + output += ['## '+l for l in want.split('\n')[:-1]] + else: + # Add non-example text. + output += [_comment_line(l) + for l in piece.split('\n')[:-1]] + + # Trim junk on both ends. + while output and output[-1] == '#': + output.pop() + while output and output[0] == '#': + output.pop(0) + # Combine the output, and return it. + return '\n'.join(output) + +def testsource(module, name): + """Extract the test sources from a doctest docstring as a script. + + Provide the module (or dotted name of the module) containing the + test to be debugged and the name (within the module) of the object + with the doc string with tests to be debugged. + """ + module = _normalize_module(module) + tests = DocTestFinder().find(module) + test = [t for t in tests if t.name == name] + if not test: + raise ValueError(name, "not found in tests") + test = test[0] + testsrc = script_from_examples(test.docstring) + return testsrc + +def debug_src(src, pm=False, globs=None): + """Debug a single doctest docstring, in argument `src`'""" + testsrc = script_from_examples(src) + debug_script(testsrc, pm, globs) + +def debug_script(src, pm=False, globs=None): + "Debug a test script. `src` is the script, as a string." + import pdb + + # Note that tempfile.NameTemporaryFile() cannot be used. As the + # docs say, a file so created cannot be opened by name a second time + # on modern Windows boxes, and execfile() needs to open it. + srcfilename = tempfile.mktemp(".py", "doctestdebug") + f = open(srcfilename, 'w') + f.write(src) + f.close() + + try: + if globs: + globs = globs.copy() + else: + globs = {} + + if pm: + try: + execfile(srcfilename, globs, globs) + except: + print sys.exc_info()[1] + pdb.post_mortem(sys.exc_info()[2]) + else: + # Note that %r is vital here. '%s' instead can, e.g., cause + # backslashes to get treated as metacharacters on Windows. + pdb.run("execfile(%r)" % srcfilename, globs, globs) + + finally: + os.remove(srcfilename) + +def debug(module, name, pm=False): + """Debug a single doctest docstring. + + Provide the module (or dotted name of the module) containing the + test to be debugged and the name (within the module) of the object + with the docstring with tests to be debugged. + """ + module = _normalize_module(module) + testsrc = testsource(module, name) + debug_script(testsrc, pm, module.__dict__) + +###################################################################### +## 10. Example Usage +###################################################################### +class _TestClass: + """ + A pointless class, for sanity-checking of docstring testing. + + Methods: + square() + get() + + >>> _TestClass(13).get() + _TestClass(-12).get() + 1 + >>> hex(_TestClass(13).square().get()) + '0xa9' + """ + + def __init__(self, val): + """val -> _TestClass object with associated value val. + + >>> t = _TestClass(123) + >>> print t.get() + 123 + """ + + self.val = val + + def square(self): + """square() -> square TestClass's associated value + + >>> _TestClass(13).square().get() + 169 + """ + + self.val = self.val ** 2 + return self + + def get(self): + """get() -> return TestClass's associated value. + + >>> x = _TestClass(-42) + >>> print x.get() + -42 + """ + + return self.val + +__test__ = {"_TestClass": _TestClass, + "string": r""" + Example of a string object, searched as-is. + >>> x = 1; y = 2 + >>> x + y, x * y + (3, 2) + """, + + "bool-int equivalence": r""" + In 2.2, boolean expressions displayed + 0 or 1. By default, we still accept + them. This can be disabled by passing + DONT_ACCEPT_TRUE_FOR_1 to the new + optionflags argument. + >>> 4 == 4 + 1 + >>> 4 == 4 + True + >>> 4 > 4 + 0 + >>> 4 > 4 + False + """, + + "blank lines": r""" + Blank lines can be marked with : + >>> print 'foo\n\nbar\n' + foo + + bar + + """, + + "ellipsis": r""" + If the ellipsis flag is used, then '...' can be used to + elide substrings in the desired output: + >>> print range(1000) #doctest: +ELLIPSIS + [0, 1, 2, ..., 999] + """, + + "whitespace normalization": r""" + If the whitespace normalization flag is used, then + differences in whitespace are ignored. + >>> print range(30) #doctest: +NORMALIZE_WHITESPACE + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29] + """, + } + +def _test(): + r = unittest.TextTestRunner() + r.run(DocTestSuite()) + +if __name__ == "__main__": + _test() diff --git a/django/test/doctest.py b/django/test/doctest.py deleted file mode 100644 index 3b364f0a75..0000000000 --- a/django/test/doctest.py +++ /dev/null @@ -1,2669 +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 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 = '' -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 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 ('' % - (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 -###################################################################### - -class DocTestParser: - """ - A class used to parse strings containing doctest examples. - """ - # This regular expression is used to find doctest examples in a - # string. It defines three groups: `source` is the source code - # (including leading indentation and prompts); `indent` is the - # indentation of the first (PS1) line of the source code; and - # `want` is the expected output (including leading indentation). - _EXAMPLE_RE = re.compile(r''' - # Source consists of a PS1 line followed by zero or more PS2 lines. - (?P - (?:^(?P [ ]*) >>> .*) # PS1 line - (?:\n [ ]* \.\.\. .*)*) # PS2 lines - \n? - # Want consists of any non-blank lines that do not start with PS1. - (?P (?:(?![ ]*$) # Not a blank line - (?![ ]*>>>) # Not a line starting with PS1 - .*$\n? # But any other line - )*) - ''', re.MULTILINE | re.VERBOSE) - - # A regular expression for handling `want` strings that contain - # expected exceptions. It divides `want` into three pieces: - # - the traceback header line (`hdr`) - # - the traceback stack (`stack`) - # - the exception message (`msg`), as generated by - # traceback.format_exception_only() - # `msg` may have multiple lines. We assume/require that the - # exception message is the first non-indented line starting with a word - # character following the traceback header line. - _EXCEPTION_RE = re.compile(r""" - # Grab the traceback header. Different versions of Python have - # said different things on the first traceback line. - ^(?P Traceback\ \( - (?: most\ recent\ call\ last - | innermost\ last - ) \) : - ) - \s* $ # toss trailing whitespace on the header. - (?P .*?) # don't blink: absorb stuff until... - ^ (?P \w+ .*) # a line *starts* with alphanum. - """, re.VERBOSE | re.MULTILINE | re.DOTALL) - - # A callable returning a true value iff its argument is a blank line - # or contains a single comment. - _IS_BLANK_OR_COMMENT = re.compile(r'^[ ]*(#.*)?$').match - - def parse(self, string, name=''): - """ - Divide the given string into examples and intervening text, - and return them as a list of alternating Examples and strings. - Line numbers for the Examples are 0-based. The optional - argument `name` is a name identifying this string, and is only - used for error messages. - """ - string = string.expandtabs() - # If all lines begin with the same indentation, then strip it. - min_indent = self._min_indent(string) - if min_indent > 0: - string = '\n'.join([l[min_indent:] for l in string.split('\n')]) - - output = [] - charno, lineno = 0, 0 - # Find all doctest examples in the string: - for m in self._EXAMPLE_RE.finditer(string): - # Add the pre-example text to `output`. - output.append(string[charno:m.start()]) - # Update lineno (lines before this example) - lineno += string.count('\n', charno, m.start()) - # Extract info from the regexp match. - (source, options, want, exc_msg) = \ - self._parse_example(m, name, lineno) - # Create an Example, and add it to the list. - if not self._IS_BLANK_OR_COMMENT(source): - output.append( Example(source, want, exc_msg, - lineno=lineno, - indent=min_indent+len(m.group('indent')), - options=options) ) - # Update lineno (lines inside this example) - lineno += string.count('\n', m.start(), m.end()) - # Update charno. - charno = m.end() - # Add any remaining post-example text to `output`. - output.append(string[charno:]) - return output - - def get_doctest(self, string, globs, name, filename, lineno): - """ - Extract all doctest examples from the given string, and - collect them into a `DocTest` object. - - `globs`, `name`, `filename`, and `lineno` are attributes for - the new `DocTest` object. See the documentation for `DocTest` - for more information. - """ - return DocTest(self.get_examples(string, name), globs, - name, filename, lineno, string) - - def get_examples(self, string, name=''): - """ - Extract all doctest examples from the given string, and return - them as a list of `Example` objects. Line numbers are - 0-based, because it's most common in doctests that nothing - interesting appears on the same line as opening triple-quote, - and so the first interesting line is called \"line 1\" then. - - The optional argument `name` is a name identifying this - string, and is only used for error messages. - """ - return [x for x in self.parse(string, name) - if isinstance(x, Example)] - - def _parse_example(self, m, name, lineno): - """ - Given a regular expression match from `_EXAMPLE_RE` (`m`), - return a pair `(source, want)`, where `source` is the matched - example's source code (with prompts and indentation stripped); - and `want` is the example's expected output (with indentation - stripped). - - `name` is the string's name, and `lineno` is the line number - where the example starts; both are used for error messages. - """ - # Get the example's indentation level. - indent = len(m.group('indent')) - - # Divide source into lines; check that they're properly - # indented; and then strip their indentation & prompts. - source_lines = m.group('source').split('\n') - self._check_prompt_blank(source_lines, indent, name, lineno) - self._check_prefix(source_lines[1:], ' '*indent + '.', name, lineno) - source = '\n'.join([sl[indent+4:] for sl in source_lines]) - - # Divide want into lines; check that it's properly indented; and - # then strip the indentation. Spaces before the last newline should - # be preserved, so plain rstrip() isn't good enough. - want = m.group('want') - want_lines = want.split('\n') - if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]): - del want_lines[-1] # forget final newline & spaces after it - self._check_prefix(want_lines, ' '*indent, name, - lineno + len(source_lines)) - want = '\n'.join([wl[indent:] for wl in want_lines]) - - # If `want` contains a traceback message, then extract it. - m = self._EXCEPTION_RE.match(want) - if m: - exc_msg = m.group('msg') - else: - exc_msg = None - - # Extract options from the source. - options = self._find_options(source, name, lineno) - - return source, options, want, exc_msg - - # This regular expression looks for option directives in the - # source code of an example. Option directives are comments - # starting with "doctest:". Warning: this may give false - # positives for string-literals that contain the string - # "#doctest:". Eliminating these false positives would require - # actually parsing the string; but we limit them by ignoring any - # line containing "#doctest:" that is *followed* by a quote mark. - _OPTION_DIRECTIVE_RE = re.compile(r'#\s*doctest:\s*([^\n\'"]*)$', - re.MULTILINE) - - def _find_options(self, source, name, lineno): - """ - Return a dictionary containing option overrides extracted from - option directives in the given source string. - - `name` is the string's name, and `lineno` is the line number - where the example starts; both are used for error messages. - """ - options = {} - # (note: with the current regexp, this will match at most once:) - for m in self._OPTION_DIRECTIVE_RE.finditer(source): - option_strings = m.group(1).replace(',', ' ').split() - for option in option_strings: - if (option[0] not in '+-' or - option[1:] not in OPTIONFLAGS_BY_NAME): - raise ValueError('line %r of the doctest for %s ' - 'has an invalid option: %r' % - (lineno+1, name, option)) - flag = OPTIONFLAGS_BY_NAME[option[1:]] - options[flag] = (option[0] == '+') - if options and self._IS_BLANK_OR_COMMENT(source): - raise ValueError('line %r of the doctest for %s has an option ' - 'directive on a line with no example: %r' % - (lineno, name, source)) - return options - - # This regular expression finds the indentation of every non-blank - # line in a string. - _INDENT_RE = re.compile('^([ ]*)(?=\S)', re.MULTILINE) - - def _min_indent(self, s): - "Return the minimum indentation of any non-blank line in `s`" - indents = [len(indent) for indent in self._INDENT_RE.findall(s)] - if len(indents) > 0: - return min(indents) - else: - return 0 - - def _check_prompt_blank(self, lines, indent, name, lineno): - """ - Given the lines of a source string (including prompts and - leading indentation), check to make sure that every prompt is - followed by a space character. If any line is not followed by - a space character, then raise ValueError. - """ - for i, line in enumerate(lines): - if len(line) >= indent+4 and line[indent+3] != ' ': - raise ValueError('line %r of the docstring for %s ' - 'lacks blank after %s: %r' % - (lineno+i+1, name, - line[indent:indent+3], line)) - - def _check_prefix(self, lines, prefix, name, lineno): - """ - Check that every line in the given list starts with the given - prefix; if any line does not, then raise a ValueError. - """ - for i, line in enumerate(lines): - if line and not line.startswith(prefix): - raise ValueError('line %r of the docstring for %s has ' - 'inconsistent leading whitespace: %r' % - (lineno+i+1, name, line)) - - -###################################################################### -## 4. DocTest Finder -###################################################################### - -class DocTestFinder: - """ - A class used to extract the DocTests that are relevant to a given - object, from its docstring and the docstrings of its contained - objects. Doctests can currently be extracted from the following - object types: modules, functions, classes, methods, staticmethods, - classmethods, and properties. - """ - - def __init__(self, verbose=False, parser=DocTestParser(), - recurse=True, _namefilter=None, exclude_empty=True): - """ - Create a new doctest finder. - - The optional argument `parser` specifies a class or - function that should be used to create new DocTest objects (or - objects that implement the same interface as DocTest). The - signature for this factory function should match the signature - of the DocTest constructor. - - If the optional argument `recurse` is false, then `find` will - only examine the given object, and not any contained objects. - - If the optional argument `exclude_empty` is false, then `find` - will include tests for objects with empty docstrings. - """ - self._parser = parser - self._verbose = verbose - self._recurse = recurse - self._exclude_empty = exclude_empty - # _namefilter is undocumented, and exists only for temporary backward- - # compatibility support of testmod's deprecated isprivate mess. - self._namefilter = _namefilter - - def find(self, obj, name=None, module=None, globs=None, - extraglobs=None): - """ - Return a list of the DocTests that are defined by the given - object's docstring, or by any of its contained objects' - docstrings. - - The optional parameter `module` is the module that contains - the given object. If the module is not specified or is None, then - the test finder will attempt to automatically determine the - correct module. The object's module is used: - - - As a default namespace, if `globs` is not specified. - - To prevent the DocTestFinder from extracting DocTests - from objects that are imported from other modules. - - To find the name of the file containing the object. - - To help find the line number of the object within its - file. - - Contained objects whose module does not match `module` are ignored. - - If `module` is False, no attempt to find the module will be made. - This is obscure, of use mostly in tests: if `module` is False, or - is None but cannot be found automatically, then all objects are - considered to belong to the (non-existent) module, so all contained - objects will (recursively) be searched for doctests. - - The globals for each DocTest is formed by combining `globs` - and `extraglobs` (bindings in `extraglobs` override bindings - in `globs`). A new copy of the globals dictionary is created - for each DocTest. If `globs` is not specified, then it - defaults to the module's `__dict__`, if specified, or {} - otherwise. If `extraglobs` is not specified, then it defaults - to {}. - - """ - # If name was not specified, then extract it from the object. - if name is None: - name = getattr(obj, '__name__', None) - if name is None: - raise ValueError("DocTestFinder.find: name must be given " - "when obj.__name__ doesn't exist: %r" % - (type(obj),)) - - # Find the module that contains the given object (if obj is - # a module, then module=obj.). Note: this may fail, in which - # case module will be None. - if module is False: - module = None - elif module is None: - module = inspect.getmodule(obj) - - # Read the module's source code. This is used by - # DocTestFinder._find_lineno to find the line number for a - # given object's docstring. - try: - file = inspect.getsourcefile(obj) or inspect.getfile(obj) - source_lines = linecache.getlines(file) - if not source_lines: - source_lines = None - except TypeError: - source_lines = None - - # Initialize globals, and merge in extraglobs. - if globs is None: - if module is None: - globs = {} - else: - globs = module.__dict__.copy() - else: - globs = globs.copy() - if extraglobs is not None: - globs.update(extraglobs) - - # Recursively explore `obj`, extracting DocTests. - tests = [] - self._find(tests, obj, name, module, source_lines, globs, {}) - return tests - - def _filter(self, obj, prefix, base): - """ - Return true if the given object should not be examined. - """ - return (self._namefilter is not None and - self._namefilter(prefix, base)) - - def _from_module(self, module, object): - """ - Return true if the given object is defined in the given - module. - """ - if module is None: - return True - elif inspect.isfunction(object): - return module.__dict__ is object.func_globals - elif inspect.isclass(object): - return module.__name__ == object.__module__ - elif inspect.getmodule(object) is not None: - return module is inspect.getmodule(object) - elif hasattr(object, '__module__'): - return module.__name__ == object.__module__ - elif isinstance(object, property): - return True # [XX] no way not be sure. - else: - raise ValueError("object must be a class or function") - - def _find(self, tests, obj, name, module, source_lines, globs, seen): - """ - Find tests for the given object and any contained objects, and - add them to `tests`. - """ - if self._verbose: - print 'Finding tests in %s' % name - - # If we've already processed this object, then ignore it. - if id(obj) in seen: - return - seen[id(obj)] = 1 - - # Find a test for this object, and add it to the list of tests. - test = self._get_test(obj, name, module, globs, source_lines) - if test is not None: - tests.append(test) - - # Look for tests in a module's contained objects. - if inspect.ismodule(obj) and self._recurse: - for valname, val in obj.__dict__.items(): - # Check if this contained object should be ignored. - if self._filter(val, name, valname): - continue - valname = '%s.%s' % (name, valname) - # Recurse to functions & classes. - if ((inspect.isfunction(val) or inspect.isclass(val)) and - self._from_module(module, val)): - self._find(tests, val, valname, module, source_lines, - globs, seen) - - # Look for tests in a module's __test__ dictionary. - if inspect.ismodule(obj) and self._recurse: - for valname, val in getattr(obj, '__test__', {}).items(): - if not isinstance(valname, basestring): - raise ValueError("DocTestFinder.find: __test__ keys " - "must be strings: %r" % - (type(valname),)) - if not (inspect.isfunction(val) or inspect.isclass(val) or - inspect.ismethod(val) or inspect.ismodule(val) or - isinstance(val, basestring)): - raise ValueError("DocTestFinder.find: __test__ values " - "must be strings, functions, methods, " - "classes, or modules: %r" % - (type(val),)) - valname = '%s.__test__.%s' % (name, valname) - self._find(tests, val, valname, module, source_lines, - globs, seen) - - # Look for tests in a class's contained objects. - if inspect.isclass(obj) and self._recurse: - for valname, val in obj.__dict__.items(): - # Check if this contained object should be ignored. - if self._filter(val, name, valname): - continue - # Special handling for staticmethod/classmethod. - if isinstance(val, staticmethod): - val = getattr(obj, valname) - if isinstance(val, classmethod): - val = getattr(obj, valname).im_func - - # Recurse to methods, properties, and nested classes. - if ((inspect.isfunction(val) or inspect.isclass(val) or - isinstance(val, property)) and - self._from_module(module, val)): - valname = '%s.%s' % (name, valname) - self._find(tests, val, valname, module, source_lines, - globs, seen) - - def _get_test(self, obj, name, module, globs, source_lines): - """ - Return a DocTest for the given object, if it defines a docstring; - otherwise, return None. - """ - # Extract the object's docstring. If it doesn't have one, - # then return None (no test for this object). - if isinstance(obj, basestring): - docstring = obj - else: - try: - if obj.__doc__ is None: - docstring = '' - else: - docstring = obj.__doc__ - if not isinstance(docstring, basestring): - docstring = str(docstring) - except (TypeError, AttributeError): - docstring = '' - - # Find the docstring's location in the file. - lineno = self._find_lineno(obj, source_lines) - - # Don't bother if the docstring is empty. - if self._exclude_empty and not docstring: - return None - - # Return a DocTest for this object. - if module is None: - filename = None - else: - filename = getattr(module, '__file__', module.__name__) - if filename[-4:] in (".pyc", ".pyo"): - filename = filename[:-1] - return self._parser.get_doctest(docstring, globs, name, - filename, lineno) - - def _find_lineno(self, obj, source_lines): - """ - Return a line number of the given object's docstring. Note: - this method assumes that the object has a docstring. - """ - lineno = None - - # Find the line number for modules. - if inspect.ismodule(obj): - lineno = 0 - - # Find the line number for classes. - # Note: this could be fooled if a class is defined multiple - # times in a single file. - if inspect.isclass(obj): - if source_lines is None: - return None - pat = re.compile(r'^\s*class\s*%s\b' % - getattr(obj, '__name__', '-')) - for i, line in enumerate(source_lines): - if pat.match(line): - lineno = i - break - - # Find the line number for functions & methods. - if inspect.ismethod(obj): obj = obj.im_func - if inspect.isfunction(obj): obj = obj.func_code - if inspect.istraceback(obj): obj = obj.tb_frame - if inspect.isframe(obj): obj = obj.f_code - if inspect.iscode(obj): - lineno = getattr(obj, 'co_firstlineno', None)-1 - - # Find the line number where the docstring starts. Assume - # that it's the first line that begins with a quote mark. - # Note: this could be fooled by a multiline function - # signature, where a continuation line begins with a quote - # mark. - if lineno is not None: - if source_lines is None: - return lineno+1 - pat = re.compile('(^|.*:)\s*\w*("|\')') - for lineno in range(lineno, len(source_lines)): - if pat.match(source_lines[lineno]): - return lineno - - # We couldn't find the line number. - return None - -###################################################################### -## 5. DocTest Runner -###################################################################### - -class DocTestRunner: - """ - A class used to run DocTest test cases, and accumulate statistics. - The `run` method is used to process a single DocTest case. It - returns a tuple `(f, t)`, where `t` is the number of test cases - tried, and `f` is the number of test cases that failed. - - >>> tests = DocTestFinder().find(_TestClass) - >>> runner = DocTestRunner(verbose=False) - >>> for test in tests: - ... print runner.run(test) - (0, 2) - (0, 1) - (0, 2) - (0, 2) - - The `summarize` method prints a summary of all the test cases that - have been run by the runner, and returns an aggregated `(f, t)` - tuple: - - >>> runner.summarize(verbose=1) - 4 items passed all tests: - 2 tests in _TestClass - 2 tests in _TestClass.__init__ - 2 tests in _TestClass.get - 1 tests in _TestClass.square - 7 tests in 4 items. - 7 passed and 0 failed. - Test passed. - (0, 7) - - The aggregated number of tried examples and failed examples is - also available via the `tries` and `failures` attributes: - - >>> runner.tries - 7 - >>> runner.failures - 0 - - The comparison between expected outputs and actual outputs is done - by an `OutputChecker`. This comparison may be customized with a - number of option flags; see the documentation for `testmod` for - more information. If the option flags are insufficient, then the - comparison may also be customized by passing a subclass of - `OutputChecker` to the constructor. - - The test runner's display output can be controlled in two ways. - First, an output function (`out) can be passed to - `TestRunner.run`; this function will be called with strings that - should be displayed. It defaults to `sys.stdout.write`. If - capturing the output is not sufficient, then the display output - can be also customized by subclassing DocTestRunner, and - overriding the methods `report_start`, `report_success`, - `report_unexpected_exception`, and `report_failure`. - """ - # This divider string is used to separate failure messages, and to - # separate sections of the summary. - DIVIDER = "*" * 70 - - def __init__(self, checker=None, verbose=None, optionflags=0): - """ - Create a new test runner. - - Optional keyword arg `checker` is the `OutputChecker` that - should be used to compare the expected outputs and actual - outputs of doctest examples. - - Optional keyword arg 'verbose' prints lots of stuff if true, - only failures if false; by default, it's true iff '-v' is in - sys.argv. - - Optional argument `optionflags` can be used to control how the - test runner compares expected output to actual output, and how - it displays failures. See the documentation for `testmod` for - more information. - """ - self._checker = checker or OutputChecker() - if verbose is None: - verbose = '-v' in sys.argv - self._verbose = verbose - self.optionflags = optionflags - self.original_optionflags = optionflags - - # Keep track of the examples we've run. - self.tries = 0 - self.failures = 0 - self._name2ft = {} - - # Create a fake output target for capturing doctest output. - self._fakeout = _SpoofOut() - - #///////////////////////////////////////////////////////////////// - # Reporting methods - #///////////////////////////////////////////////////////////////// - - def report_start(self, out, test, example): - """ - Report that the test runner is about to process the given - example. (Only displays a message if verbose=True) - """ - if self._verbose: - if example.want: - out('Trying:\n' + _indent(example.source) + - 'Expecting:\n' + _indent(example.want)) - else: - out('Trying:\n' + _indent(example.source) + - 'Expecting nothing\n') - - def report_success(self, out, test, example, got): - """ - Report that the given example ran successfully. (Only - displays a message if verbose=True) - """ - if self._verbose: - out("ok\n") - - def report_failure(self, out, test, example, got): - """ - Report that the given example failed. - """ - out(self._failure_header(test, example) + - self._checker.output_difference(example, got, self.optionflags)) - - def report_unexpected_exception(self, out, test, example, exc_info): - """ - Report that the given example raised an unexpected exception. - """ - out(self._failure_header(test, example) + - 'Exception raised:\n' + _indent(_exception_traceback(exc_info))) - - def _failure_header(self, test, example): - out = [self.DIVIDER] - if test.filename: - if test.lineno is not None and example.lineno is not None: - lineno = test.lineno + example.lineno + 1 - else: - lineno = '?' - out.append('File "%s", line %s, in %s' % - (test.filename, lineno, test.name)) - else: - out.append('Line %s, in %s' % (example.lineno+1, test.name)) - out.append('Failed example:') - source = example.source - out.append(_indent(source)) - return '\n'.join(out) - - #///////////////////////////////////////////////////////////////// - # DocTest Running - #///////////////////////////////////////////////////////////////// - - def __run(self, test, compileflags, out): - """ - Run the examples in `test`. Write the outcome of each example - with one of the `DocTestRunner.report_*` methods, using the - writer function `out`. `compileflags` is the set of compiler - flags that should be used to execute examples. Return a tuple - `(f, t)`, where `t` is the number of examples tried, and `f` - is the number of examples that failed. The examples are run - in the namespace `test.globs`. - """ - # Keep track of the number of failures and tries. - failures = tries = 0 - - # Save the option flags (since option directives can be used - # to modify them). - original_optionflags = self.optionflags - - SUCCESS, FAILURE, BOOM = range(3) # `outcome` state - - check = self._checker.check_output - - # Process each example. - for examplenum, example in enumerate(test.examples): - - # If REPORT_ONLY_FIRST_FAILURE is set, then suppress - # reporting after the first failure. - quiet = (self.optionflags & REPORT_ONLY_FIRST_FAILURE and - failures > 0) - - # Merge in the example's options. - self.optionflags = original_optionflags - if example.options: - for (optionflag, val) in example.options.items(): - if val: - self.optionflags |= optionflag - else: - self.optionflags &= ~optionflag - - # Record that we started this example. - tries += 1 - if not quiet: - self.report_start(out, test, example) - - # Use a special filename for compile(), so we can retrieve - # the source code during interactive debugging (see - # __patched_linecache_getlines). - filename = '' % (test.name, examplenum) - - # Run the example in the given context (globs), and record - # any exception that gets raised. (But don't intercept - # keyboard interrupts.) - try: - # Don't blink! This is where the user's code gets run. - exec compile(example.source, filename, "single", - compileflags, 1) in test.globs - self.debugger.set_continue() # ==== Example Finished ==== - exception = None - except KeyboardInterrupt: - raise - except: - exception = sys.exc_info() - self.debugger.set_continue() # ==== Example Finished ==== - - got = self._fakeout.getvalue() # the actual output - self._fakeout.truncate(0) - outcome = FAILURE # guilty until proved innocent or insane - - # If the example executed without raising any exceptions, - # verify its output. - if exception is None: - if check(example.want, got, self.optionflags): - outcome = SUCCESS - - # The example raised an exception: check if it was expected. - else: - exc_info = sys.exc_info() - exc_msg = traceback.format_exception_only(*exc_info[:2])[-1] - if not quiet: - got += _exception_traceback(exc_info) - - # If `example.exc_msg` is None, then we weren't expecting - # an exception. - if example.exc_msg is None: - outcome = BOOM - - # We expected an exception: see whether it matches. - elif check(example.exc_msg, exc_msg, self.optionflags): - outcome = SUCCESS - - # Another chance if they didn't care about the detail. - elif self.optionflags & IGNORE_EXCEPTION_DETAIL: - m1 = re.match(r'[^:]*:', example.exc_msg) - m2 = re.match(r'[^:]*:', exc_msg) - if m1 and m2 and check(m1.group(0), m2.group(0), - self.optionflags): - outcome = SUCCESS - - # Report the outcome. - if outcome is SUCCESS: - if not quiet: - self.report_success(out, test, example, got) - elif outcome is FAILURE: - if not quiet: - self.report_failure(out, test, example, got) - failures += 1 - elif outcome is BOOM: - if not quiet: - self.report_unexpected_exception(out, test, example, - exc_info) - failures += 1 - else: - assert False, ("unknown outcome", outcome) - - # Restore the option flags (in case they were modified) - self.optionflags = original_optionflags - - # Record and return the number of failures and tries. - self.__record_outcome(test, failures, tries) - return failures, tries - - def __record_outcome(self, test, f, t): - """ - Record the fact that the given DocTest (`test`) generated `f` - failures out of `t` tried examples. - """ - f2, t2 = self._name2ft.get(test.name, (0,0)) - self._name2ft[test.name] = (f+f2, t+t2) - self.failures += f - self.tries += t - - __LINECACHE_FILENAME_RE = re.compile(r'[\w\.]+)' - r'\[(?P\d+)\]>$') - def __patched_linecache_getlines(self, filename, module_globals=None): - m = self.__LINECACHE_FILENAME_RE.match(filename) - if m and m.group('name') == self.test.name: - example = self.test.examples[int(m.group('examplenum'))] - return example.source.splitlines(True) - else: - if sys.version_info < (2, 5, 0): - return self.save_linecache_getlines(filename) - else: - return self.save_linecache_getlines(filename, module_globals) - - def run(self, test, compileflags=None, out=None, clear_globs=True): - """ - Run the examples in `test`, and display the results using the - writer function `out`. - - The examples are run in the namespace `test.globs`. If - `clear_globs` is true (the default), then this namespace will - be cleared after the test runs, to help with garbage - collection. If you would like to examine the namespace after - the test completes, then use `clear_globs=False`. - - `compileflags` gives the set of flags that should be used by - the Python compiler when running the examples. If not - specified, then it will default to the set of future-import - flags that apply to `globs`. - - The output of each example is checked using - `DocTestRunner.check_output`, and the results are formatted by - the `DocTestRunner.report_*` methods. - """ - self.test = test - - if compileflags is None: - compileflags = _extract_future_flags(test.globs) - - save_stdout = sys.stdout - if out is None: - out = save_stdout.write - sys.stdout = self._fakeout - - # Patch pdb.set_trace to restore sys.stdout during interactive - # debugging (so it's not still redirected to self._fakeout). - # Note that the interactive output will go to *our* - # save_stdout, even if that's not the real sys.stdout; this - # allows us to write test cases for the set_trace behavior. - save_set_trace = pdb.set_trace - self.debugger = _OutputRedirectingPdb(save_stdout) - self.debugger.reset() - pdb.set_trace = self.debugger.set_trace - - # Patch linecache.getlines, so we can see the example's source - # when we're inside the debugger. - self.save_linecache_getlines = linecache.getlines - linecache.getlines = self.__patched_linecache_getlines - - try: - return self.__run(test, compileflags, out) - finally: - sys.stdout = save_stdout - pdb.set_trace = save_set_trace - linecache.getlines = self.save_linecache_getlines - if clear_globs: - test.globs.clear() - - #///////////////////////////////////////////////////////////////// - # Summarization - #///////////////////////////////////////////////////////////////// - def summarize(self, verbose=None): - """ - Print a summary of all the test cases that have been run by - this DocTestRunner, and return a tuple `(f, t)`, where `f` is - the total number of failed examples, and `t` is the total - number of tried examples. - - The optional `verbose` argument controls how detailed the - summary is. If the verbosity is not specified, then the - DocTestRunner's verbosity is used. - """ - if verbose is None: - verbose = self._verbose - notests = [] - passed = [] - failed = [] - totalt = totalf = 0 - for x in self._name2ft.items(): - name, (f, t) = x - assert f <= t - totalt += t - totalf += f - if t == 0: - notests.append(name) - elif f == 0: - passed.append( (name, t) ) - else: - failed.append(x) - if verbose: - if notests: - print len(notests), "items had no tests:" - notests.sort() - for thing in notests: - print " ", thing - if passed: - print len(passed), "items passed all tests:" - passed.sort() - for thing, count in passed: - print " %3d tests in %s" % (count, thing) - if failed: - print self.DIVIDER - print len(failed), "items had failures:" - failed.sort() - for thing, (f, t) in failed: - print " %3d of %3d in %s" % (f, t, thing) - if verbose: - print totalt, "tests in", len(self._name2ft), "items." - print totalt - totalf, "passed and", totalf, "failed." - if totalf: - print "***Test Failed***", totalf, "failures." - elif verbose: - print "Test passed." - return totalf, totalt - - #///////////////////////////////////////////////////////////////// - # Backward compatibility cruft to maintain doctest.master. - #///////////////////////////////////////////////////////////////// - def merge(self, other): - d = self._name2ft - for name, (f, t) in other._name2ft.items(): - if name in d: - print "*** DocTestRunner.merge: '" + name + "' in both" \ - " testers; summing outcomes." - f2, t2 = d[name] - f = f + f2 - t = t + t2 - d[name] = f, t - -class OutputChecker: - """ - A class used to check the whether the actual output from a doctest - example matches the expected output. `OutputChecker` defines two - methods: `check_output`, which compares a given pair of outputs, - and returns true if they match; and `output_difference`, which - returns a string describing the differences between two outputs. - """ - def check_output(self, want, got, optionflags): - """ - Return True iff the actual output from an example (`got`) - matches the expected output (`want`). These strings are - always considered to match if they are identical; but - depending on what option flags the test runner is using, - several non-exact match types are also possible. See the - documentation for `TestRunner` for more information about - option flags. - """ - # Handle the common case first, for efficiency: - # if they're string-identical, always return true. - if got == want: - return True - - # The values True and False replaced 1 and 0 as the return - # value for boolean comparisons in Python 2.3. - if not (optionflags & DONT_ACCEPT_TRUE_FOR_1): - if (got,want) == ("True\n", "1\n"): - return True - if (got,want) == ("False\n", "0\n"): - return True - - # can be used as a special sequence to signify a - # blank line, unless the DONT_ACCEPT_BLANKLINE flag is used. - if not (optionflags & DONT_ACCEPT_BLANKLINE): - # Replace in want with a blank line. - want = re.sub('(?m)^%s\s*?$' % re.escape(BLANKLINE_MARKER), - '', want) - # If a line in got contains only spaces, then remove the - # spaces. - got = re.sub('(?m)^\s*?$', '', got) - if got == want: - return True - - # This flag causes doctest to ignore any differences in the - # contents of whitespace strings. Note that this can be used - # in conjunction with the ELLIPSIS flag. - if optionflags & NORMALIZE_WHITESPACE: - got = ' '.join(got.split()) - want = ' '.join(want.split()) - if got == want: - return True - - # The ELLIPSIS flag says to let the sequence "..." in `want` - # match any substring in `got`. - if optionflags & ELLIPSIS: - if _ellipsis_match(want, got): - return True - - # We didn't find any match; return false. - return False - - # Should we do a fancy diff? - def _do_a_fancy_diff(self, want, got, optionflags): - # Not unless they asked for a fancy diff. - if not optionflags & (REPORT_UDIFF | - REPORT_CDIFF | - REPORT_NDIFF): - return False - - # If expected output uses ellipsis, a meaningful fancy diff is - # too hard ... or maybe not. In two real-life failures Tim saw, - # a diff was a major help anyway, so this is commented out. - # [todo] _ellipsis_match() knows which pieces do and don't match, - # and could be the basis for a kick-ass diff in this case. - ##if optionflags & ELLIPSIS and ELLIPSIS_MARKER in want: - ## return False - - # ndiff does intraline difference marking, so can be useful even - # for 1-line differences. - if optionflags & REPORT_NDIFF: - return True - - # The other diff types need at least a few lines to be helpful. - return want.count('\n') > 2 and got.count('\n') > 2 - - def output_difference(self, example, got, optionflags): - """ - Return a string describing the differences between the - expected output for a given example (`example`) and the actual - output (`got`). `optionflags` is the set of option flags used - to compare `want` and `got`. - """ - want = example.want - # If s are being used, then replace blank lines - # with in the actual output string. - if not (optionflags & DONT_ACCEPT_BLANKLINE): - got = re.sub('(?m)^[ ]*(?=\n)', BLANKLINE_MARKER, got) - - # Check if we should use diff. - if self._do_a_fancy_diff(want, got, optionflags): - # Split want & got into lines. - want_lines = want.splitlines(True) # True == keep line ends - got_lines = got.splitlines(True) - # Use difflib to find their differences. - if optionflags & REPORT_UDIFF: - diff = difflib.unified_diff(want_lines, got_lines, n=2) - diff = list(diff)[2:] # strip the diff header - kind = 'unified diff with -expected +actual' - elif optionflags & REPORT_CDIFF: - diff = difflib.context_diff(want_lines, got_lines, n=2) - diff = list(diff)[2:] # strip the diff header - kind = 'context diff with expected followed by actual' - elif optionflags & REPORT_NDIFF: - engine = difflib.Differ(charjunk=difflib.IS_CHARACTER_JUNK) - diff = list(engine.compare(want_lines, got_lines)) - kind = 'ndiff with -expected +actual' - else: - assert 0, 'Bad diff option' - # Remove trailing whitespace on diff output. - diff = [line.rstrip() + '\n' for line in diff] - return 'Differences (%s):\n' % kind + _indent(''.join(diff)) - - # If we're not using diff, then simply list the expected - # output followed by the actual output. - if want and got: - return 'Expected:\n%sGot:\n%s' % (_indent(want), _indent(got)) - elif want: - return 'Expected:\n%sGot nothing\n' % _indent(want) - elif got: - return 'Expected nothing\nGot:\n%s' % _indent(got) - else: - return 'Expected nothing\nGot nothing\n' - -class DocTestFailure(Exception): - """A DocTest example has failed in debugging mode. - - The exception instance has variables: - - - test: the DocTest object being run - - - excample: the Example object that failed - - - got: the actual output - """ - def __init__(self, test, example, got): - self.test = test - self.example = example - self.got = got - - def __str__(self): - return str(self.test) - -class UnexpectedException(Exception): - """A DocTest example has encountered an unexpected exception - - The exception instance has variables: - - - test: the DocTest object being run - - - excample: the Example object that failed - - - exc_info: the exception info - """ - def __init__(self, test, example, exc_info): - self.test = test - self.example = example - self.exc_info = exc_info - - def __str__(self): - return str(self.test) - -class DebugRunner(DocTestRunner): - r"""Run doc tests but raise an exception as soon as there is a failure. - - If an unexpected exception occurs, an UnexpectedException is raised. - It contains the test, the example, and the original exception: - - >>> runner = DebugRunner(verbose=False) - >>> test = DocTestParser().get_doctest('>>> raise KeyError\n42', - ... {}, 'foo', 'foo.py', 0) - >>> try: - ... runner.run(test) - ... except UnexpectedException, failure: - ... pass - - >>> failure.test is test - True - - >>> failure.example.want - '42\n' - - >>> exc_info = failure.exc_info - >>> raise exc_info[0], exc_info[1], exc_info[2] - Traceback (most recent call last): - ... - KeyError - - We wrap the original exception to give the calling application - access to the test and example information. - - If the output doesn't match, then a DocTestFailure is raised: - - >>> test = DocTestParser().get_doctest(''' - ... >>> x = 1 - ... >>> x - ... 2 - ... ''', {}, 'foo', 'foo.py', 0) - - >>> try: - ... runner.run(test) - ... except DocTestFailure, failure: - ... pass - - DocTestFailure objects provide access to the test: - - >>> failure.test is test - True - - As well as to the example: - - >>> failure.example.want - '2\n' - - and the actual output: - - >>> failure.got - '1\n' - - If a failure or error occurs, the globals are left intact: - - >>> del test.globs['__builtins__'] - >>> test.globs - {'x': 1} - - >>> test = DocTestParser().get_doctest(''' - ... >>> x = 2 - ... >>> raise KeyError - ... ''', {}, 'foo', 'foo.py', 0) - - >>> runner.run(test) - Traceback (most recent call last): - ... - UnexpectedException: - - >>> del test.globs['__builtins__'] - >>> test.globs - {'x': 2} - - But the globals are cleared if there is no error: - - >>> test = DocTestParser().get_doctest(''' - ... >>> x = 2 - ... ''', {}, 'foo', 'foo.py', 0) - - >>> runner.run(test) - (0, 1) - - >>> test.globs - {} - - """ - - def run(self, test, compileflags=None, out=None, clear_globs=True): - r = DocTestRunner.run(self, test, compileflags, out, False) - if clear_globs: - test.globs.clear() - return r - - def report_unexpected_exception(self, out, test, example, exc_info): - raise UnexpectedException(test, example, exc_info) - - def report_failure(self, out, test, example, got): - raise DocTestFailure(test, example, got) - -###################################################################### -## 6. Test Functions -###################################################################### -# These should be backwards compatible. - -# For backward compatibility, a global instance of a DocTestRunner -# class, updated by testmod. -master = None - -def testmod(m=None, name=None, globs=None, verbose=None, isprivate=None, - report=True, optionflags=0, extraglobs=None, - raise_on_error=False, exclude_empty=False): - """m=None, name=None, globs=None, verbose=None, isprivate=None, - report=True, optionflags=0, extraglobs=None, raise_on_error=False, - exclude_empty=False - - Test examples in docstrings in functions and classes reachable - from module m (or the current module if m is not supplied), starting - with m.__doc__. Unless isprivate is specified, private names - are not skipped. - - Also test examples reachable from dict m.__test__ if it exists and is - not None. m.__test__ maps names to functions, classes and strings; - function and class docstrings are tested even if the name is private; - strings are tested directly, as if they were docstrings. - - Return (#failures, #tests). - - See doctest.__doc__ for an overview. - - Optional keyword arg "name" gives the name of the module; by default - use m.__name__. - - Optional keyword arg "globs" gives a dict to be used as the globals - when executing examples; by default, use m.__dict__. A copy of this - dict is actually used for each docstring, so that each docstring's - examples start with a clean slate. - - Optional keyword arg "extraglobs" gives a dictionary that should be - merged into the globals that are used to execute examples. By - default, no extra globals are used. This is new in 2.4. - - Optional keyword arg "verbose" prints lots of stuff if true, prints - only failures if false; by default, it's true iff "-v" is in sys.argv. - - Optional keyword arg "report" prints a summary at the end when true, - else prints nothing at the end. In verbose mode, the summary is - detailed, else very brief (in fact, empty if all tests passed). - - Optional keyword arg "optionflags" or's together module constants, - and defaults to 0. This is new in 2.3. Possible values (see the - docs for details): - - DONT_ACCEPT_TRUE_FOR_1 - DONT_ACCEPT_BLANKLINE - NORMALIZE_WHITESPACE - ELLIPSIS - IGNORE_EXCEPTION_DETAIL - REPORT_UDIFF - REPORT_CDIFF - REPORT_NDIFF - REPORT_ONLY_FIRST_FAILURE - - Optional keyword arg "raise_on_error" raises an exception on the - first unexpected exception or failure. This allows failures to be - post-mortem debugged. - - Deprecated in Python 2.4: - Optional keyword arg "isprivate" specifies a function used to - determine whether a name is private. The default function is - treat all functions as public. Optionally, "isprivate" can be - set to doctest.is_private to skip over functions marked as private - using the underscore naming convention; see its docs for details. - - Advanced tomfoolery: testmod runs methods of a local instance of - class doctest.Tester, then merges the results into (or creates) - global Tester instance doctest.master. Methods of doctest.master - can be called directly too, if you want to do something unusual. - Passing report=0 to testmod is especially useful then, to delay - displaying a summary. Invoke doctest.master.summarize(verbose) - when you're done fiddling. - """ - global master - - if isprivate is not None: - warnings.warn("the isprivate argument is deprecated; " - "examine DocTestFinder.find() lists instead", - DeprecationWarning) - - # If no module was given, then use __main__. - if m is None: - # DWA - m will still be None if this wasn't invoked from the command - # line, in which case the following TypeError is about as good an error - # as we should expect - m = sys.modules.get('__main__') - - # Check that we were actually given a module. - if not inspect.ismodule(m): - raise TypeError("testmod: module required; %r" % (m,)) - - # If no name was given, then use the module's name. - if name is None: - name = m.__name__ - - # Find, parse, and run all tests in the given module. - finder = DocTestFinder(_namefilter=isprivate, exclude_empty=exclude_empty) - - if raise_on_error: - runner = DebugRunner(verbose=verbose, optionflags=optionflags) - else: - runner = DocTestRunner(verbose=verbose, optionflags=optionflags) - - for test in finder.find(m, name, globs=globs, extraglobs=extraglobs): - runner.run(test) - - if report: - runner.summarize() - - if master is None: - master = runner - else: - master.merge(runner) - - return runner.failures, runner.tries - -def testfile(filename, module_relative=True, name=None, package=None, - globs=None, verbose=None, report=True, optionflags=0, - extraglobs=None, raise_on_error=False, parser=DocTestParser()): - """ - Test examples in the given file. Return (#failures, #tests). - - Optional keyword arg "module_relative" specifies how filenames - should be interpreted: - - - If "module_relative" is True (the default), then "filename" - specifies a module-relative path. By default, this path is - relative to the calling module's directory; but if the - "package" argument is specified, then it is relative to that - package. To ensure os-independence, "filename" should use - "/" characters to separate path segments, and should not - be an absolute path (i.e., it may not begin with "/"). - - - If "module_relative" is False, then "filename" specifies an - os-specific path. The path may be absolute or relative (to - the current working directory). - - Optional keyword arg "name" gives the name of the test; by default - use the file's basename. - - Optional keyword argument "package" is a Python package or the - name of a Python package whose directory should be used as the - base directory for a module relative filename. If no package is - specified, then the calling module's directory is used as the base - directory for module relative filenames. It is an error to - specify "package" if "module_relative" is False. - - Optional keyword arg "globs" gives a dict to be used as the globals - when executing examples; by default, use {}. A copy of this dict - is actually used for each docstring, so that each docstring's - examples start with a clean slate. - - Optional keyword arg "extraglobs" gives a dictionary that should be - merged into the globals that are used to execute examples. By - default, no extra globals are used. - - Optional keyword arg "verbose" prints lots of stuff if true, prints - only failures if false; by default, it's true iff "-v" is in sys.argv. - - Optional keyword arg "report" prints a summary at the end when true, - else prints nothing at the end. In verbose mode, the summary is - detailed, else very brief (in fact, empty if all tests passed). - - Optional keyword arg "optionflags" or's together module constants, - and defaults to 0. Possible values (see the docs for details): - - DONT_ACCEPT_TRUE_FOR_1 - DONT_ACCEPT_BLANKLINE - NORMALIZE_WHITESPACE - ELLIPSIS - IGNORE_EXCEPTION_DETAIL - REPORT_UDIFF - REPORT_CDIFF - REPORT_NDIFF - REPORT_ONLY_FIRST_FAILURE - - Optional keyword arg "raise_on_error" raises an exception on the - first unexpected exception or failure. This allows failures to be - post-mortem debugged. - - Optional keyword arg "parser" specifies a DocTestParser (or - subclass) that should be used to extract tests from the files. - - Advanced tomfoolery: testmod runs methods of a local instance of - class doctest.Tester, then merges the results into (or creates) - global Tester instance doctest.master. Methods of doctest.master - can be called directly too, if you want to do something unusual. - Passing report=0 to testmod is especially useful then, to delay - displaying a summary. Invoke doctest.master.summarize(verbose) - when you're done fiddling. - """ - global master - - if package and not module_relative: - raise ValueError("Package may only be specified for module-" - "relative paths.") - - # Relativize the path - if module_relative: - package = _normalize_module(package) - filename = _module_relative_path(package, filename) - - # If no name was given, then use the file's name. - if name is None: - name = os.path.basename(filename) - - # Assemble the globals. - if globs is None: - globs = {} - else: - globs = globs.copy() - if extraglobs is not None: - globs.update(extraglobs) - - if raise_on_error: - runner = DebugRunner(verbose=verbose, optionflags=optionflags) - else: - runner = DocTestRunner(verbose=verbose, optionflags=optionflags) - - # Read the file, convert it to a test, and run it. - s = open(filename).read() - test = parser.get_doctest(s, globs, name, filename, 0) - runner.run(test) - - if report: - runner.summarize() - - if master is None: - master = runner - else: - master.merge(runner) - - return runner.failures, runner.tries - -def run_docstring_examples(f, globs, verbose=False, name="NoName", - compileflags=None, optionflags=0): - """ - Test examples in the given object's docstring (`f`), using `globs` - as globals. Optional argument `name` is used in failure messages. - If the optional argument `verbose` is true, then generate output - even if there are no failures. - - `compileflags` gives the set of flags that should be used by the - Python compiler when running the examples. If not specified, then - it will default to the set of future-import flags that apply to - `globs`. - - Optional keyword arg `optionflags` specifies options for the - testing and output. See the documentation for `testmod` for more - information. - """ - # Find, parse, and run all tests in the given module. - finder = DocTestFinder(verbose=verbose, recurse=False) - runner = DocTestRunner(verbose=verbose, optionflags=optionflags) - for test in finder.find(f, name, globs=globs): - runner.run(test, compileflags=compileflags) - -###################################################################### -## 7. Tester -###################################################################### -# This is provided only for backwards compatibility. It's not -# actually used in any way. - -class Tester: - def __init__(self, mod=None, globs=None, verbose=None, - isprivate=None, optionflags=0): - - warnings.warn("class Tester is deprecated; " - "use class doctest.DocTestRunner instead", - DeprecationWarning, stacklevel=2) - if mod is None and globs is None: - raise TypeError("Tester.__init__: must specify mod or globs") - if mod is not None and not inspect.ismodule(mod): - raise TypeError("Tester.__init__: mod must be a module; %r" % - (mod,)) - if globs is None: - globs = mod.__dict__ - self.globs = globs - - self.verbose = verbose - self.isprivate = isprivate - self.optionflags = optionflags - self.testfinder = DocTestFinder(_namefilter=isprivate) - self.testrunner = DocTestRunner(verbose=verbose, - optionflags=optionflags) - - def runstring(self, s, name): - test = DocTestParser().get_doctest(s, self.globs, name, None, None) - if self.verbose: - print "Running string", name - (f,t) = self.testrunner.run(test) - if self.verbose: - print f, "of", t, "examples failed in string", name - return (f,t) - - def rundoc(self, object, name=None, module=None): - f = t = 0 - tests = self.testfinder.find(object, name, module=module, - globs=self.globs) - for test in tests: - (f2, t2) = self.testrunner.run(test) - (f,t) = (f+f2, t+t2) - return (f,t) - - def rundict(self, d, name, module=None): - import new - m = new.module(name) - m.__dict__.update(d) - if module is None: - module = False - return self.rundoc(m, name, module) - - def run__test__(self, d, name): - import new - m = new.module(name) - m.__test__ = d - return self.rundoc(m, name) - - def summarize(self, verbose=None): - return self.testrunner.summarize(verbose) - - def merge(self, other): - self.testrunner.merge(other.testrunner) - -###################################################################### -## 8. Unittest Support -###################################################################### - -_unittest_reportflags = 0 - -def set_unittest_reportflags(flags): - """Sets the unittest option flags. - - The old flag is returned so that a runner could restore the old - value if it wished to: - - >>> old = _unittest_reportflags - >>> set_unittest_reportflags(REPORT_NDIFF | - ... REPORT_ONLY_FIRST_FAILURE) == old - True - - >>> import doctest - >>> doctest._unittest_reportflags == (REPORT_NDIFF | - ... REPORT_ONLY_FIRST_FAILURE) - True - - Only reporting flags can be set: - - >>> set_unittest_reportflags(ELLIPSIS) - Traceback (most recent call last): - ... - ValueError: ('Only reporting flags allowed', 8) - - >>> set_unittest_reportflags(old) == (REPORT_NDIFF | - ... REPORT_ONLY_FIRST_FAILURE) - True - """ - global _unittest_reportflags - - if (flags & REPORTING_FLAGS) != flags: - raise ValueError("Only reporting flags allowed", flags) - old = _unittest_reportflags - _unittest_reportflags = flags - return old - - -class DocTestCase(unittest.TestCase): - - def __init__(self, test, optionflags=0, setUp=None, tearDown=None, - checker=None, runner=DocTestRunner): - - unittest.TestCase.__init__(self) - self._dt_optionflags = optionflags - self._dt_checker = checker - self._dt_test = test - self._dt_setUp = setUp - self._dt_tearDown = tearDown - self._dt_runner = runner - - def setUp(self): - test = self._dt_test - - if self._dt_setUp is not None: - self._dt_setUp(test) - - def tearDown(self): - test = self._dt_test - - if self._dt_tearDown is not None: - self._dt_tearDown(test) - - test.globs.clear() - - def runTest(self): - test = self._dt_test - old = sys.stdout - new = StringIO() - optionflags = self._dt_optionflags - - if not (optionflags & REPORTING_FLAGS): - # The option flags don't include any reporting flags, - # so add the default reporting flags - optionflags |= _unittest_reportflags - - runner = self._dt_runner(optionflags=optionflags, - checker=self._dt_checker, verbose=False) - - try: - runner.DIVIDER = "-"*70 - failures, tries = runner.run( - test, out=new.write, clear_globs=False) - finally: - sys.stdout = old - - if failures: - raise self.failureException(self.format_failure(new.getvalue())) - - def format_failure(self, err): - test = self._dt_test - if test.lineno is None: - lineno = 'unknown line number' - else: - lineno = '%s' % test.lineno - lname = '.'.join(test.name.split('.')[-1:]) - return ('Failed doctest test for %s\n' - ' File "%s", line %s, in %s\n\n%s' - % (test.name, test.filename, lineno, lname, err) - ) - - def debug(self): - r"""Run the test case without results and without catching exceptions - - The unit test framework includes a debug method on test cases - and test suites to support post-mortem debugging. The test code - is run in such a way that errors are not caught. This way a - caller can catch the errors and initiate post-mortem debugging. - - The DocTestCase provides a debug method that raises - UnexpectedException errors if there is an unexepcted - exception: - - >>> test = DocTestParser().get_doctest('>>> raise KeyError\n42', - ... {}, 'foo', 'foo.py', 0) - >>> case = DocTestCase(test) - >>> try: - ... case.debug() - ... except UnexpectedException, failure: - ... pass - - The UnexpectedException contains the test, the example, and - the original exception: - - >>> failure.test is test - True - - >>> failure.example.want - '42\n' - - >>> exc_info = failure.exc_info - >>> raise exc_info[0], exc_info[1], exc_info[2] - Traceback (most recent call last): - ... - KeyError - - If the output doesn't match, then a DocTestFailure is raised: - - >>> test = DocTestParser().get_doctest(''' - ... >>> x = 1 - ... >>> x - ... 2 - ... ''', {}, 'foo', 'foo.py', 0) - >>> case = DocTestCase(test) - - >>> try: - ... case.debug() - ... except DocTestFailure, failure: - ... pass - - DocTestFailure objects provide access to the test: - - >>> failure.test is test - True - - As well as to the example: - - >>> failure.example.want - '2\n' - - and the actual output: - - >>> failure.got - '1\n' - - """ - - self.setUp() - runner = DebugRunner(optionflags=self._dt_optionflags, - checker=self._dt_checker, verbose=False) - runner.run(self._dt_test) - self.tearDown() - - def id(self): - return self._dt_test.name - - def __repr__(self): - name = self._dt_test.name.split('.') - return "%s (%s)" % (name[-1], '.'.join(name[:-1])) - - __str__ = __repr__ - - def shortDescription(self): - return "Doctest: " + self._dt_test.name - -def DocTestSuite(module=None, globs=None, extraglobs=None, test_finder=None, - test_class=DocTestCase, **options): - """ - Convert doctest tests for a module to a unittest test suite. - - This converts each documentation string in a module that - contains doctest tests to a unittest test case. If any of the - tests in a doc string fail, then the test case fails. An exception - is raised showing the name of the file containing the test and a - (sometimes approximate) line number. - - The `module` argument provides the module to be tested. The argument - can be either a module or a module name. - - If no argument is given, the calling module is used. - - A number of options may be provided as keyword arguments: - - setUp - A set-up function. This is called before running the - tests in each file. The setUp function will be passed a DocTest - object. The setUp function can access the test globals as the - globs attribute of the test passed. - - tearDown - A tear-down function. This is called after running the - tests in each file. The tearDown function will be passed a DocTest - object. The tearDown function can access the test globals as the - globs attribute of the test passed. - - globs - A dictionary containing initial global variables for the tests. - - optionflags - A set of doctest option flags expressed as an integer. - """ - - if test_finder is None: - test_finder = DocTestFinder() - - module = _normalize_module(module) - tests = test_finder.find(module, globs=globs, extraglobs=extraglobs) - if globs is None: - globs = module.__dict__ - if not tests: - # Why do we want to do this? Because it reveals a bug that might - # otherwise be hidden. - raise ValueError(module, "has no tests") - - tests.sort() - suite = unittest.TestSuite() - for test in tests: - if len(test.examples) == 0: - continue - if not test.filename: - filename = module.__file__ - if filename[-4:] in (".pyc", ".pyo"): - filename = filename[:-1] - test.filename = filename - suite.addTest(test_class(test, **options)) - - return suite - -class DocFileCase(DocTestCase): - - def id(self): - return '_'.join(self._dt_test.name.split('.')) - - def __repr__(self): - return self._dt_test.filename - __str__ = __repr__ - - def format_failure(self, err): - return ('Failed doctest test for %s\n File "%s", line 0\n\n%s' - % (self._dt_test.name, self._dt_test.filename, err) - ) - -def DocFileTest(path, module_relative=True, package=None, - globs=None, parser=DocTestParser(), **options): - if globs is None: - globs = {} - - if package and not module_relative: - raise ValueError("Package may only be specified for module-" - "relative paths.") - - # Relativize the path. - if module_relative: - package = _normalize_module(package) - path = _module_relative_path(package, path) - - # Find the file and read it. - name = os.path.basename(path) - doc = open(path).read() - - # Convert it to a test, and wrap it in a DocFileCase. - test = parser.get_doctest(doc, globs, name, path, 0) - return DocFileCase(test, **options) - -def DocFileSuite(*paths, **kw): - """A unittest suite for one or more doctest files. - - The path to each doctest file is given as a string; the - interpretation of that string depends on the keyword argument - "module_relative". - - A number of options may be provided as keyword arguments: - - module_relative - If "module_relative" is True, then the given file paths are - interpreted as os-independent module-relative paths. By - default, these paths are relative to the calling module's - directory; but if the "package" argument is specified, then - they are relative to that package. To ensure os-independence, - "filename" should use "/" characters to separate path - segments, and may not be an absolute path (i.e., it may not - begin with "/"). - - If "module_relative" is False, then the given file paths are - interpreted as os-specific paths. These paths may be absolute - or relative (to the current working directory). - - package - A Python package or the name of a Python package whose directory - should be used as the base directory for module relative paths. - If "package" is not specified, then the calling module's - directory is used as the base directory for module relative - filenames. It is an error to specify "package" if - "module_relative" is False. - - setUp - A set-up function. This is called before running the - tests in each file. The setUp function will be passed a DocTest - object. The setUp function can access the test globals as the - globs attribute of the test passed. - - tearDown - A tear-down function. This is called after running the - tests in each file. The tearDown function will be passed a DocTest - object. The tearDown function can access the test globals as the - globs attribute of the test passed. - - globs - A dictionary containing initial global variables for the tests. - - optionflags - A set of doctest option flags expressed as an integer. - - parser - A DocTestParser (or subclass) that should be used to extract - tests from the files. - """ - suite = unittest.TestSuite() - - # We do this here so that _normalize_module is called at the right - # level. If it were called in DocFileTest, then this function - # would be the caller and we might guess the package incorrectly. - if kw.get('module_relative', True): - kw['package'] = _normalize_module(kw.get('package')) - - for path in paths: - suite.addTest(DocFileTest(path, **kw)) - - return suite - -###################################################################### -## 9. Debugging Support -###################################################################### - -def script_from_examples(s): - r"""Extract script from text with examples. - - Converts text with examples to a Python script. Example input is - converted to regular code. Example output and all other words - are converted to comments: - - >>> text = ''' - ... Here are examples of simple math. - ... - ... Python has super accurate integer addition - ... - ... >>> 2 + 2 - ... 5 - ... - ... And very friendly error messages: - ... - ... >>> 1/0 - ... To Infinity - ... And - ... Beyond - ... - ... You can use logic if you want: - ... - ... >>> if 0: - ... ... blah - ... ... blah - ... ... - ... - ... Ho hum - ... ''' - - >>> print script_from_examples(text) - # Here are examples of simple math. - # - # Python has super accurate integer addition - # - 2 + 2 - # Expected: - ## 5 - # - # And very friendly error messages: - # - 1/0 - # Expected: - ## To Infinity - ## And - ## Beyond - # - # You can use logic if you want: - # - if 0: - blah - blah - # - # Ho hum - """ - output = [] - for piece in DocTestParser().parse(s): - if isinstance(piece, Example): - # Add the example's source code (strip trailing NL) - output.append(piece.source[:-1]) - # Add the expected output: - want = piece.want - if want: - output.append('# Expected:') - output += ['## '+l for l in want.split('\n')[:-1]] - else: - # Add non-example text. - output += [_comment_line(l) - for l in piece.split('\n')[:-1]] - - # Trim junk on both ends. - while output and output[-1] == '#': - output.pop() - while output and output[0] == '#': - output.pop(0) - # Combine the output, and return it. - return '\n'.join(output) - -def testsource(module, name): - """Extract the test sources from a doctest docstring as a script. - - Provide the module (or dotted name of the module) containing the - test to be debugged and the name (within the module) of the object - with the doc string with tests to be debugged. - """ - module = _normalize_module(module) - tests = DocTestFinder().find(module) - test = [t for t in tests if t.name == name] - if not test: - raise ValueError(name, "not found in tests") - test = test[0] - testsrc = script_from_examples(test.docstring) - return testsrc - -def debug_src(src, pm=False, globs=None): - """Debug a single doctest docstring, in argument `src`'""" - testsrc = script_from_examples(src) - debug_script(testsrc, pm, globs) - -def debug_script(src, pm=False, globs=None): - "Debug a test script. `src` is the script, as a string." - import pdb - - # Note that tempfile.NameTemporaryFile() cannot be used. As the - # docs say, a file so created cannot be opened by name a second time - # on modern Windows boxes, and execfile() needs to open it. - srcfilename = tempfile.mktemp(".py", "doctestdebug") - f = open(srcfilename, 'w') - f.write(src) - f.close() - - try: - if globs: - globs = globs.copy() - else: - globs = {} - - if pm: - try: - execfile(srcfilename, globs, globs) - except: - print sys.exc_info()[1] - pdb.post_mortem(sys.exc_info()[2]) - else: - # Note that %r is vital here. '%s' instead can, e.g., cause - # backslashes to get treated as metacharacters on Windows. - pdb.run("execfile(%r)" % srcfilename, globs, globs) - - finally: - os.remove(srcfilename) - -def debug(module, name, pm=False): - """Debug a single doctest docstring. - - Provide the module (or dotted name of the module) containing the - test to be debugged and the name (within the module) of the object - with the docstring with tests to be debugged. - """ - module = _normalize_module(module) - testsrc = testsource(module, name) - debug_script(testsrc, pm, module.__dict__) - -###################################################################### -## 10. Example Usage -###################################################################### -class _TestClass: - """ - A pointless class, for sanity-checking of docstring testing. - - Methods: - square() - get() - - >>> _TestClass(13).get() + _TestClass(-12).get() - 1 - >>> hex(_TestClass(13).square().get()) - '0xa9' - """ - - def __init__(self, val): - """val -> _TestClass object with associated value val. - - >>> t = _TestClass(123) - >>> print t.get() - 123 - """ - - self.val = val - - def square(self): - """square() -> square TestClass's associated value - - >>> _TestClass(13).square().get() - 169 - """ - - self.val = self.val ** 2 - return self - - def get(self): - """get() -> return TestClass's associated value. - - >>> x = _TestClass(-42) - >>> print x.get() - -42 - """ - - return self.val - -__test__ = {"_TestClass": _TestClass, - "string": r""" - Example of a string object, searched as-is. - >>> x = 1; y = 2 - >>> x + y, x * y - (3, 2) - """, - - "bool-int equivalence": r""" - In 2.2, boolean expressions displayed - 0 or 1. By default, we still accept - them. This can be disabled by passing - DONT_ACCEPT_TRUE_FOR_1 to the new - optionflags argument. - >>> 4 == 4 - 1 - >>> 4 == 4 - True - >>> 4 > 4 - 0 - >>> 4 > 4 - False - """, - - "blank lines": r""" - Blank lines can be marked with : - >>> print 'foo\n\nbar\n' - foo - - bar - - """, - - "ellipsis": r""" - If the ellipsis flag is used, then '...' can be used to - elide substrings in the desired output: - >>> print range(1000) #doctest: +ELLIPSIS - [0, 1, 2, ..., 999] - """, - - "whitespace normalization": r""" - If the whitespace normalization flag is used, then - differences in whitespace are ignored. - >>> print range(30) #doctest: +NORMALIZE_WHITESPACE - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, - 27, 28, 29] - """, - } - -def _test(): - r = unittest.TextTestRunner() - r.run(DocTestSuite()) - -if __name__ == "__main__": - _test() diff --git a/django/test/simple.py b/django/test/simple.py index cfaa09a0a4..5f7f86f220 100644 --- a/django/test/simple.py +++ b/django/test/simple.py @@ -1,5 +1,6 @@ -import unittest, doctest +import unittest from django.conf import settings +from django.test import _doctest as doctest from django.test.utils import setup_test_environment, teardown_test_environment from django.test.utils import create_test_db, destroy_test_db from django.test.testcases import OutputChecker, DocTestRunner diff --git a/django/test/testcases.py b/django/test/testcases.py index dd1f73befd..2bc1b5a5f8 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -1,8 +1,9 @@ -import re, doctest, unittest +import re, unittest from urlparse import urlparse from django.db import transaction from django.core import management, mail from django.db.models import get_apps +from django.test import _doctest as doctest from django.test.client import Client normalize_long_ints = lambda s: re.sub(r'(?= 1: print "Creating test database..." @@ -81,6 +95,12 @@ def create_test_db(verbosity=1, autoclobber=False): if settings.DATABASE_ENGINE == "sqlite3": TEST_DATABASE_NAME = ":memory:" else: + suffix = { + 'postgresql': get_postgresql_create_suffix, + 'postgresql_psycopg2': get_postgresql_create_suffix, + 'mysql': get_mysql_create_suffix, + 'mysql_old': get_mysql_create_suffix, + }.get(settings.DATABASE_ENGINE, lambda: '')() if settings.TEST_DATABASE_NAME: TEST_DATABASE_NAME = settings.TEST_DATABASE_NAME else: @@ -92,7 +112,7 @@ def create_test_db(verbosity=1, autoclobber=False): cursor = connection.cursor() _set_autocommit(connection) try: - cursor.execute("CREATE DATABASE %s" % backend.quote_name(TEST_DATABASE_NAME)) + cursor.execute("CREATE DATABASE %s %s" % (backend.quote_name(TEST_DATABASE_NAME), suffix)) except Exception, e: sys.stderr.write("Got an error creating the test database: %s\n" % e) if not autoclobber: @@ -104,7 +124,7 @@ def create_test_db(verbosity=1, autoclobber=False): cursor.execute("DROP DATABASE %s" % backend.quote_name(TEST_DATABASE_NAME)) if verbosity >= 1: print "Creating test database..." - cursor.execute("CREATE DATABASE %s" % backend.quote_name(TEST_DATABASE_NAME)) + cursor.execute("CREATE DATABASE %s %s" % (backend.quote_name(TEST_DATABASE_NAME), suffix)) except Exception, e: sys.stderr.write("Got an error recreating the test database: %s\n" % e) sys.exit(2) diff --git a/django/utils/feedgenerator.py b/django/utils/feedgenerator.py index aa315b5292..2c82e9a37a 100644 --- a/django/utils/feedgenerator.py +++ b/django/utils/feedgenerator.py @@ -168,6 +168,8 @@ class Rss201rev2Feed(RssFeed): (item['author_email'], item['author_name'])) elif item["author_email"]: handler.addQuickElement(u"author", item["author_email"]) + elif item["author_name"]: + handler.addQuickElement(u"dc:creator", item["author_name"], {"xmlns:dc": u"http://purl.org/dc/elements/1.1/"}) if item['pubdate'] is not None: handler.addQuickElement(u"pubDate", rfc2822_date(item['pubdate']).decode('ascii')) diff --git a/docs/add_ons.txt b/docs/add_ons.txt index 4f45d99d9a..ffc4f7420f 100644 --- a/docs/add_ons.txt +++ b/docs/add_ons.txt @@ -6,8 +6,9 @@ Django aims to follow Python's `"batteries included" philosophy`_. It ships with a variety of extra, optional tools that solve common Web-development problems. -This code lives in ``django/contrib`` in the Django distribution. Here's a -rundown of the packages in ``contrib``: +This code lives in ``django/contrib`` in the Django distribution. This document +gives a rundown of the packages in ``contrib``, along with any dependencies +those packages have. .. admonition:: Note @@ -26,6 +27,8 @@ The automatic Django administrative interface. For more information, see .. _Tutorial 2: ../tutorial02/ +Requires the auth_ and contenttypes_ contrib packages to be installed. + auth ==== @@ -144,6 +147,8 @@ See the `flatpages documentation`_. .. _flatpages documentation: ../flatpages/ +Requires the sites_ contrib package to be installed as well. + localflavor =========== diff --git a/docs/db-api.txt b/docs/db-api.txt index de612e0e3d..775913d693 100644 --- a/docs/db-api.txt +++ b/docs/db-api.txt @@ -134,6 +134,15 @@ the database until you explicitly call ``save()``. The ``save()`` method has no return value. +Updating ``ForeignKey`` fields works exactly the same way; simply assign an +object of the right type to the field in question:: + + joe = Author.objects.create(name="Joe") + entry.author = joe + entry.save() + +Django will complain if you try to assign an object of the wrong type. + How Django knows to UPDATE vs. INSERT ------------------------------------- @@ -1229,8 +1238,8 @@ whose ``headline`` contains ``'Lennon'``:: Blog.objects.filter(entry__headline__contains='Lennon') -Escaping parenthesis and underscores in LIKE statements -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Escaping percent signs and underscores in LIKE statements +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The field lookups that equate to ``LIKE`` SQL statements (``iexact``, ``contains``, ``icontains``, ``startswith``, ``istartswith``, ``endswith`` @@ -1748,7 +1757,8 @@ Shortcuts As you develop views, you will discover a number of common idioms in the way you use the database API. Django encodes some of these idioms as -shortcuts that can be used to simplify the process of writing views. +shortcuts that can be used to simplify the process of writing views. These +functions are in the ``django.shortcuts`` module. get_object_or_404() ------------------- diff --git a/docs/django-admin.txt b/docs/django-admin.txt index cc2eadc365..d20db7edc9 100644 --- a/docs/django-admin.txt +++ b/docs/django-admin.txt @@ -295,7 +295,7 @@ Serving static files with the development server ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ By default, the development server doesn't serve any static files for your site -(such as CSS files, images, things under ``MEDIA_ROOT_URL`` and so forth). If +(such as CSS files, images, things under ``MEDIA_URL`` and so forth). If you want to configure Django to serve static media, read the `serving static files`_ documentation. @@ -403,9 +403,10 @@ 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. -``syncdb`` will also search for and install any fixture named ``initial_data``. -See the documentation for ``loaddata`` for details on the specification of -fixture data files. +``syncdb`` will also search for and install any fixture named ``initial_data`` +with an appropriate extension (e.g. ``json`` or ``xml``). See the +documentation for ``loaddata`` for details on the specification of fixture +data files. test ---- diff --git a/docs/fastcgi.txt b/docs/fastcgi.txt index 119688096f..81888bba76 100644 --- a/docs/fastcgi.txt +++ b/docs/fastcgi.txt @@ -203,7 +203,7 @@ This is probably the most common case, if you're using Django's admin site:: DocumentRoot /home/user/public_html Alias /media /home/user/python/django/contrib/admin/media RewriteEngine On - RewriteRule ^/(media.*)$ /$1 [QSA,L] + RewriteRule ^/(media.*)$ /$1 [QSA,L,PT] RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^/(.*)$ /mysite.fcgi/$1 [QSA,L] diff --git a/docs/generic_views.txt b/docs/generic_views.txt index bb5f7320f6..359a82506a 100644 --- a/docs/generic_views.txt +++ b/docs/generic_views.txt @@ -99,6 +99,9 @@ which is a dictionary of the parameters captured in the URL. dictionary is callable, the generic view will call it just before rendering the template. + * ``mimetype``: The MIME type to use for the resulting document. Defaults + to the value of the ``DEFAULT_CONTENT_TYPE`` setting. + **Example:** Given the following URL patterns:: diff --git a/docs/install.txt b/docs/install.txt index c3ccd27f76..4f5a4bbe31 100644 --- a/docs/install.txt +++ b/docs/install.txt @@ -97,7 +97,9 @@ This file should also be located in your ``site-packages`` directory. * If you're using Windows: ``C:\Python2.X\lib\site-packages`` - * If you're using MacOSX: ``/Library/Python2.X/site-packages`` + * If you're using MacOSX: ``/Library/Python2.X/site-packages`` or + ``/Library/Frameworks/Python.framework/Versions/2.X/lib/python2.X/site-packages/`` + (in later releases). Install the Django code ======================= diff --git a/docs/model-api.txt b/docs/model-api.txt index ae1c37fc8f..a6bb8a5cfc 100644 --- a/docs/model-api.txt +++ b/docs/model-api.txt @@ -269,6 +269,13 @@ For example, say your ``MEDIA_ROOT`` is set to ``'/home/media'``, and upload a file on Jan. 15, 2007, it will be saved in the directory ``/home/media/photos/2007/01/15``. +If you want to retrieve the upload file's on-disk filename, or a URL that +refers to that file, or the file's size, you can use the +``get_FOO_filename()``, ``get_FOO_url()`` and ``get_FOO_size()`` methods. +They are all documented here__. + +__ ../db-api/#get-foo-filename + Note that whenever you deal with uploaded files, you should pay close attention to where you're uploading them and what type of files they are, to avoid security holes. *Validate all uploaded files* so that you're sure the files are @@ -338,9 +345,14 @@ image. Has two extra optional arguments, ``height_field`` and ``width_field``, which, if set, will be auto-populated with the height and width of the image each time a model instance is saved. +In addition to the special ``get_FOO_*`` methods that are available for +``FileField``, an ``ImageField`` also has ``get_FOO_height()`` and +``get_FOO_width()`` methods. These are documented elsewhere_. + Requires the `Python Imaging Library`_. .. _Python Imaging Library: http://www.pythonware.com/products/pil/ +.. _elsewhere: ../db-api/#get-foo-height-and-get-foo-width ``IntegerField`` ~~~~~~~~~~~~~~~~ @@ -463,8 +475,10 @@ If ``True``, Django will store empty values as ``NULL`` in the database. Default is ``False``. Note that empty string values will always get stored as empty strings, not -as ``NULL`` -- so use ``null=True`` for non-string fields such as integers, -booleans and dates. +as ``NULL``. Only use ``null=True`` for non-string fields such as integers, +booleans and dates. For both types of fields, you will also need to set +``blank=True`` if you wish to permit empty values in forms, as the ``null`` +parameter only affects database storage (see blank_, below). Avoid using ``null`` on string-based fields such as ``CharField`` and ``TextField`` unless you have an excellent reason. If a string-based field @@ -1846,14 +1860,15 @@ rows. Example:: row = cursor.fetchone() return row -``connection`` and ``cursor`` simply use the standard `Python DB-API`_. If -you're not familiar with the Python DB-API, note that the SQL statement in -``cursor.execute()`` uses placeholders, ``"%s"``, rather than adding parameters -directly within the SQL. If you use this technique, the underlying database -library will automatically add quotes and escaping to your parameter(s) as -necessary. (Also note that Django expects the ``"%s"`` placeholder, *not* the -``"?"`` placeholder, which is used by the SQLite Python bindings. This is for -the sake of consistency and sanity.) +``connection`` and ``cursor`` mostly implement the standard `Python DB-API`_ +(except when it comes to `transaction handling`_). If you're not familiar with +the Python DB-API, note that the SQL statement in ``cursor.execute()`` uses +placeholders, ``"%s"``, rather than adding parameters directly within the SQL. +If you use this technique, the underlying database library will automatically +add quotes and escaping to your parameter(s) as necessary. (Also note that +Django expects the ``"%s"`` placeholder, *not* the ``"?"`` placeholder, which is +used by the SQLite Python bindings. This is for the sake of consistency and +sanity.) A final note: If all you want to do is a custom ``WHERE`` clause, you can just just the ``where``, ``tables`` and ``params`` arguments to the standard lookup @@ -1861,6 +1876,7 @@ API. See `Other lookup options`_. .. _Python DB-API: http://www.python.org/peps/pep-0249.html .. _Other lookup options: ../db-api/#extra-params-select-where-tables +.. _transaction handling: ../transactions/ Overriding default model methods -------------------------------- diff --git a/docs/modpython.txt b/docs/modpython.txt index 37909a09fd..388a6168f3 100644 --- a/docs/modpython.txt +++ b/docs/modpython.txt @@ -51,9 +51,17 @@ whereas ```` points at places in the URL structure of a Web site. ```` would be meaningless here. Also, if you've manually altered your ``PYTHONPATH`` to put your Django project -on it, you'll need to tell mod_python:: +on it, you'll need to tell mod_python: - PythonPath "['/path/to/project'] + sys.path" +.. parsed-literal:: + + + SetHandler python-program + PythonHandler django.core.handlers.modpython + SetEnv DJANGO_SETTINGS_MODULE mysite.settings + PythonDebug On + **PythonPath "['/path/to/project'] + sys.path"** + .. caution:: @@ -211,6 +219,41 @@ Here are two recommended approaches: 2. Or, copy the admin media files so that they live within your Apache document root. +Using eggs with mod_python +========================== + +If you installed Django from a Python egg_ or are using eggs in your Django +project, some extra configuration is required. Create an extra file in your +project (or somewhere else) that contains something like the following:: + + import os + os.environ['PYTHON_EGG_CACHE'] = '/some/directory' + +Here, ``/some/directory`` is a directory that the Apache webserver process can +write to. It will be used as the location for any unpacking of code the eggs +need to do. + +Then you have to tell mod_python to import this file before doing anything +else. This is done using the PythonImport_ directive to mod_python. You need +to ensure that you have specified the ``PythonInterpreter`` directive to +mod_python as described above__ (you need to do this even if you aren't +serving multiple installations in this case). Then add the ``PythonImport`` +line inside the ``Location`` or ``VirtualHost`` section. For example:: + + PythonInterpreter my_django + PythonImport /path/to/my/project/file.py my_django + +Note that you can use an absolute path here (or a normal dotted import path), +as described in the `mod_python manual`_. We use an absolute path in the +above example because if any Python path modifications are required to access +your project, they will not have been done at the time the ``PythonImport`` +line is processed. + +.. _Egg: http://peak.telecommunity.com/DevCenter/PythonEggs +.. _PythonImport: http://www.modpython.org/live/current/doc-html/dir-other-pimp.html +.. _mod_python manual: PythonImport_ +__ `Multiple Django installations on the same Apache`_ + Error handling ============== @@ -256,3 +299,5 @@ as necessary. .. _Expat Causing Apache Crash: http://www.dscpl.com.au/articles/modpython-006.html .. _mod_python FAQ entry: http://modpython.org/FAQ/faqw.py?req=show&file=faq02.013.htp .. _Getting mod_python Working: http://www.dscpl.com.au/articles/modpython-001.html + + diff --git a/docs/newforms.txt b/docs/newforms.txt index 7ec4e9560c..bb6c179648 100644 --- a/docs/newforms.txt +++ b/docs/newforms.txt @@ -686,13 +686,13 @@ For example::
    -
  • {{ form.sender.label }} {{ form.sender.label }}
  • +
  • {{ form.sender.label }} {{ form.sender }}
  • {{ form.sender.help_text }}
  • - {% if form.sender.errors %}
      {{ form.sender.errors }}{% endif %} + {% if form.sender.errors %}
        {{ form.sender.errors }}
      {% endif %} -
    • {{ form.subject.label }} {{ form.subject.label }}
    • +
    • {{ form.subject.label }} {{ form.subject }}
    • {{ form.subject.help_text }}
    • - {% if form.subject.errors %}
        {{ form.subject.errors }}{% endif %} + {% if form.subject.errors %}
          {{ form.subject.errors }}
        {% endif %} ...
      diff --git a/docs/serialization.txt b/docs/serialization.txt index 3216cb061e..01afa2708c 100644 --- a/docs/serialization.txt +++ b/docs/serialization.txt @@ -44,6 +44,25 @@ This is useful if you want to serialize data directly to a file-like object .. _HTTPResponse: ../request_response/#httpresponse-objects +Subset of fields +~~~~~~~~~~~~~~~~ + +If you only want a subset of fields to be serialized, you can +specify a `fields` argument to the serializer:: + + from django.core import serializers + data = serializers.serialize('xml', SomeModel.objects.all(), fields=('name','size')) + +In this example, only the `name` and `size` attributes of each model will +be serialized. + +.. note:: + + Depending on your model, you may find that it is not possible to deserialize + a model that only serializes a subset of its fields. If a serialized object + doesn't specify all the fields that are required by a model, the deserializer + will not be able to save deserialized instances. + Deserializing data ------------------ @@ -92,10 +111,14 @@ Django "ships" with a few included serializers: ``python`` Translates to and from "simple" Python objects (lists, dicts, strings, etc.). Not really all that useful on its own, but used as a base for other serializers. + + ``yaml`` Serializes to YAML (Yet Another Markup Lanuage). This + serializer is only available if PyYAML_ is installed. ========== ============================================================== .. _json: http://json.org/ .. _simplejson: http://undefined.org/python/#simplejson +.. _PyYAML: http://www.pyyaml.org/ Notes for specific serialization formats ---------------------------------------- diff --git a/docs/settings.txt b/docs/settings.txt index bf0a9f85f6..12e6dab4bc 100644 --- a/docs/settings.txt +++ b/docs/settings.txt @@ -264,6 +264,11 @@ MySQL will connect via a Unix socket to the specified socket. For example:: If you're using MySQL and this value *doesn't* start with a forward slash, then this value is assumed to be the host. +If you're using PostgreSQL, an empty string means to use a Unix domain socket +for the connection, rather than a network connection to localhost. If you +explictly need to use a TCP/IP connection on the local machine with +PostgreSQL, specify ``localhost`` here. + DATABASE_NAME ------------- @@ -771,7 +776,8 @@ Default:: ("django.core.context_processors.auth", "django.core.context_processors.debug", - "django.core.context_processors.i18n") + "django.core.context_processors.i18n", + "django.core.context_processors.media") A tuple of callables that are used to populate the context in ``RequestContext``. These callables take a request object as their argument and return a dictionary @@ -820,26 +826,57 @@ misspelled) variables. See `How invalid variables are handled`_. .. _How invalid variables are handled: ../templates_python/#how-invalid-variables-are-handled -TEST_RUNNER ------------ +TEST_DATABASE_CHARSET +--------------------- -Default: ``'django.test.simple.run_tests'`` +**New in Django development version** -The name of the method to use for starting the test suite. See -`Testing Django Applications`_. +Default: ``None`` -.. _Testing Django Applications: ../testing/ +The character set encoding used to create the test database. The value of this +string is passed directly through to the database, so its format is +backend-specific. + +Supported for the PostgreSQL_ (``postgresql``, ``postgresql_psycopg2``) and MySQL_ (``mysql``, ``mysql_old``) backends. + +.. _PostgreSQL: http://www.postgresql.org/docs/8.2/static/multibyte.html +.. _MySQL: http://www.mysql.org/doc/refman/5.0/en/charset-database.html + +TEST_DATABASE_COLLATION +------------------------ + +**New in Django development version** + +Default: ``None`` + +The collation order to use when creating the test database. This value is +passed directly to the backend, so its format is backend-specific. + +Only supported for ``mysql`` and ``mysql_old`` backends (see `section 10.3.2`_ +of the MySQL manual for details). + +.. _section 10.3.2: http://www.mysql.org/doc/refman/5.0/en/charset-database.html TEST_DATABASE_NAME ------------------ Default: ``None`` -The name of database to use when running the test suite. If a value of +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/ +TEST_RUNNER +----------- + +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/ + TIME_FORMAT ----------- diff --git a/docs/templates.txt b/docs/templates.txt index 2f9f769b96..d8b511dedc 100644 --- a/docs/templates.txt +++ b/docs/templates.txt @@ -91,9 +91,12 @@ Filters can be "chained." The output of one filter is applied to the next. ``{{ text|escape|linebreaks }}`` is a common idiom for escaping text contents, then converting line breaks to ``

      `` tags. -Some filters take arguments. A filter argument looks like this: -``{{ bio|truncatewords:"30" }}``. This will display the first 30 words of the -``bio`` variable. Filter arguments always are in double quotes. +Some filters take arguments. A filter argument looks like this: ``{{ +bio|truncatewords:30 }}``. This will display the first 30 words of the ``bio`` +variable. + +Filter arguments that contain spaces must be quoted; for example, to join a list +with commas and spaced you'd use ``{{ list|join:", " }}``. The `Built-in filter reference`_ below describes all the built-in filters. @@ -112,7 +115,7 @@ know how to write Python code. Comments ======== -To comment-out part of a template, use the comment syntax: ``{# #}``. +To comment-out part of a line in a template, use the comment syntax: ``{# #}``. For example, this template would render as ``'hello'``:: @@ -122,6 +125,12 @@ A comment can contain any template code, invalid or not. For example:: {# {% if foo %}bar{% else %} #} +This syntax can only be used for single-line comments (no newlines are +permitted between the ``{#`` and ``#}`` delimiters). If you need to comment +out a multiline portion of the template, see the ``comment`` tag, below__. + +__ comment_ + Template inheritance ==================== @@ -843,10 +852,11 @@ The first argument is a path to a view function in the format should be comma-separated values that will be used as positional and keyword arguments in the URL. All arguments required by the URLconf should be present. -For example, suppose you have a view, ``app_name.client``, whose URLconf takes -a client ID. The URLconf line might look like this:: +For example, suppose you have a view, ``app_views.client``, whose URLconf +takes a client ID (here, ``client()`` is a method inside the views file +``app_views.py``). The URLconf line might look like this:: - ('^client/(\d+)/$', 'app_name.client') + ('^client/(\d+)/$', 'app_views.client') If this app's URLconf is included into the project's URLconf under a path such as this:: @@ -855,7 +865,7 @@ such as this:: ...then, in a template, you can create a link to this view like this:: - {% url app_name.client client.id %} + {% url app_views.client client.id %} The template tag will output the string ``/clients/client/123/``. diff --git a/docs/templates_python.txt b/docs/templates_python.txt index 08a287f572..f3e2f2c64b 100644 --- a/docs/templates_python.txt +++ b/docs/templates_python.txt @@ -294,7 +294,8 @@ return a dictionary of items to be merged into the context. By default, ("django.core.context_processors.auth", "django.core.context_processors.debug", - "django.core.context_processors.i18n") + "django.core.context_processors.i18n", + "django.core.context_processors.media") Each processor is applied in order. That means, if one processor adds a variable to the context and a second processor adds a variable with the same @@ -390,6 +391,15 @@ See the `internationalization docs`_ for more. .. _LANGUAGE_CODE setting: ../settings/#language-code .. _internationalization docs: ../i18n/ +django.core.context_processors.media +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If ``TEMPLATE_CONTEXT_PROCESSORS`` contains this processor, every +``RequestContext`` will contain a variable ``MEDIA_URL``, providing the +value of the `MEDIA_URL setting`_. + +.. _MEDIA_URL setting: ../settings/#media-url + django.core.context_processors.request ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/testing.txt b/docs/testing.txt index 92edf46e09..50c4ec3046 100644 --- a/docs/testing.txt +++ b/docs/testing.txt @@ -4,7 +4,7 @@ Testing Django applications Automated testing is an extremely useful bug-killing tool for the modern Web developer. You can use a collection of tests -- a **test suite** -- to -to solve, or avoid, a number of problems: +solve, or avoid, a number of problems: * When you're writing new code, you can use tests to validate your code works as expected. @@ -147,7 +147,7 @@ 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 +docstrings. This gives the added advantage of giving your modules automatic documentation -- well-written doctests can kill both the documentation and the testing bird with a single stone. @@ -571,6 +571,14 @@ 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. +**New in Django development version:** For fine-grained control over the +character encoding of your database, use the ``TEST_DATABASE_CHARSET`` setting. +If you're using MySQL, you can also use the ``TEST_DATABASE_COLLATION`` setting +to control the particular collation used by the test database. See the +settings_ documentation for details of these advanced settings. + +.. _settings: ../settings/ + The test database is created by the user in the ``DATABASE_USER`` setting. This user needs to have sufficient privileges to create a new database on the system. diff --git a/docs/tutorial03.txt b/docs/tutorial03.txt index 8bb63791aa..41febe021d 100644 --- a/docs/tutorial03.txt +++ b/docs/tutorial03.txt @@ -60,9 +60,10 @@ arguments from the dictionary (an optional third item in the tuple). For more on ``HTTPRequest`` objects, see the `request and response documentation`_. For more details on URLconfs, see the `URLconf documentation`_. -When you ran ``python manage.py startproject mysite`` at the beginning of +When you ran ``python django-admin.py startproject mysite`` at the beginning of Tutorial 1, it created a default URLconf in ``mysite/urls.py``. It also -automatically set your ``ROOT_URLCONF`` setting to point at that file:: +automatically set your ``ROOT_URLCONF`` setting (in ``settings.py``) to point +at that file:: ROOT_URLCONF = 'mysite.urls' diff --git a/docs/tutorial04.txt b/docs/tutorial04.txt index 6fee842f8b..5cc12c445d 100644 --- a/docs/tutorial04.txt +++ b/docs/tutorial04.txt @@ -48,6 +48,7 @@ So let's create a ``vote()`` function in ``mysite/polls/views.py``:: from django.shortcuts import get_object_or_404, render_to_response from django.http import HttpResponseRedirect + from django.core.urlresolvers import reverse from mysite.polls.models import Choice, Poll # ... def vote(request, poll_id): @@ -66,7 +67,7 @@ So let's create a ``vote()`` function in ``mysite/polls/views.py``:: # Always return an HttpResponseRedirect after successfully dealing # with POST data. This prevents data from being posted twice if a # user hits the Back button. - return HttpResponseRedirect('/polls/%s/results/' % p.id) + return HttpResponseRedirect(reverse('mysite.polls.views.results', args=(p.id,))) This code includes a few things we haven't covered yet in this tutorial: @@ -86,13 +87,29 @@ This code includes a few things we haven't covered yet in this tutorial: * After incrementing the choice count, the code returns an ``HttpResponseRedirect`` rather than a normal ``HttpResponse``. ``HttpResponseRedirect`` takes a single argument: the URL to which the - user will be redirected. You should leave off the "http://" and domain - name if you can. That helps your app become portable across domains. + user will be redirected (see the following point for how we construct + the URL in this case). As the Python comment above points out, you should always return an ``HttpResponseRedirect`` after successfully dealing with POST data. This tip isn't specific to Django; it's just good Web development practice. + * We are using the ``reverse()`` function in the ``HttpResponseRedirect`` + constructor in this example. This function helps avoid having to + hardcode a URL in the view function. It is given the name of the view + that we want to pass control to and the variable portion of the URL + pattern that points to that view. In this case, using the URLConf we set + up in Tutorial 3, this ``reverse()`` call will return a string like :: + + '/polls/3/results/' + + ... where the ``3`` is the value of ``p.id``. This redirected URL will + then call the ``'results'`` view to display the final page. Note that + you need to use the full name of the view here (including the prefix). + + For more information about ``reverse()``, see the `URL dispatcher`_ + documentation. + As mentioned in Tutorial 3, ``request`` is a ``HTTPRequest`` object. For more on ``HTTPRequest`` objects, see the `request and response documentation`_. @@ -121,6 +138,7 @@ results page that gets updated each time you vote. If you submit the form without having chosen a choice, you should see the error message. .. _request and response documentation: ../request_response/ +.. _URL dispatcher: ../url_dispatch#reverse Use generic views: Less code is better ====================================== @@ -256,4 +274,8 @@ installments: * Advanced admin features: Permissions * Advanced admin features: Custom JavaScript +In the meantime, you can read through the rest of the `Django documentation`_ +and start writing your own applications. + .. _Tutorial 3: ../tutorial03/ +.. _Django documentation: http://www.djangoproject.com/documentation/ diff --git a/docs/url_dispatch.txt b/docs/url_dispatch.txt index 39eed0625b..402b1200b5 100644 --- a/docs/url_dispatch.txt +++ b/docs/url_dispatch.txt @@ -551,3 +551,38 @@ not restricted to valid Python names. Putting a prefix on your URL names, perhaps derived from the application name, will decrease the chances of collision. We recommend something like ``myapp-comment`` instead of ``comment``. + +Utility methods +=============== + +reverse() +--------- + +If you need to use something similar to the ``{% url %}`` template tag in your +code, Django provides the ``django.core.urlresolvers.reverse()``. The +``reverse()`` function has the following signature:: + + reverse(viewname, urlconf=None, args=None, kwargs=None) + +``viewname`` is either the function name (either a function reference, or the +string version of the name, if you used that form in ``urlpatterns``) or the +`URL pattern name`_. Normally, you won't need to worry about the +``urlconf`` parameter and will only pass in the positional and keyword +arguments to use in the URL matching. For example:: + + from django.core.urlresolvers import reverse + + def myview(request): + return HttpResponseRedirect(reverse('arch-summary', args=[1945])) + +.. _URL pattern name: `Naming URL patterns`_ + +permalink() +----------- + +The ``permalink()`` decorator is useful for writing short methods that return +a full URL path. For example, a model's ``get_absolute_url()`` method. Refer +to the `model API documentation`_ for more information about ``permalink()``. + +.. _model API documentation: ../model-api/#the-permalink-decorator + diff --git a/tests/modeltests/lookup/models.py b/tests/modeltests/lookup/models.py index c634aef8a1..6af70f8351 100644 --- a/tests/modeltests/lookup/models.py +++ b/tests/modeltests/lookup/models.py @@ -131,6 +131,27 @@ True [('headline', 'Article 7'), ('id', 7)] [('headline', 'Article 1'), ('id', 1)] + +# you can use values() even on extra fields +>>> for d in Article.objects.extra( select={'id_plus_one' : 'id + 1'} ).values('id', 'id_plus_one'): +... i = d.items() +... i.sort() +... i +[('id', 5), ('id_plus_one', 6)] +[('id', 6), ('id_plus_one', 7)] +[('id', 4), ('id_plus_one', 5)] +[('id', 2), ('id_plus_one', 3)] +[('id', 3), ('id_plus_one', 4)] +[('id', 7), ('id_plus_one', 8)] +[('id', 1), ('id_plus_one', 2)] + +# however, an exception FieldDoesNotExist will still be thrown +# if you try to access non-existent field (field that is neither on the model nor extra) +>>> Article.objects.extra( select={'id_plus_one' : 'id + 1'} ).values('id', 'id_plus_two') +Traceback (most recent call last): + ... +FieldDoesNotExist: Article has no field named 'id_plus_two' + # if you don't specify which fields, all are returned >>> list(Article.objects.filter(id=5).values()) == [{'id': 5, 'headline': 'Article 5', 'pub_date': datetime(2005, 8, 1, 9, 0)}] True diff --git a/tests/modeltests/serializers/models.py b/tests/modeltests/serializers/models.py index 339303fc0a..8d44d5eae7 100644 --- a/tests/modeltests/serializers/models.py +++ b/tests/modeltests/serializers/models.py @@ -159,4 +159,8 @@ __test__ = {'API_TESTS':""" >>> article.author +# Serializer output can be restricted to a subset of fields +>>> print serializers.serialize("json", Article.objects.all(), fields=('headline','pub_date')) +[{"pk": "1", "model": "serializers.article", "fields": {"headline": "Just kidding; I love TV poker", "pub_date": "2006-06-16 11:00:00"}}, {"pk": "2", "model": "serializers.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": "3", "model": "serializers.article", "fields": {"headline": "Forward references pose no problem", "pub_date": "2006-06-16 15:00:00"}}] + """} diff --git a/tests/regressiontests/forms/tests.py b/tests/regressiontests/forms/tests.py index 8d219dc0a1..4114919c2e 100644 --- a/tests/regressiontests/forms/tests.py +++ b/tests/regressiontests/forms/tests.py @@ -3656,6 +3656,25 @@ u' id="header"' u' class="news" title="Read this"' >>> flatatt({}) u'' + +#################################### +# Test accessing errors in clean() # +#################################### + +>>> class UserForm(Form): +... username = CharField(max_length=10) +... password = CharField(widget=PasswordInput) +... def clean(self): +... data = self.cleaned_data +... if not self.errors: +... data['username'] = data['username'].lower() +... return data + +>>> f = UserForm({'username': 'SirRobin', 'password': 'blue'}) +>>> f.is_valid() +True +>>> f.cleaned_data['username'] +u'sirrobin' """ __test__ = { diff --git a/tests/regressiontests/serializers_regress/models.py b/tests/regressiontests/serializers_regress/models.py index 999b79ccaf..b441885f10 100644 --- a/tests/regressiontests/serializers_regress/models.py +++ b/tests/regressiontests/serializers_regress/models.py @@ -129,6 +129,9 @@ class M2MSelfData(models.Model): class FKDataToField(models.Model): data = models.ForeignKey(UniqueAnchor, null=True, to_field='data') +class FKDataToO2O(models.Model): + data = models.ForeignKey(O2OData, null=True) + # The following test classes are for validating the # deserialization of objects that use a user-defined # field as the primary key. @@ -202,3 +205,7 @@ class USStatePKData(models.Model): # class XMLPKData(models.Model): # data = models.XMLField(primary_key=True) +class ComplexModel(models.Model): + field1 = models.CharField(maxlength=10) + field2 = models.CharField(maxlength=10) + field3 = models.CharField(maxlength=10) diff --git a/tests/regressiontests/serializers_regress/tests.py b/tests/regressiontests/serializers_regress/tests.py index 5df4c6818a..cd27041eb2 100644 --- a/tests/regressiontests/serializers_regress/tests.py +++ b/tests/regressiontests/serializers_regress/tests.py @@ -9,6 +9,7 @@ forward, backwards and self references. import unittest, datetime +from cStringIO import StringIO from django.utils.functional import curry from django.core import serializers @@ -197,6 +198,8 @@ The end."""), (fk_obj, 451, FKDataToField, "UAnchor 2"), (fk_obj, 452, FKDataToField, None), + (fk_obj, 460, FKDataToO2O, 300), + (data_obj, 500, Anchor, "Anchor 3"), (data_obj, 501, Anchor, "Anchor 4"), (data_obj, 502, UniqueAnchor, "UAnchor 2"), @@ -276,5 +279,41 @@ def serializerTest(format, self): for (func, pk, klass, datum) in test_data: func[1](self, pk, klass, datum) +def fieldsTest(format, self): + # Clear the database first + management.flush(verbosity=0, interactive=False) + + obj = ComplexModel(field1='first',field2='second',field3='third') + obj.save() + + # Serialize then deserialize the test database + serialized_data = serializers.serialize(format, [obj], indent=2, fields=('field1','field3')) + result = serializers.deserialize(format, serialized_data).next() + + # Check that the deserialized object contains data in only the serialized fields. + self.assertEqual(result.object.field1, 'first') + self.assertEqual(result.object.field2, '') + self.assertEqual(result.object.field3, 'third') + +def streamTest(format, self): + # Clear the database first + management.flush(verbosity=0, interactive=False) + + obj = ComplexModel(field1='first',field2='second',field3='third') + obj.save() + + # Serialize the test database to a stream + stream = StringIO() + serializers.serialize(format, [obj], indent=2, stream=stream) + + # Serialize normally for a comparison + string_data = serializers.serialize(format, [obj], indent=2) + + # Check that the two are the same + self.assertEqual(string_data, stream.buffer()) + stream.close() + for format in serializers.get_serializer_formats(): setattr(SerializerTests, 'test_'+format+'_serializer', curry(serializerTest, format)) + setattr(SerializerTests, 'test_'+format+'_serializer_fields', curry(fieldsTest, format)) + setattr(SerializerTests, 'test_'+format+'_serializer_stream', curry(fieldsTest, format)) -- cgit v1.3