diff --git a/AUTHORS b/AUTHORS index 6f825aea9e..a0f25e1c12 100644 --- a/AUTHORS +++ b/AUTHORS @@ -52,7 +52,7 @@ answer newbie questions, and generally made Django that much better: andy@jadedplanet.net Fabrice Aneche ant9000@netwise.it - Florian Apolloner + Florian Apolloner arien David Ascher Jökull Sólberg Auðunsson diff --git a/django/conf/locale/it/LC_MESSAGES/django.mo b/django/conf/locale/it/LC_MESSAGES/django.mo index 683680bb7d..e96764a0a8 100644 Binary files a/django/conf/locale/it/LC_MESSAGES/django.mo and b/django/conf/locale/it/LC_MESSAGES/django.mo differ diff --git a/django/conf/locale/it/LC_MESSAGES/django.po b/django/conf/locale/it/LC_MESSAGES/django.po index a8f02e4967..4e87ad549d 100644 --- a/django/conf/locale/it/LC_MESSAGES/django.po +++ b/django/conf/locale/it/LC_MESSAGES/django.po @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: Django vSVN\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2008-06-02 18:40+0200\n" -"PO-Revision-Date: 2008-05-18 19:13+0200\n" +"POT-Creation-Date: 2008-07-12 20:30+0200\n" +"PO-Revision-Date: 2008-07-12 20:45+0200\n" "Last-Translator: Nicola Larosa \n" "Language-Team: Italiano\n" "MIME-Version: 1.0\n" @@ -16,191 +16,199 @@ msgstr "" "X-Generator: KBabel 1.11.4\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: conf/global_settings.py:43 +#: conf/global_settings.py:44 msgid "Arabic" msgstr "Arabo" -#: conf/global_settings.py:44 +#: conf/global_settings.py:45 msgid "Bengali" msgstr "Bengali" -#: conf/global_settings.py:45 +#: conf/global_settings.py:46 msgid "Bulgarian" msgstr "Bulgaro" -#: conf/global_settings.py:46 +#: conf/global_settings.py:47 msgid "Catalan" msgstr "Catalano" -#: conf/global_settings.py:47 +#: conf/global_settings.py:48 msgid "Czech" msgstr "Ceco" -#: conf/global_settings.py:48 +#: conf/global_settings.py:49 msgid "Welsh" msgstr "Gallese" -#: conf/global_settings.py:49 +#: conf/global_settings.py:50 msgid "Danish" msgstr "Danese" -#: conf/global_settings.py:50 +#: conf/global_settings.py:51 msgid "German" msgstr "Tedesco" -#: conf/global_settings.py:51 +#: conf/global_settings.py:52 msgid "Greek" msgstr "Greco" -#: conf/global_settings.py:52 +#: conf/global_settings.py:53 msgid "English" msgstr "Inglese" -#: conf/global_settings.py:53 +#: conf/global_settings.py:54 msgid "Spanish" msgstr "Spagnolo" -#: conf/global_settings.py:54 +#: conf/global_settings.py:55 +msgid "Estonian" +msgstr "Estone" + +#: conf/global_settings.py:56 msgid "Argentinean Spanish" msgstr "Spagnolo argentino" -#: conf/global_settings.py:55 +#: conf/global_settings.py:57 msgid "Basque" msgstr "Basco" -#: conf/global_settings.py:56 +#: conf/global_settings.py:58 msgid "Persian" msgstr "Persiano" -#: conf/global_settings.py:57 +#: conf/global_settings.py:59 msgid "Finnish" msgstr "Finlandese" -#: conf/global_settings.py:58 +#: conf/global_settings.py:60 msgid "French" msgstr "Francese" -#: conf/global_settings.py:59 +#: conf/global_settings.py:61 msgid "Irish" msgstr "Irlandese" -#: conf/global_settings.py:60 +#: conf/global_settings.py:62 msgid "Galician" msgstr "Galiziano" -#: conf/global_settings.py:61 +#: conf/global_settings.py:63 msgid "Hungarian" msgstr "Ungherese" -#: conf/global_settings.py:62 +#: conf/global_settings.py:64 msgid "Hebrew" msgstr "Ebraico" -#: conf/global_settings.py:63 +#: conf/global_settings.py:65 msgid "Croatian" msgstr "Croato" -#: conf/global_settings.py:64 +#: conf/global_settings.py:66 msgid "Icelandic" msgstr "Islandese" -#: conf/global_settings.py:65 +#: conf/global_settings.py:67 msgid "Italian" msgstr "Italiano" -#: conf/global_settings.py:66 +#: conf/global_settings.py:68 msgid "Japanese" msgstr "Giapponese" -#: conf/global_settings.py:67 +#: conf/global_settings.py:69 msgid "Georgian" msgstr "Georgiano" -#: conf/global_settings.py:68 +#: conf/global_settings.py:70 msgid "Korean" msgstr "Coreano" -#: conf/global_settings.py:69 +#: conf/global_settings.py:71 msgid "Khmer" msgstr "Khmer" -#: conf/global_settings.py:70 +#: conf/global_settings.py:72 msgid "Kannada" msgstr "Kannada" -#: conf/global_settings.py:71 +#: conf/global_settings.py:73 msgid "Latvian" msgstr "Lettone" -#: conf/global_settings.py:72 +#: conf/global_settings.py:74 +msgid "Lithuanian" +msgstr "Lituano" + +#: conf/global_settings.py:75 msgid "Macedonian" msgstr "Macedone" -#: conf/global_settings.py:73 +#: conf/global_settings.py:76 msgid "Dutch" msgstr "Olandese" -#: conf/global_settings.py:74 +#: conf/global_settings.py:77 msgid "Norwegian" msgstr "Norvegese" -#: conf/global_settings.py:75 +#: conf/global_settings.py:78 msgid "Polish" msgstr "Polacco" -#: conf/global_settings.py:76 +#: conf/global_settings.py:79 msgid "Portugese" msgstr "Portoghese" -#: conf/global_settings.py:77 +#: conf/global_settings.py:80 msgid "Brazilian Portuguese" msgstr "Brasiliano Portoghese" -#: conf/global_settings.py:78 +#: conf/global_settings.py:81 msgid "Romanian" msgstr "Rumeno" -#: conf/global_settings.py:79 +#: conf/global_settings.py:82 msgid "Russian" msgstr "Russo" -#: conf/global_settings.py:80 +#: conf/global_settings.py:83 msgid "Slovak" msgstr "Slovacco" -#: conf/global_settings.py:81 +#: conf/global_settings.py:84 msgid "Slovenian" msgstr "Sloveno" -#: conf/global_settings.py:82 +#: conf/global_settings.py:85 msgid "Serbian" msgstr "Serbo" -#: conf/global_settings.py:83 +#: conf/global_settings.py:86 msgid "Swedish" msgstr "Svedese" -#: conf/global_settings.py:84 +#: conf/global_settings.py:87 msgid "Tamil" msgstr "Tamil" -#: conf/global_settings.py:85 +#: conf/global_settings.py:88 msgid "Telugu" msgstr "Telugu" -#: conf/global_settings.py:86 +#: conf/global_settings.py:89 msgid "Turkish" msgstr "Turco" -#: conf/global_settings.py:87 +#: conf/global_settings.py:90 msgid "Ukrainian" msgstr "Ucraino" -#: conf/global_settings.py:88 +#: conf/global_settings.py:91 msgid "Simplified Chinese" msgstr "Cinese semplificato" -#: conf/global_settings.py:89 +#: conf/global_settings.py:92 msgid "Traditional Chinese" msgstr "Cinese tradizionale" @@ -1133,15 +1141,15 @@ msgstr "permessi" msgid "group" msgstr "gruppo" -#: contrib/auth/models.py:98 contrib/auth/models.py:141 +#: contrib/auth/models.py:98 contrib/auth/models.py:148 msgid "groups" msgstr "gruppi" -#: contrib/auth/models.py:131 +#: contrib/auth/models.py:138 msgid "username" msgstr "nome utente" -#: contrib/auth/models.py:131 +#: contrib/auth/models.py:138 msgid "" "Required. 30 characters or fewer. Alphanumeric characters only (letters, " "digits and underscores)." @@ -1149,23 +1157,23 @@ msgstr "" "Obbligatorio. 30 caratteri o meno. Solo caratteri alfanumerici (lettere, " "cifre e sottolineati)." -#: contrib/auth/models.py:132 +#: contrib/auth/models.py:139 msgid "first name" msgstr "nome" -#: contrib/auth/models.py:133 +#: contrib/auth/models.py:140 msgid "last name" msgstr "cognome" -#: contrib/auth/models.py:134 +#: contrib/auth/models.py:141 msgid "e-mail address" msgstr "indirizzo e-mail" -#: contrib/auth/models.py:135 +#: contrib/auth/models.py:142 msgid "password" msgstr "password" -#: contrib/auth/models.py:135 +#: contrib/auth/models.py:142 msgid "" "Use '[algo]$[salt]$[hexdigest]' or use the change " "password form." @@ -1173,19 +1181,19 @@ msgstr "" "Usare '[algo]$[salt]$[hexdigest]' oppure la maschera " "di cambio password." -#: contrib/auth/models.py:136 +#: contrib/auth/models.py:143 msgid "staff status" msgstr "privilegi di staff" -#: contrib/auth/models.py:136 +#: contrib/auth/models.py:143 msgid "Designates whether the user can log into this admin site." msgstr "Indica se l'utente può accedere a questo sito di amministrazione." -#: contrib/auth/models.py:137 +#: contrib/auth/models.py:144 msgid "active" msgstr "attivo" -#: contrib/auth/models.py:137 +#: contrib/auth/models.py:144 msgid "" "Designates whether this user should be treated as active. Unselect this " "instead of deleting accounts." @@ -1193,11 +1201,11 @@ msgstr "" "Indica se l'utente debba essere considerato attivo. Deselezionare " "qui, piuttosto che cancellare gli account." -#: contrib/auth/models.py:138 +#: contrib/auth/models.py:145 msgid "superuser status" msgstr "privilegi di superutente" -#: contrib/auth/models.py:138 +#: contrib/auth/models.py:145 msgid "" "Designates that this user has all permissions without explicitly assigning " "them." @@ -1205,15 +1213,15 @@ msgstr "" "Indica che l'utente ha tutti i privilegi, senza che siano stati assegnati " "esplicitamente." -#: contrib/auth/models.py:139 +#: contrib/auth/models.py:146 msgid "last login" msgstr "ultimo accesso" -#: contrib/auth/models.py:140 +#: contrib/auth/models.py:147 msgid "date joined" msgstr "iscritto in data" -#: contrib/auth/models.py:142 +#: contrib/auth/models.py:149 msgid "" "In addition to the permissions manually assigned, this user will also get " "all permissions granted to each group he/she is in." @@ -1221,39 +1229,39 @@ msgstr "" "In aggiunta ai privilegi assegnati manualmente, l'utente riceverà anche " "tutti i privilegi assegnati ad ogni gruppo cui appartiene." -#: contrib/auth/models.py:143 +#: contrib/auth/models.py:150 msgid "user permissions" msgstr "privilegi utente" -#: contrib/auth/models.py:147 +#: contrib/auth/models.py:154 msgid "user" msgstr "utente" -#: contrib/auth/models.py:148 +#: contrib/auth/models.py:155 msgid "users" msgstr "utenti" -#: contrib/auth/models.py:154 +#: contrib/auth/models.py:160 msgid "Personal info" msgstr "Informazioni personali" -#: contrib/auth/models.py:155 +#: contrib/auth/models.py:161 msgid "Permissions" msgstr "Permessi" -#: contrib/auth/models.py:156 +#: contrib/auth/models.py:162 msgid "Important dates" msgstr "Date importanti" -#: contrib/auth/models.py:157 +#: contrib/auth/models.py:163 msgid "Groups" msgstr "Gruppi" -#: contrib/auth/models.py:316 +#: contrib/auth/models.py:323 msgid "message" msgstr "messaggio" -#: contrib/auth/views.py:47 +#: contrib/auth/views.py:49 msgid "Logged out" msgstr "Accesso annullato" @@ -3523,23 +3531,23 @@ msgstr "redirezione" msgid "redirects" msgstr "redirezioni" -#: contrib/sessions/models.py:41 +#: contrib/sessions/models.py:45 msgid "session key" msgstr "chiave di sessione" -#: contrib/sessions/models.py:42 +#: contrib/sessions/models.py:47 msgid "session data" msgstr "dati di sessione" -#: contrib/sessions/models.py:43 +#: contrib/sessions/models.py:48 msgid "expire date" msgstr "data di scadenza" -#: contrib/sessions/models.py:48 +#: contrib/sessions/models.py:53 msgid "session" msgstr "sessione" -#: contrib/sessions/models.py:49 +#: contrib/sessions/models.py:54 msgid "sessions" msgstr "sessioni" @@ -3607,7 +3615,7 @@ msgstr "Sono ammessi soltanto caratteri numerici." msgid "This value can't be comprised solely of digits." msgstr "Questo valore non può essere composto solo da cifre." -#: core/validators.py:128 newforms/fields.py:152 +#: core/validators.py:128 newforms/fields.py:157 msgid "Enter a whole number." msgstr "Inserire un numero intero." @@ -3624,7 +3632,7 @@ msgstr "L'anno deve essere 1900 o successivo." msgid "Invalid date: %s" msgstr "Data non valida: %s" -#: core/validators.py:156 db/models/fields/__init__.py:548 +#: core/validators.py:156 db/models/fields/__init__.py:554 msgid "Enter a valid date in YYYY-MM-DD format." msgstr "Inserire una data valida in formato AAAA-MM-GG." @@ -3632,20 +3640,19 @@ msgstr "Inserire una data valida in formato AAAA-MM-GG." msgid "Enter a valid time in HH:MM format." msgstr "Inserire un ora valida in formato OO:MM." -#: core/validators.py:165 db/models/fields/__init__.py:625 +#: core/validators.py:165 db/models/fields/__init__.py:631 msgid "Enter a valid date/time in YYYY-MM-DD HH:MM format." msgstr "Inserire una data/ora valida in formato AAAA-MM-GG OO:MM." -#: core/validators.py:170 newforms/fields.py:403 +#: core/validators.py:170 newforms/fields.py:408 msgid "Enter a valid e-mail address." msgstr "Inserire un indirizzo e-mail valido." -#: core/validators.py:182 core/validators.py:474 newforms/fields.py:433 -#: oldforms/__init__.py:687 +#: core/validators.py:182 core/validators.py:474 newforms/fields.py:426 msgid "No file was submitted. Check the encoding type on the form." msgstr "Non è stato inviato alcun file. Verificare il tipo di codifica della form." -#: core/validators.py:193 newforms/fields.py:459 +#: core/validators.py:193 newforms/fields.py:468 msgid "" "Upload a valid image. The file you uploaded was either not an image or a " "corrupted image." @@ -3876,38 +3883,38 @@ msgstr "" msgid "%(object)s with this %(type)s already exists for the given %(field)s." msgstr "Un %(object)s·con questo·%(type)s·esiste già per questo·%(field)s." -#: db/models/fields/__init__.py:54 +#: db/models/fields/__init__.py:52 #, python-format msgid "%(optname)s with this %(fieldname)s already exists." msgstr "Un %(optname)s·con questo·%(fieldname)s·esiste già." -#: db/models/fields/__init__.py:179 db/models/fields/__init__.py:348 -#: db/models/fields/__init__.py:780 db/models/fields/__init__.py:791 -#: newforms/fields.py:46 oldforms/__init__.py:374 +#: db/models/fields/__init__.py:182 db/models/fields/__init__.py:354 +#: db/models/fields/__init__.py:788 db/models/fields/__init__.py:799 +#: newforms/fields.py:51 oldforms/__init__.py:374 msgid "This field is required." msgstr "Questo campo è obbligatorio." -#: db/models/fields/__init__.py:448 +#: db/models/fields/__init__.py:454 msgid "This value must be an integer." msgstr "Questo valore deve essere un intero." -#: db/models/fields/__init__.py:487 +#: db/models/fields/__init__.py:493 msgid "This value must be either True or False." msgstr "Questo valore deve essere True o False." -#: db/models/fields/__init__.py:511 +#: db/models/fields/__init__.py:517 msgid "This field cannot be null." msgstr "Questo campo non può essere nullo." -#: db/models/fields/__init__.py:689 +#: db/models/fields/__init__.py:695 msgid "This value must be a decimal number." msgstr "Questo valore deve essere un numero decimale." -#: db/models/fields/__init__.py:800 +#: db/models/fields/__init__.py:808 msgid "Enter a valid filename." msgstr "Inserire un nome di file valido." -#: db/models/fields/__init__.py:981 +#: db/models/fields/__init__.py:999 msgid "This value must be either None, True or False." msgstr "Questo valore deve essere None, True o False." @@ -3916,118 +3923,118 @@ msgstr "Questo valore deve essere None, True o False." msgid "Please enter a valid %s." msgstr "Inserire un %s valido." -#: db/models/fields/related.py:721 +#: db/models/fields/related.py:756 msgid "Separate multiple IDs with commas." msgstr "Separare gli ID multipli con virgole." -#: db/models/fields/related.py:723 +#: db/models/fields/related.py:758 msgid "Hold down \"Control\", or \"Command\" on a Mac, to select more than one." msgstr "" "Tenere premuto \"Control\", o \"Command\" su Mac, per selezionarne più di " "uno." -#: db/models/fields/related.py:770 +#: db/models/fields/related.py:805 #, 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] "Inserire un ID valido per %(self)s. Il valore %(value)r non è valido." msgstr[1] "Inserire ID validi per %(self)s. I valori %(value)r non sono validi." -#: newforms/fields.py:47 +#: newforms/fields.py:52 msgid "Enter a valid value." msgstr "Inserire un valore valido." -#: newforms/fields.py:124 +#: newforms/fields.py:129 #, python-format msgid "Ensure this value has at most %(max)d characters (it has %(length)d)." msgstr "" "Assicurarsi che questo valore non contenga più di %(max)d caratteri (ne ha %" "(length)d)." -#: newforms/fields.py:125 +#: newforms/fields.py:130 #, python-format msgid "Ensure this value has at least %(min)d characters (it has %(length)d)." msgstr "" "Assicurarsi che questo valore contenga almeno %(min)d caratteri (ne ha %" "(length)d)." -#: newforms/fields.py:153 newforms/fields.py:182 newforms/fields.py:211 +#: newforms/fields.py:158 newforms/fields.py:187 newforms/fields.py:216 #, python-format msgid "Ensure this value is less than or equal to %s." msgstr "Assicurarsi che questo valore sia minore o uguale a %s." -#: newforms/fields.py:154 newforms/fields.py:183 newforms/fields.py:212 +#: newforms/fields.py:159 newforms/fields.py:188 newforms/fields.py:217 #, python-format msgid "Ensure this value is greater than or equal to %s." msgstr "Assicurarsi che questo valore sia maggiore o uguale a %s." -#: newforms/fields.py:181 newforms/fields.py:210 +#: newforms/fields.py:186 newforms/fields.py:215 msgid "Enter a number." msgstr "Inserire un numero." -#: newforms/fields.py:213 +#: newforms/fields.py:218 #, python-format msgid "Ensure that there are no more than %s digits in total." msgstr "Assicurarsi che non vi siano più di %s cifre in totale." -#: newforms/fields.py:214 +#: newforms/fields.py:219 #, python-format msgid "Ensure that there are no more than %s decimal places." msgstr "Assicurarsi che non vi siano più di %s cifre decimali." -#: newforms/fields.py:215 +#: newforms/fields.py:220 #, python-format msgid "Ensure that there are no more than %s digits before the decimal point." msgstr "Assicurarsi che non vi siano più di %s cifre prima della virgola." -#: newforms/fields.py:263 newforms/fields.py:751 +#: newforms/fields.py:268 newforms/fields.py:779 msgid "Enter a valid date." msgstr "Inserire una data valida." -#: newforms/fields.py:296 newforms/fields.py:752 +#: newforms/fields.py:301 newforms/fields.py:780 msgid "Enter a valid time." msgstr "Inserire un ora valida." -#: newforms/fields.py:335 +#: newforms/fields.py:340 msgid "Enter a valid date/time." msgstr "Inserire una coppia data/ora valida." -#: newforms/fields.py:434 +#: newforms/fields.py:427 msgid "No file was submitted." msgstr "Nessun file è stato inviato." -#: newforms/fields.py:435 oldforms/__init__.py:689 +#: newforms/fields.py:428 oldforms/__init__.py:693 msgid "The submitted file is empty." msgstr "Il file inviato è vuoto." -#: newforms/fields.py:497 +#: newforms/fields.py:522 msgid "Enter a valid URL." msgstr "Inserire una URL valida." -#: newforms/fields.py:498 +#: newforms/fields.py:523 msgid "This URL appears to be a broken link." msgstr "Questa URL non sembra funzionare." -#: newforms/fields.py:560 newforms/models.py:299 +#: newforms/fields.py:588 newforms/models.py:306 msgid "Select a valid choice. That choice is not one of the available choices." msgstr "" "Scegliere un'opzione valida. La scelta effettuata non compare tra quelle " "disponibili." -#: newforms/fields.py:599 +#: newforms/fields.py:627 #, python-format msgid "Select a valid choice. %(value)s is not one of the available choices." msgstr "Scegliere un'opzione valida. '%(value)s non compare tra quelle disponibili." -#: newforms/fields.py:600 newforms/fields.py:662 newforms/models.py:371 +#: newforms/fields.py:628 newforms/fields.py:690 newforms/models.py:373 msgid "Enter a list of values." msgstr "Inserire una lista di valori." -#: newforms/fields.py:780 +#: newforms/fields.py:808 msgid "Enter a valid IPv4 address." msgstr "Inserire un indirizzo IPv4 valido." -#: newforms/models.py:372 +#: newforms/models.py:374 #, python-format msgid "Select a valid choice. %s is not one of the available choices." msgstr "Scegliere un'opzione valida. '%s non compare tra quelle disponibili." @@ -4048,15 +4055,15 @@ msgstr "Non sono ammessi a capo manuali qui." msgid "Select a valid choice; '%(data)s' is not in %(choices)s." msgstr "Scegliere un'opzione valida; '%(data)s' non presente in %(choices)s." -#: oldforms/__init__.py:745 +#: oldforms/__init__.py:754 msgid "Enter a whole number between -32,768 and 32,767." msgstr "Inserire un numero intero compreso tra -32.768 e 32.767 ." -#: oldforms/__init__.py:755 +#: oldforms/__init__.py:764 msgid "Enter a positive number." msgstr "Inserire un numero positivo." -#: oldforms/__init__.py:765 +#: oldforms/__init__.py:774 msgid "Enter a whole number between 0 and 32,767." msgstr "Inserire un numero intero compreso tra 0 e 32.767 ." @@ -4290,7 +4297,7 @@ msgstr "Nov." msgid "Dec." msgstr "Dic." -#: utils/text.py:127 +#: utils/text.py:128 msgid "or" msgstr "o" @@ -4344,23 +4351,23 @@ msgstr "%(number)d %(type)s" msgid ", %(number)d %(type)s" msgstr ", %(number)d %(type)s" -#: utils/translation/trans_real.py:403 +#: utils/translation/trans_real.py:412 msgid "DATE_FORMAT" msgstr "j F Y" -#: utils/translation/trans_real.py:404 +#: utils/translation/trans_real.py:413 msgid "DATETIME_FORMAT" msgstr "j F Y, H:i" -#: utils/translation/trans_real.py:405 +#: utils/translation/trans_real.py:414 msgid "TIME_FORMAT" msgstr "H:i" -#: utils/translation/trans_real.py:421 +#: utils/translation/trans_real.py:430 msgid "YEAR_MONTH_FORMAT" msgstr "Y F" -#: utils/translation/trans_real.py:422 +#: utils/translation/trans_real.py:431 msgid "MONTH_DAY_FORMAT" msgstr "F j" diff --git a/django/core/files/uploadedfile.py b/django/core/files/uploadedfile.py index 9287a1bec8..7f515f94d4 100644 --- a/django/core/files/uploadedfile.py +++ b/django/core/files/uploadedfile.py @@ -193,7 +193,7 @@ class TemporaryUploadedFile(UploadedFile): """ Returns the full path of this file. """ - return self.name + return self._file.name # Most methods on this object get proxied to NamedTemporaryFile. # We can't directly subclass because NamedTemporaryFile is actually a diff --git a/django/core/management/__init__.py b/django/core/management/__init__.py index 819b19a366..5e01ccbbe9 100644 --- a/django/core/management/__init__.py +++ b/django/core/management/__init__.py @@ -134,6 +134,35 @@ class LaxOptionParser(OptionParser): """ def error(self, msg): pass + + def _process_args(self, largs, rargs, values): + """ + Overrides OptionParser._process_args to exclusively handle default + options and ignore args and other options. + + This overrides the behavior of the super class, which stop parsing + at the first unrecognized option. + """ + while rargs: + arg = rargs[0] + try: + if arg[0:2] == "--" and len(arg) > 2: + # process a single long option (possibly with value(s)) + # the superclass code pops the arg off rargs + self._process_long_opt(rargs, values) + elif arg[:1] == "-" and len(arg) > 1: + # process a cluster of short options (possibly with + # value(s) for the last one only) + # the superclass code pops the arg off rargs + self._process_short_opts(rargs, values) + else: + # it's either a non-default option or an arg + # either way, add it to the args list so we can keep + # dealing with options + del rargs[0] + raise error + except: + largs.append(arg) class ManagementUtility(object): """ diff --git a/django/core/management/commands/syncdb.py b/django/core/management/commands/syncdb.py index 91e78c96e8..3792a312d7 100644 --- a/django/core/management/commands/syncdb.py +++ b/django/core/management/commands/syncdb.py @@ -41,10 +41,11 @@ class Command(NoArgsCommand): # but raises an ImportError for some reason. The only way we # can do this is to check the text of the exception. Note that # we're a bit broad in how we check the text, because different - # Python implementations may not use the same text. CPython - # uses the text "No module named management". + # Python implementations may not use the same text. + # CPython uses the text "No module named management" + # PyPy uses "No module named myproject.myapp.management" msg = exc.args[0] - if not msg.startswith('No module named management') or 'management' not in msg: + if not msg.startswith('No module named') or 'management' not in msg: raise cursor = connection.cursor() @@ -105,7 +106,10 @@ class Command(NoArgsCommand): # Send the post_syncdb signal, so individual apps can do whatever they need # to do at this point. emit_post_sync_signal(created_models, verbosity, interactive) - + + # The connection may have been closed by a syncdb handler. + cursor = connection.cursor() + # Install custom SQL for the app (but only if this # is a model we've just created) for app in models.get_apps(): @@ -144,7 +148,7 @@ class Command(NoArgsCommand): for sql in index_sql: cursor.execute(sql) except Exception, e: - sys.stderr.write("Failed to install index for %s.%s model: %s" % \ + sys.stderr.write("Failed to install index for %s.%s model: %s\n" % \ (app_name, model._meta.object_name, e)) transaction.rollback_unless_managed() else: diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index eed2f89c52..cb20ae51e2 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -314,7 +314,7 @@ class Field(object): params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_month))) if self.unique_for_year: params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_year))) - if self.unique or (self.primary_key and not rel): + if self.unique and not rel: params['validator_list'].append(curry(manipulator_validator_unique, self, opts, manipulator)) # Only add is_required=True if the field cannot be blank. Primary keys @@ -539,7 +539,7 @@ class DateField(Field): raise validators.ValidationError, _('Enter a valid date in YYYY-MM-DD format.') def get_db_prep_lookup(self, lookup_type, value): - if lookup_type == 'range': + if lookup_type in ('range', 'in'): value = [smart_unicode(v) for v in value] elif lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte') and hasattr(value, 'strftime'): value = value.strftime('%Y-%m-%d') @@ -626,7 +626,7 @@ class DateTimeField(DateField): return Field.get_db_prep_save(self, value) def get_db_prep_lookup(self, lookup_type, value): - if lookup_type == 'range': + if lookup_type in ('range', 'in'): value = [smart_unicode(v) for v in value] else: value = smart_unicode(value) @@ -705,7 +705,7 @@ class DecimalField(Field): return super(DecimalField, self).get_db_prep_save(value) def get_db_prep_lookup(self, lookup_type, value): - if lookup_type == 'range': + if lookup_type in ('range', 'in'): value = [self._format(v) for v in value] else: value = self._format(value) @@ -820,12 +820,14 @@ class FileField(Field): def save_file(self, new_data, new_object, original_object, change, rel, save=True): upload_field_name = self.get_manipulator_field_names('')[0] if new_data.get(upload_field_name, False): - func = getattr(new_object, 'save_%s_file' % self.name) if rel: file = new_data[upload_field_name][0] else: file = new_data[upload_field_name] + if not file: + return + # Backwards-compatible support for files-as-dictionaries. # We don't need to raise a warning because Model._save_FIELD_file will # do so for us. @@ -834,6 +836,7 @@ class FileField(Field): except AttributeError: file_name = file['filename'] + func = getattr(new_object, 'save_%s_file' % self.name) func(file_name, file, save) def get_directory_name(self): @@ -1086,7 +1089,7 @@ class TimeField(Field): return smart_unicode(value) else: prep = smart_unicode - if lookup_type == 'range': + if lookup_type in ('range', 'in'): value = [prep(v) for v in value] else: value = prep(value) diff --git a/django/db/models/query.py b/django/db/models/query.py index 986846fc3a..b1921a8e4b 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -1,13 +1,17 @@ import warnings +try: + set +except NameError: + from sets import Set as set # Python 2.3 fallback -from django.conf import settings from django.db import connection, transaction, IntegrityError -from django.db.models.fields import DateField, FieldDoesNotExist +from django.db.models.fields import DateField from django.db.models.query_utils import Q, select_related_descend from django.db.models import signals, sql from django.dispatch import dispatcher from django.utils.datastructures import SortedDict + # Used to control how many objects are worked with at once in some cases (e.g. # when deleting objects). CHUNK_SIZE = 100 @@ -16,7 +20,12 @@ ITER_CHUNK_SIZE = CHUNK_SIZE # Pull into this namespace for backwards compatibility. EmptyResultSet = sql.EmptyResultSet + class CyclicDependency(Exception): + """ + An error when dealing with a collection of objects that have a cyclic + dependency, i.e. when deleting multiple objects. + """ pass diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index 7944d0358f..f682c71d07 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -283,12 +283,11 @@ class Query(object): if ordering: result.append('ORDER BY %s' % ', '.join(ordering)) - # FIXME: Pull this out to make life easier for Oracle et al. if with_limits: - if self.high_mark: + if self.high_mark is not None: result.append('LIMIT %d' % (self.high_mark - self.low_mark)) if self.low_mark: - if not self.high_mark: + if self.high_mark is None: val = self.connection.ops.no_limit_value() if val: result.append('LIMIT %d' % val) @@ -1381,12 +1380,12 @@ class Query(object): constraints. So low is added to the current low value and both will be clamped to any existing high value. """ - if high: + if high is not None: if self.high_mark: self.high_mark = min(self.high_mark, self.low_mark + high) else: self.high_mark = self.low_mark + high - if low: + if low is not None: if self.high_mark: self.low_mark = min(self.high_mark, self.low_mark + low) else: diff --git a/django/http/multipartparser.py b/django/http/multipartparser.py index fc48aa9e7b..fa1ae11968 100644 --- a/django/http/multipartparser.py +++ b/django/http/multipartparser.py @@ -270,24 +270,9 @@ class LazyStream(object): self._empty = False self._leftover = '' self.length = length - self._position = 0 + self.position = 0 self._remaining = length - - # These fields are to do sanity checking to make sure we don't - # have infinite loops getting/ungetting from the stream. The - # purpose overall is to raise an exception if we perform lots - # of stream get/unget gymnastics without getting - # anywhere. Naturally this is not sound, but most probably - # would indicate a bug if the exception is raised. - - # largest position tell us how far this lazystream has ever - # been advanced - self._largest_position = 0 - - # "modifications since" will start at zero and increment every - # time the position is modified but a new largest position is - # not achieved. - self._modifications_since = 0 + self._unget_history = [] def tell(self): return self.position @@ -329,6 +314,7 @@ class LazyStream(object): self._leftover = '' else: output = self._producer.next() + self._unget_history = [] self.position += len(output) return output @@ -351,25 +337,30 @@ class LazyStream(object): Future calls to read() will return those bytes first. The stream position and thus tell() will be rewound. """ + if not bytes: + return + self._update_unget_history(len(bytes)) self.position -= len(bytes) self._leftover = ''.join([bytes, self._leftover]) - def _set_position(self, value): - if value > self._largest_position: - self._modifications_since = 0 - self._largest_position = value - else: - self._modifications_since += 1 - if self._modifications_since > 500: - raise SuspiciousOperation( - "The multipart parser got stuck, which shouldn't happen with" - " normal uploaded files. Check for malicious upload activity;" - " if there is none, report this to the Django developers." - ) + def _update_unget_history(self, num_bytes): + """ + Updates the unget history as a sanity check to see if we've pushed + back the same number of bytes in one chunk. If we keep ungetting the + same number of bytes many times (here, 50), we're mostly likely in an + infinite loop of some sort. This is usually caused by a + maliciously-malformed MIME request. + """ + self._unget_history = [num_bytes] + self._unget_history[:49] + number_equal = len([current_number for current_number in self._unget_history + if current_number == num_bytes]) - self._position = value - - position = property(lambda self: self._position, _set_position) + if number_equal > 40: + raise SuspiciousOperation( + "The multipart parser got stuck, which shouldn't happen with" + " normal uploaded files. Check for malicious upload activity;" + " if there is none, report this to the Django developers." + ) class ChunkIter(object): """ diff --git a/django/newforms/fields.py b/django/newforms/fields.py index ad46d78859..bfff9fe8b0 100644 --- a/django/newforms/fields.py +++ b/django/newforms/fields.py @@ -507,6 +507,8 @@ class ImageField(FileField): trial_image.verify() except Exception: # Python Imaging Library doesn't recognize it as an image raise ValidationError(self.error_messages['invalid_image']) + if hasattr(f, 'seek') and callable(f.seek): + f.seek(0) return f url_re = re.compile( diff --git a/django/utils/itercompat.py b/django/utils/itercompat.py index 3742d6c5d8..c166da35b8 100644 --- a/django/utils/itercompat.py +++ b/django/utils/itercompat.py @@ -67,3 +67,8 @@ def is_iterable(x): else: return True +def sorted(in_value): + "A naive implementation of sorted" + out_value = in_value[:] + out_value.sort() + return out_value diff --git a/docs/contributing.txt b/docs/contributing.txt index 61b24f4705..f3bee14069 100644 --- a/docs/contributing.txt +++ b/docs/contributing.txt @@ -738,6 +738,11 @@ If you're using another backend: deleted when the tests are finished. This means your user account needs permission to execute ``CREATE DATABASE``. +You will also need to ensure that your database uses UTF-8 as the default +character set. If your database server doesn't use UTF-8 as a default charset, +you will need to include a value for ``TEST_DATABASE_CHARSET`` in your settings +file. + If you want to run the full suite of tests, you'll need to install a number of dependencies: diff --git a/docs/db-api.txt b/docs/db-api.txt index 9a604bf320..5fdcd946bd 100644 --- a/docs/db-api.txt +++ b/docs/db-api.txt @@ -2212,6 +2212,18 @@ updated is that it can only access one database table, the model's main table. So don't try to filter based on related fields or anything like that; it won't work. +Be aware that the ``update()`` method is converted directly to an SQL +statement. It is a bulk operation for direct updates. It doesn't run any +``save()`` methods on your models, or emit the ``pre_save`` or ``post_save`` +signals (which are a consequence of calling ``save()``). If you want to save +every item in a ``QuerySet`` and make sure that the ``save()`` method is +called on each instance, you don't need any special function to handle that. +Just loop over them and call ``save()``: + + for item in my_queryset: + item.save() + + Extra instance methods ====================== diff --git a/docs/testing.txt b/docs/testing.txt index 0b18545efb..bb091bfd6b 100644 --- a/docs/testing.txt +++ b/docs/testing.txt @@ -89,7 +89,7 @@ read Python's official documentation for the details. For example, this function has a docstring that describes what it does:: def add_two(num): - "Adds 2 to the given number and returns the result." + "Return the result of adding two to the provided number." return num + 2 Because tests often make great documentation, putting tests directly in @@ -600,8 +600,6 @@ Specifically, a ``Response`` object has the following attributes: ``context`` will be a list of ``Context`` objects, in the order in which they were rendered. - ``headers`` The HTTP headers of the response. This is a dictionary. - ``request`` The request data that stimulated the response. ``status_code`` The HTTP status of the response, as an integer. See @@ -619,6 +617,10 @@ Specifically, a ``Response`` object has the following attributes: which they were rendered. =============== ========================================================== +You can also use dictionary syntax on the response object to query the value +of any settings in the HTTP headers. For example, you could determine the +content type of a response using ``response['Content-Type']``. + .. _RFC2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html .. _template inheritance: ../templates/#template-inheritance diff --git a/docs/tutorial01.txt b/docs/tutorial01.txt index 04863cc7fd..9e765b1a9b 100644 --- a/docs/tutorial01.txt +++ b/docs/tutorial01.txt @@ -146,7 +146,7 @@ database's connection parameters: * ``DATABASE_ENGINE`` -- Either 'postgresql_psycopg2', 'mysql' or 'sqlite3'. Other backends are `also available`_. * ``DATABASE_NAME`` -- The name of your database, or the full (absolute) - path to the database file if you're using SQLite. + path to the database file if you're using SQLite. * ``DATABASE_USER`` -- Your database username (not used for SQLite). * ``DATABASE_PASSWORD`` -- Your database password (not used for SQLite). * ``DATABASE_HOST`` -- The host your database is on. Leave this as an @@ -161,6 +161,9 @@ database's connection parameters: this point. Do that with "``CREATE DATABASE database_name;``" within your database's interactive prompt. + If you're using SQLite, you don't need to create anything beforehand - the + database file will be created automatically when it is needed. + While you're editing ``settings.py``, take note of the ``INSTALLED_APPS`` setting towards the bottom of the file. That variable holds the names of all Django applications that are activated in this Django instance. Apps can be diff --git a/tests/modeltests/basic/models.py b/tests/modeltests/basic/models.py index c3ad38d661..835c5c90cf 100644 --- a/tests/modeltests/basic/models.py +++ b/tests/modeltests/basic/models.py @@ -4,12 +4,18 @@ This is a basic model with only two non-primary-key fields. """ - +# Python 2.3 doesn't have set as a builtin try: set except NameError: from sets import Set as set +# Python 2.3 doesn't have sorted() +try: + sorted +except NameError: + from django.utils.itercompat import sorted + from django.db import models class Article(models.Model): diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py index 289dcb42a6..6838f11d4e 100644 --- a/tests/modeltests/model_forms/models.py +++ b/tests/modeltests/model_forms/models.py @@ -900,7 +900,7 @@ u'...test3.txt' ... class Meta: ... model = ImageFile ->>> image_data = open(os.path.join(os.path.dirname(__file__), "test.png")).read() +>>> image_data = open(os.path.join(os.path.dirname(__file__), "test.png"), 'rb').read() >>> f = ImageFileForm(data={'description': u'An image'}, files={'image': SimpleUploadedFile('test.png', image_data)}) >>> f.is_valid() diff --git a/tests/modeltests/or_lookups/models.py b/tests/modeltests/or_lookups/models.py index 22bada07b1..6e56095d7c 100644 --- a/tests/modeltests/or_lookups/models.py +++ b/tests/modeltests/or_lookups/models.py @@ -8,6 +8,11 @@ Alternatively, use positional arguments, and pass one or more expressions of clauses using the variable ``django.db.models.Q`` (or any object with an add_to_query method). """ +# Python 2.3 doesn't have sorted() +try: + sorted +except NameError: + from django.utils.itercompat import sorted from django.db import models diff --git a/tests/modeltests/test_client/models.py b/tests/modeltests/test_client/models.py index 1a6e1bdc18..3797bf2d52 100644 --- a/tests/modeltests/test_client/models.py +++ b/tests/modeltests/test_client/models.py @@ -70,7 +70,13 @@ class ClientTest(TestCase): self.assertEqual(response.context['data'], '37') self.assertEqual(response.template.name, 'POST Template') self.failUnless('Data received' in response.content) - + + def test_response_headers(self): + "Check the value of HTTP headers returned in a response" + response = self.client.get("/test_client/header_view/") + + self.assertEquals(response['X-DJANGO-TEST'], 'Slartibartfast') + def test_raw_post(self): "POST raw data (with a content type) to a view" test_doc = """BlinkMalcolm Gladwell""" diff --git a/tests/modeltests/test_client/urls.py b/tests/modeltests/test_client/urls.py index 09ee7eaf34..0e511d7360 100644 --- a/tests/modeltests/test_client/urls.py +++ b/tests/modeltests/test_client/urls.py @@ -5,6 +5,7 @@ import views urlpatterns = patterns('', (r'^get_view/$', views.get_view), (r'^post_view/$', views.post_view), + (r'^header_view/$', views.view_with_header), (r'^raw_post_view/$', views.raw_post_view), (r'^redirect_view/$', views.redirect_view), (r'^permanent_redirect_view/$', redirect_to, { 'url': '/test_client/get_view/' }), diff --git a/tests/modeltests/test_client/views.py b/tests/modeltests/test_client/views.py index 3f4a54c5bd..f4eab6462d 100644 --- a/tests/modeltests/test_client/views.py +++ b/tests/modeltests/test_client/views.py @@ -32,6 +32,12 @@ def post_view(request): return HttpResponse(t.render(c)) +def view_with_header(request): + "A view that has a custom header" + response = HttpResponse() + response['X-DJANGO-TEST'] = 'Slartibartfast' + return response + def raw_post_view(request): """A view which expects raw XML to be posted and returns content extracted from the XML""" diff --git a/tests/regressiontests/admin_scripts/management/commands/app_command.py b/tests/regressiontests/admin_scripts/management/commands/app_command.py index f72e912ac0..3d8c43755c 100644 --- a/tests/regressiontests/admin_scripts/management/commands/app_command.py +++ b/tests/regressiontests/admin_scripts/management/commands/app_command.py @@ -1,4 +1,9 @@ from django.core.management.base import AppCommand +# Python 2.3 doesn't have sorted() +try: + sorted +except NameError: + from django.utils.itercompat import sorted class Command(AppCommand): help = 'Test Application-based commands' diff --git a/tests/regressiontests/admin_scripts/management/commands/base_command.py b/tests/regressiontests/admin_scripts/management/commands/base_command.py index 0187a23b29..536f40409a 100644 --- a/tests/regressiontests/admin_scripts/management/commands/base_command.py +++ b/tests/regressiontests/admin_scripts/management/commands/base_command.py @@ -1,6 +1,17 @@ from django.core.management.base import BaseCommand +from optparse import make_option +# Python 2.3 doesn't have sorted() +try: + sorted +except NameError: + from django.utils.itercompat import sorted class Command(BaseCommand): + option_list = BaseCommand.option_list + ( + make_option('--option_a','-a', action='store', dest='option_a', default='1'), + make_option('--option_b','-b', action='store', dest='option_b', default='2'), + make_option('--option_c','-c', action='store', dest='option_c', default='3'), + ) help = 'Test basic commands' requires_model_validation = False args = '[labels ...]' diff --git a/tests/regressiontests/admin_scripts/management/commands/label_command.py b/tests/regressiontests/admin_scripts/management/commands/label_command.py index 2b735c8e60..e749209d9c 100644 --- a/tests/regressiontests/admin_scripts/management/commands/label_command.py +++ b/tests/regressiontests/admin_scripts/management/commands/label_command.py @@ -1,4 +1,9 @@ from django.core.management.base import LabelCommand +# Python 2.3 doesn't have sorted() +try: + sorted +except NameError: + from django.utils.itercompat import sorted class Command(LabelCommand): help = "Test Label-based commands" diff --git a/tests/regressiontests/admin_scripts/management/commands/noargs_command.py b/tests/regressiontests/admin_scripts/management/commands/noargs_command.py index 683eb7a62c..f0f418752a 100644 --- a/tests/regressiontests/admin_scripts/management/commands/noargs_command.py +++ b/tests/regressiontests/admin_scripts/management/commands/noargs_command.py @@ -1,4 +1,9 @@ from django.core.management.base import NoArgsCommand +# Python 2.3 doesn't have sorted() +try: + sorted +except NameError: + from django.utils.itercompat import sorted class Command(NoArgsCommand): help = "Test No-args commands" diff --git a/tests/regressiontests/admin_scripts/tests.py b/tests/regressiontests/admin_scripts/tests.py index 441ce10d07..3ebcfbb6cd 100644 --- a/tests/regressiontests/admin_scripts/tests.py +++ b/tests/regressiontests/admin_scripts/tests.py @@ -7,7 +7,7 @@ import os import unittest import shutil -from django import conf, bin +from django import conf, bin, get_version from django.conf import settings class AdminScriptTestCase(unittest.TestCase): @@ -29,7 +29,7 @@ class AdminScriptTestCase(unittest.TestCase): settings_file.write("%s = '%s'\n" % (s, str(getattr(settings,s)))) if apps is None: - apps = ['django.contrib.auth', 'django.contrib.contenttypes', 'regressiontests.admin_scripts'] + apps = ['django.contrib.auth', 'django.contrib.contenttypes', 'admin_scripts'] if apps: settings_file.write("INSTALLED_APPS = %s\n" % apps) @@ -53,7 +53,7 @@ class AdminScriptTestCase(unittest.TestCase): # Build the command line cmd = 'python "%s"' % script - cmd += ''.join(' %s' % arg for arg in args) + cmd += ''.join([' %s' % arg for arg in args]) # Remember the old environment old_django_settings_module = os.environ.get('DJANGO_SETTINGS_MODULE', None) @@ -66,8 +66,8 @@ class AdminScriptTestCase(unittest.TestCase): elif 'DJANGO_SETTINGS_MODULE' in os.environ: del os.environ['DJANGO_SETTINGS_MODULE'] - os.environ['PYTHONPATH'] = os.pathsep.join([project_dir,base_dir]) - + os.environ['PYTHONPATH'] = os.pathsep.join([test_dir,base_dir]) + # Move to the test directory and run os.chdir(test_dir) stdin, stdout, stderr = os.popen3(cmd) @@ -129,17 +129,17 @@ class DjangoAdminNoSettings(AdminScriptTestCase): def test_builtin_with_bad_settings(self): "no settings: django-admin builtin commands fail if settings file (from argument) doesn't exist" - args = ['sqlall','--settings=regressiontests.bad_settings', 'admin_scripts'] + args = ['sqlall','--settings=bad_settings', 'admin_scripts'] out, err = self.run_django_admin(args) self.assertNoOutput(out) - self.assertOutput(err, "Could not import settings 'regressiontests.bad_settings'") + self.assertOutput(err, "Could not import settings 'bad_settings'") def test_builtin_with_bad_environment(self): "no settings: django-admin builtin commands fail if settings file (from environment) doesn't exist" args = ['sqlall','admin_scripts'] - out, err = self.run_django_admin(args,'regressiontests.bad_settings') + out, err = self.run_django_admin(args,'bad_settings') self.assertNoOutput(out) - self.assertOutput(err, "Could not import settings 'regressiontests.bad_settings'") + self.assertOutput(err, "Could not import settings 'bad_settings'") class DjangoAdminDefaultSettings(AdminScriptTestCase): @@ -161,7 +161,7 @@ class DjangoAdminDefaultSettings(AdminScriptTestCase): def test_builtin_with_settings(self): "default: django-admin builtin commands succeed if settings are provided as argument" - args = ['sqlall','--settings=regressiontests.settings', 'admin_scripts'] + args = ['sqlall','--settings=settings', 'admin_scripts'] out, err = self.run_django_admin(args) self.assertNoOutput(err) self.assertOutput(out, 'CREATE TABLE') @@ -169,23 +169,23 @@ class DjangoAdminDefaultSettings(AdminScriptTestCase): def test_builtin_with_environment(self): "default: django-admin builtin commands succeed if settings are provided in the environment" args = ['sqlall','admin_scripts'] - out, err = self.run_django_admin(args,'regressiontests.settings') + out, err = self.run_django_admin(args,'settings') self.assertNoOutput(err) self.assertOutput(out, 'CREATE TABLE') def test_builtin_with_bad_settings(self): "default: django-admin builtin commands fail if settings file (from argument) doesn't exist" - args = ['sqlall','--settings=regressiontests.bad_settings', 'admin_scripts'] + args = ['sqlall','--settings=bad_settings', 'admin_scripts'] out, err = self.run_django_admin(args) self.assertNoOutput(out) - self.assertOutput(err, "Could not import settings 'regressiontests.bad_settings'") + self.assertOutput(err, "Could not import settings 'bad_settings'") def test_builtin_with_bad_environment(self): "default: django-admin builtin commands fail if settings file (from environment) doesn't exist" args = ['sqlall','admin_scripts'] - out, err = self.run_django_admin(args,'regressiontests.bad_settings') + out, err = self.run_django_admin(args,'bad_settings') self.assertNoOutput(out) - self.assertOutput(err, "Could not import settings 'regressiontests.bad_settings'") + self.assertOutput(err, "Could not import settings 'bad_settings'") def test_custom_command(self): "default: django-admin can't execute user commands" @@ -196,7 +196,7 @@ class DjangoAdminDefaultSettings(AdminScriptTestCase): def test_custom_command_with_settings(self): "default: django-admin can't execute user commands, even if settings are provided as argument" - args = ['noargs_command', '--settings=regressiontests.settings'] + args = ['noargs_command', '--settings=settings'] out, err = self.run_django_admin(args) self.assertNoOutput(out) self.assertOutput(err, "Unknown command: 'noargs_command'") @@ -204,7 +204,7 @@ class DjangoAdminDefaultSettings(AdminScriptTestCase): def test_custom_command_with_environment(self): "default: django-admin can't execute user commands, even if settings are provided in environment" args = ['noargs_command'] - out, err = self.run_django_admin(args,'regressiontests.settings') + out, err = self.run_django_admin(args,'settings') self.assertNoOutput(out) self.assertOutput(err, "Unknown command: 'noargs_command'") @@ -227,7 +227,7 @@ class DjangoAdminMinimalSettings(AdminScriptTestCase): def test_builtin_with_settings(self): "minimal: django-admin builtin commands fail if settings are provided as argument" - args = ['sqlall','--settings=regressiontests.settings', 'admin_scripts'] + args = ['sqlall','--settings=settings', 'admin_scripts'] out, err = self.run_django_admin(args) self.assertNoOutput(out) self.assertOutput(err, 'App with label admin_scripts could not be found') @@ -235,23 +235,23 @@ class DjangoAdminMinimalSettings(AdminScriptTestCase): def test_builtin_with_environment(self): "minimal: django-admin builtin commands fail if settings are provided in the environment" args = ['sqlall','admin_scripts'] - out, err = self.run_django_admin(args,'regressiontests.settings') + out, err = self.run_django_admin(args,'settings') self.assertNoOutput(out) self.assertOutput(err, 'App with label admin_scripts could not be found') def test_builtin_with_bad_settings(self): "minimal: django-admin builtin commands fail if settings file (from argument) doesn't exist" - args = ['sqlall','--settings=regressiontests.bad_settings', 'admin_scripts'] + args = ['sqlall','--settings=bad_settings', 'admin_scripts'] out, err = self.run_django_admin(args) self.assertNoOutput(out) - self.assertOutput(err, "Could not import settings 'regressiontests.bad_settings'") + self.assertOutput(err, "Could not import settings 'bad_settings'") def test_builtin_with_bad_environment(self): "minimal: django-admin builtin commands fail if settings file (from environment) doesn't exist" args = ['sqlall','admin_scripts'] - out, err = self.run_django_admin(args,'regressiontests.bad_settings') + out, err = self.run_django_admin(args,'bad_settings') self.assertNoOutput(out) - self.assertOutput(err, "Could not import settings 'regressiontests.bad_settings'") + self.assertOutput(err, "Could not import settings 'bad_settings'") def test_custom_command(self): "minimal: django-admin can't execute user commands" @@ -262,7 +262,7 @@ class DjangoAdminMinimalSettings(AdminScriptTestCase): def test_custom_command_with_settings(self): "minimal: django-admin can't execute user commands, even if settings are provided as argument" - args = ['noargs_command', '--settings=regressiontests.settings'] + args = ['noargs_command', '--settings=settings'] out, err = self.run_django_admin(args) self.assertNoOutput(out) self.assertOutput(err, "Unknown command: 'noargs_command'") @@ -270,7 +270,7 @@ class DjangoAdminMinimalSettings(AdminScriptTestCase): def test_custom_command_with_environment(self): "minimal: django-admin can't execute user commands, even if settings are provided in environment" args = ['noargs_command'] - out, err = self.run_django_admin(args,'regressiontests.settings') + out, err = self.run_django_admin(args,'settings') self.assertNoOutput(out) self.assertOutput(err, "Unknown command: 'noargs_command'") @@ -293,7 +293,7 @@ class DjangoAdminAlternateSettings(AdminScriptTestCase): def test_builtin_with_settings(self): "alternate: django-admin builtin commands succeed if settings are provided as argument" - args = ['sqlall','--settings=regressiontests.alternate_settings', 'admin_scripts'] + args = ['sqlall','--settings=alternate_settings', 'admin_scripts'] out, err = self.run_django_admin(args) self.assertNoOutput(err) self.assertOutput(out, 'CREATE TABLE') @@ -301,23 +301,23 @@ class DjangoAdminAlternateSettings(AdminScriptTestCase): def test_builtin_with_environment(self): "alternate: django-admin builtin commands succeed if settings are provided in the environment" args = ['sqlall','admin_scripts'] - out, err = self.run_django_admin(args,'regressiontests.alternate_settings') + out, err = self.run_django_admin(args,'alternate_settings') self.assertNoOutput(err) self.assertOutput(out, 'CREATE TABLE') def test_builtin_with_bad_settings(self): "alternate: django-admin builtin commands fail if settings file (from argument) doesn't exist" - args = ['sqlall','--settings=regressiontests.bad_settings', 'admin_scripts'] + args = ['sqlall','--settings=bad_settings', 'admin_scripts'] out, err = self.run_django_admin(args) self.assertNoOutput(out) - self.assertOutput(err, "Could not import settings 'regressiontests.bad_settings'") + self.assertOutput(err, "Could not import settings 'bad_settings'") def test_builtin_with_bad_environment(self): "alternate: django-admin builtin commands fail if settings file (from environment) doesn't exist" args = ['sqlall','admin_scripts'] - out, err = self.run_django_admin(args,'regressiontests.bad_settings') + out, err = self.run_django_admin(args,'bad_settings') self.assertNoOutput(out) - self.assertOutput(err, "Could not import settings 'regressiontests.bad_settings'") + self.assertOutput(err, "Could not import settings 'bad_settings'") def test_custom_command(self): "alternate: django-admin can't execute user commands" @@ -328,7 +328,7 @@ class DjangoAdminAlternateSettings(AdminScriptTestCase): def test_custom_command_with_settings(self): "alternate: django-admin can't execute user commands, even if settings are provided as argument" - args = ['noargs_command', '--settings=regressiontests.alternate_settings'] + args = ['noargs_command', '--settings=alternate_settings'] out, err = self.run_django_admin(args) self.assertNoOutput(out) self.assertOutput(err, "Unknown command: 'noargs_command'") @@ -336,7 +336,7 @@ class DjangoAdminAlternateSettings(AdminScriptTestCase): def test_custom_command_with_environment(self): "alternate: django-admin can't execute user commands, even if settings are provided in environment" args = ['noargs_command'] - out, err = self.run_django_admin(args,'regressiontests.alternate_settings') + out, err = self.run_django_admin(args,'alternate_settings') self.assertNoOutput(out) self.assertOutput(err, "Unknown command: 'noargs_command'") @@ -364,7 +364,7 @@ class DjangoAdminMultipleSettings(AdminScriptTestCase): def test_builtin_with_settings(self): "alternate: django-admin builtin commands succeed if settings are provided as argument" - args = ['sqlall','--settings=regressiontests.alternate_settings', 'admin_scripts'] + args = ['sqlall','--settings=alternate_settings', 'admin_scripts'] out, err = self.run_django_admin(args) self.assertNoOutput(err) self.assertOutput(out, 'CREATE TABLE') @@ -372,22 +372,22 @@ class DjangoAdminMultipleSettings(AdminScriptTestCase): def test_builtin_with_environment(self): "alternate: django-admin builtin commands succeed if settings are provided in the environment" args = ['sqlall','admin_scripts'] - out, err = self.run_django_admin(args,'regressiontests.alternate_settings') + out, err = self.run_django_admin(args,'alternate_settings') self.assertNoOutput(err) self.assertOutput(out, 'CREATE TABLE') def test_builtin_with_bad_settings(self): "alternate: django-admin builtin commands fail if settings file (from argument) doesn't exist" - args = ['sqlall','--settings=regressiontests.bad_settings', 'admin_scripts'] + args = ['sqlall','--settings=bad_settings', 'admin_scripts'] out, err = self.run_django_admin(args) - self.assertOutput(err, "Could not import settings 'regressiontests.bad_settings'") + self.assertOutput(err, "Could not import settings 'bad_settings'") def test_builtin_with_bad_environment(self): "alternate: django-admin builtin commands fail if settings file (from environment) doesn't exist" args = ['sqlall','admin_scripts'] - out, err = self.run_django_admin(args,'regressiontests.bad_settings') + out, err = self.run_django_admin(args,'bad_settings') self.assertNoOutput(out) - self.assertOutput(err, "Could not import settings 'regressiontests.bad_settings'") + self.assertOutput(err, "Could not import settings 'bad_settings'") def test_custom_command(self): "alternate: django-admin can't execute user commands" @@ -398,7 +398,7 @@ class DjangoAdminMultipleSettings(AdminScriptTestCase): def test_custom_command_with_settings(self): "alternate: django-admin can't execute user commands, even if settings are provided as argument" - args = ['noargs_command', '--settings=regressiontests.alternate_settings'] + args = ['noargs_command', '--settings=alternate_settings'] out, err = self.run_django_admin(args) self.assertNoOutput(out) self.assertOutput(err, "Unknown command: 'noargs_command'") @@ -406,7 +406,7 @@ class DjangoAdminMultipleSettings(AdminScriptTestCase): def test_custom_command_with_environment(self): "alternate: django-admin can't execute user commands, even if settings are provided in environment" args = ['noargs_command'] - out, err = self.run_django_admin(args,'regressiontests.alternate_settings') + out, err = self.run_django_admin(args,'alternate_settings') self.assertNoOutput(out) self.assertOutput(err, "Unknown command: 'noargs_command'") @@ -725,26 +725,62 @@ class CommandTypes(AdminScriptTestCase): def tearDown(self): self.remove_settings('settings.py') + def test_version(self): + "--version is handled as a special case" + args = ['--version'] + out, err = self.run_manage(args) + self.assertNoOutput(err) + # Only check the first part of the version number + self.assertOutput(out, get_version().split('-')[0]) + + def test_help(self): + "--help is handled as a special case" + args = ['--help'] + out, err = self.run_manage(args) + self.assertOutput(out, "Usage: manage.py [options]") + self.assertOutput(err, "Type 'manage.py help ' for help on a specific subcommand.") + + def test_specific_help(self): + "--help can be used on a specific command" + args = ['sqlall','--help'] + out, err = self.run_manage(args) + self.assertNoOutput(err) + self.assertOutput(out, "Prints the CREATE TABLE, custom SQL and CREATE INDEX SQL statements for the given model module name(s).") + def test_base_command(self): "User BaseCommands can execute when a label is provided" args = ['base_command','testlabel'] out, err = self.run_manage(args) self.assertNoOutput(err) - self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('pythonpath', None), ('settings', None), ('traceback', None)]") + self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', '1'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None)]") def test_base_command_no_label(self): "User BaseCommands can execute when no labels are provided" args = ['base_command'] out, err = self.run_manage(args) self.assertNoOutput(err) - self.assertOutput(out, "EXECUTE:BaseCommand labels=(), options=[('pythonpath', None), ('settings', None), ('traceback', None)]") + self.assertOutput(out, "EXECUTE:BaseCommand labels=(), options=[('option_a', '1'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None)]") def test_base_command_multiple_label(self): "User BaseCommands can execute when no labels are provided" args = ['base_command','testlabel','anotherlabel'] out, err = self.run_manage(args) self.assertNoOutput(err) - self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel', 'anotherlabel'), options=[('pythonpath', None), ('settings', None), ('traceback', None)]") + self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel', 'anotherlabel'), options=[('option_a', '1'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None)]") + + def test_base_command_with_option(self): + "User BaseCommands can execute with options when a label is provided" + args = ['base_command','testlabel','--option_a=x'] + out, err = self.run_manage(args) + self.assertNoOutput(err) + self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None)]") + + def test_base_command_with_options(self): + "User BaseCommands can execute with multiple options when a label is provided" + args = ['base_command','testlabel','-a','x','--option_b=y'] + out, err = self.run_manage(args) + self.assertNoOutput(err) + self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', 'y'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None)]") def test_noargs(self): "NoArg Commands can be executed" @@ -765,8 +801,9 @@ class CommandTypes(AdminScriptTestCase): out, err = self.run_manage(args) self.assertNoOutput(err) self.assertOutput(out, "EXECUTE:AppCommand app=, options=[('pythonpath', None), ('settings', None), ('traceback', None)]") - + self.assertOutput(out, os.sep.join(['django','contrib','auth','models.py'])) + self.assertOutput(out, "'>, options=[('pythonpath', None), ('settings', None), ('traceback', None)]") + def test_app_command_no_apps(self): "User AppCommands raise an error when no app name is provided" args = ['app_command'] @@ -781,7 +818,8 @@ class CommandTypes(AdminScriptTestCase): self.assertOutput(out, "EXECUTE:AppCommand app=, options=[('pythonpath', None), ('settings', None), ('traceback', None)]") self.assertOutput(out, "EXECUTE:AppCommand app=, options=[('pythonpath', None), ('settings', None), ('traceback', None)]") + self.assertOutput(out, os.sep.join(['django','contrib','contenttypes','models.py'])) + self.assertOutput(out, "'>, options=[('pythonpath', None), ('settings', None), ('traceback', None)]") def test_app_command_invalid_appname(self): "User AppCommands can execute when a single app name is provided" @@ -815,3 +853,56 @@ class CommandTypes(AdminScriptTestCase): self.assertNoOutput(err) self.assertOutput(out, "EXECUTE:LabelCommand label=testlabel, options=[('pythonpath', None), ('settings', None), ('traceback', None)]") self.assertOutput(out, "EXECUTE:LabelCommand label=anotherlabel, options=[('pythonpath', None), ('settings', None), ('traceback', None)]") + +class ArgumentOrder(AdminScriptTestCase): + """Tests for 2-stage argument parsing scheme. + + django-admin command arguments are parsed in 2 parts; the core arguments + (--settings, --traceback and --pythonpath) are parsed using a Lax parser. + This Lax parser ignores any unknown options. Then the full settings are + passed to the command parser, which extracts commands of interest to the + individual command. + """ + def setUp(self): + self.write_settings('settings.py', apps=['django.contrib.auth','django.contrib.contenttypes']) + self.write_settings('alternate_settings.py') + + def tearDown(self): + self.remove_settings('settings.py') + self.remove_settings('alternate_settings.py') + + def test_setting_then_option(self): + "Options passed after settings are correctly handled" + args = ['base_command','testlabel','--settings=alternate_settings','--option_a=x'] + out, err = self.run_manage(args) + self.assertNoOutput(err) + self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None)]") + + def test_setting_then_short_option(self): + "Short options passed after settings are correctly handled" + args = ['base_command','testlabel','--settings=alternate_settings','--option_a=x'] + out, err = self.run_manage(args) + self.assertNoOutput(err) + self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None)]") + + def test_option_then_setting(self): + "Options passed before settings are correctly handled" + args = ['base_command','testlabel','--option_a=x','--settings=alternate_settings'] + out, err = self.run_manage(args) + self.assertNoOutput(err) + self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None)]") + + def test_short_option_then_setting(self): + "Short options passed before settings are correctly handled" + args = ['base_command','testlabel','-a','x','--settings=alternate_settings'] + out, err = self.run_manage(args) + self.assertNoOutput(err) + self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None)]") + + def test_option_then_setting_then_option(self): + "Options are correctly handled when they are passed before and after a setting" + args = ['base_command','testlabel','--option_a=x','--settings=alternate_settings','--option_b=y'] + out, err = self.run_manage(args) + self.assertNoOutput(err) + self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', 'y'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None)]") + diff --git a/tests/regressiontests/defaultfilters/tests.py b/tests/regressiontests/defaultfilters/tests.py index b56c33a652..1b659a91f4 100644 --- a/tests/regressiontests/defaultfilters/tests.py +++ b/tests/regressiontests/defaultfilters/tests.py @@ -537,6 +537,12 @@ u'123' from django.template.defaultfilters import * import datetime +# Python 2.3 doesn't have sorted() +try: + sorted +except NameError: + from django.utils.itercompat import sorted + if __name__ == '__main__': import doctest doctest.testmod() diff --git a/tests/regressiontests/file_uploads/tests.py b/tests/regressiontests/file_uploads/tests.py index 8a61966240..d2b581686f 100644 --- a/tests/regressiontests/file_uploads/tests.py +++ b/tests/regressiontests/file_uploads/tests.py @@ -25,7 +25,7 @@ class FileUploadTests(TestCase): file2.seek(0) # This file contains chinese symbols for a name. - file3 = open(os.path.join(tdir, u'test_中文_Orl\u00e9ans.jpg'), 'w+b') + file3 = open(os.path.join(tdir, u'test_中文_Orl\u00e9ans.jpg'.encode('utf-8')), 'w+b') file3.write('b' * (2 ** 10)) file3.seek(0) diff --git a/tests/regressiontests/model_inheritance_regress/models.py b/tests/regressiontests/model_inheritance_regress/models.py index 24d6186150..b78b493e15 100644 --- a/tests/regressiontests/model_inheritance_regress/models.py +++ b/tests/regressiontests/model_inheritance_regress/models.py @@ -6,6 +6,12 @@ import datetime from django.db import models +# Python 2.3 doesn't have sorted() +try: + sorted +except NameError: + from django.utils.itercompat import sorted + class Place(models.Model): name = models.CharField(max_length=50) address = models.CharField(max_length=80) diff --git a/tests/regressiontests/queries/models.py b/tests/regressiontests/queries/models.py index 566411e513..65d0d6ec65 100644 --- a/tests/regressiontests/queries/models.py +++ b/tests/regressiontests/queries/models.py @@ -8,6 +8,12 @@ import pickle from django.db import models from django.db.models.query import Q +# Python 2.3 doesn't have sorted() +try: + sorted +except NameError: + from django.utils.itercompat import sorted + class Tag(models.Model): name = models.CharField(max_length=10) parent = models.ForeignKey('self', blank=True, null=True, @@ -805,5 +811,14 @@ Bug #7371 >>> Related.objects.order_by('custom') [] +Bug #7448, #7707 -- Complex objects should be converted to strings before being +used in lookups. +>>> Item.objects.filter(created__in=[time1, time2]) +[, ] + +Bug #7698 -- People like to slice with '0' as the high-water mark. +>>> Item.objects.all()[0:0] +[] + """} diff --git a/tests/regressiontests/utils/datastructures.py b/tests/regressiontests/utils/datastructures.py index 52bf81a9e0..5d31d21318 100644 --- a/tests/regressiontests/utils/datastructures.py +++ b/tests/regressiontests/utils/datastructures.py @@ -44,8 +44,15 @@ >>> d.keys() [2, 1] >>> real_dict = dict(tuples) ->>> real_dict.values() +>>> sorted(real_dict.values()) ['one', 'second-two'] ->>> d.values() +>>> d.values() # Here the order of SortedDict values *is* what we are testing ['second-two', 'one'] -""" \ No newline at end of file +""" + +# Python 2.3 doesn't have sorted() +try: + sorted +except NameError: + from django.utils.itercompat import sorted + \ No newline at end of file diff --git a/tests/regressiontests/utils/itercompat.py b/tests/regressiontests/utils/itercompat.py new file mode 100644 index 0000000000..ad79cffcd1 --- /dev/null +++ b/tests/regressiontests/utils/itercompat.py @@ -0,0 +1,15 @@ +""" +# Tests of the utils itercompat library. + +>>> from django.utils.itercompat import sorted as compat_sorted + +# Check the replacement version of sorted +>>> x = [5,1,4,2,3] +>>> y = compat_sorted(x) +>>> print y +[1, 2, 3, 4, 5] + +>>> print x +[5, 1, 4, 2, 3] + +""" \ No newline at end of file diff --git a/tests/regressiontests/utils/tests.py b/tests/regressiontests/utils/tests.py index 6fc645505b..cd4762e02f 100644 --- a/tests/regressiontests/utils/tests.py +++ b/tests/regressiontests/utils/tests.py @@ -8,12 +8,14 @@ from django.utils import html, checksums import timesince import datastructures +import itercompat from decorators import DecoratorFromMiddlewareTests # Extra tests __test__ = { 'timesince': timesince, 'datastructures': datastructures, + 'itercompat': itercompat, } class TestUtilsHtml(TestCase):