diff options
Diffstat (limited to 'django/utils')
| -rw-r--r-- | django/utils/cache.py | 9 | ||||
| -rw-r--r-- | django/utils/datastructures.py | 39 | ||||
| -rw-r--r-- | django/utils/feedgenerator.py | 8 | ||||
| -rw-r--r-- | django/utils/functional.py | 16 | ||||
| -rw-r--r-- | django/utils/html.py | 2 | ||||
| -rw-r--r-- | django/utils/httpwrappers.py | 273 | ||||
| -rw-r--r-- | django/utils/termcolors.py | 70 | ||||
| -rw-r--r-- | django/utils/text.py | 4 | ||||
| -rw-r--r-- | django/utils/timesince.py | 1 | ||||
| -rw-r--r-- | django/utils/translation.py | 30 |
10 files changed, 153 insertions, 299 deletions
diff --git a/django/utils/cache.py b/django/utils/cache.py index b888cef179..5eba302ebe 100644 --- a/django/utils/cache.py +++ b/django/utils/cache.py @@ -80,8 +80,17 @@ def patch_response_headers(response, cache_timeout=None): if not response.has_header('Expires'): expires = now + datetime.timedelta(0, cache_timeout) response['Expires'] = expires.strftime('%a, %d %b %Y %H:%M:%S GMT') + if cache_timeout < 0: + cache_timeout = 0 # Can't have max-age negative patch_cache_control(response, max_age=cache_timeout) +def add_never_cache_headers(response): + """ + Add headers to a response to indicate that + a page should never be cached. + """ + patch_response_headers(response, cache_timeout=-1) + def patch_vary_headers(response, newheaders): """ Adds (or updates) the "Vary" header in the given HttpResponse object. diff --git a/django/utils/datastructures.py b/django/utils/datastructures.py index 20aa30bcff..bc8fb07ef5 100644 --- a/django/utils/datastructures.py +++ b/django/utils/datastructures.py @@ -40,6 +40,43 @@ class MergeDict: return True return False +class SortedDict(dict): + "A dictionary that keeps its keys in the order in which they're inserted." + def __init__(self, data={}): + dict.__init__(self, data) + self.keyOrder = data.keys() + + def __setitem__(self, key, value): + dict.__setitem__(self, key, value) + if key not in self.keyOrder: + self.keyOrder.append(key) + + def __delitem__(self, key): + dict.__delitem__(self, key) + self.keyOrder.remove(key) + + def __iter__(self): + for k in self.keyOrder: + yield k + + def items(self): + return zip(self.keyOrder, self.values()) + + def keys(self): + return self.keyOrder[:] + + def values(self): + return [dict.__getitem__(self,k) for k in self.keyOrder] + + def update(self, dict): + for k, v in dict.items(): + self.__setitem__(k, v) + + def setdefault(self, key, default): + if key not in self.keyOrder: + self.keyOrder.append(key) + return dict.setdefault(self, key, default) + class MultiValueDictKeyError(KeyError): pass @@ -193,4 +230,4 @@ class DotExpandedDict(dict): try: current[bits[-1]] = v except TypeError: # Special-case if current isn't a dict. - current = {bits[-1]: v} + current = {bits[-1] : v} diff --git a/django/utils/feedgenerator.py b/django/utils/feedgenerator.py index 22db5abe7f..f7c25f2933 100644 --- a/django/utils/feedgenerator.py +++ b/django/utils/feedgenerator.py @@ -21,8 +21,6 @@ http://diveintomark.org/archives/2004/02/04/incompatible-rss from django.utils.xmlutils import SimplerXMLGenerator import datetime, re, time import email.Utils -from xml.dom import minidom -from xml.parsers.expat import ExpatError def rfc2822_date(date): return email.Utils.formatdate(time.mktime(date.timetuple())) @@ -158,9 +156,11 @@ class Rss201rev2Feed(RssFeed): handler.addQuickElement(u"description", item['description']) # Author information. - if item['author_email'] is not None and item['author_name'] is not None: - handler.addQuickElement(u"author", u"%s (%s)" % \ + if item["author_name"] and item["author_email"]: + handler.addQuickElement(u"author", "%s (%s)" % \ (item['author_email'], item['author_name'])) + elif item["author_email"]: + handler.addQuickElement(u"author", item["author_email"]) if item['pubdate'] is not None: handler.addQuickElement(u"pubDate", rfc2822_date(item['pubdate']).decode('ascii')) diff --git a/django/utils/functional.py b/django/utils/functional.py index 69aeb81850..d1514d5728 100644 --- a/django/utils/functional.py +++ b/django/utils/functional.py @@ -24,14 +24,14 @@ def lazy(func, *resultclasses): # the evaluation and store the result. Afterwards, the result # is delivered directly. So the result is memoized. def __init__(self, args, kw): - self.__func = func - self.__args = args - self.__kw = kw - self.__dispatch = {} - for resultclass in resultclasses: - self.__dispatch[resultclass] = {} - for (k, v) in resultclass.__dict__.items(): - setattr(self, k, self.__promise__(resultclass, k, v)) + self.__func = func + self.__args = args + self.__kw = kw + self.__dispatch = {} + for resultclass in resultclasses: + self.__dispatch[resultclass] = {} + for (k, v) in resultclass.__dict__.items(): + setattr(self, k, self.__promise__(resultclass, k, v)) def __promise__(self, klass, funcname, func): # Builds a wrapper around some magic method and registers that magic diff --git a/django/utils/html.py b/django/utils/html.py index 6c9779a156..a0d1e82dcf 100644 --- a/django/utils/html.py +++ b/django/utils/html.py @@ -25,7 +25,7 @@ def escape(html): "Returns the given HTML with ampersands, quotes and carets encoded" if not isinstance(html, basestring): html = str(html) - return html.replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"') + return html.replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"').replace("'", ''') def linebreaks(value): "Converts newlines into <p> and <br />s" diff --git a/django/utils/httpwrappers.py b/django/utils/httpwrappers.py deleted file mode 100644 index c059ff60a8..0000000000 --- a/django/utils/httpwrappers.py +++ /dev/null @@ -1,273 +0,0 @@ -from Cookie import SimpleCookie -from pprint import pformat -from urllib import urlencode -from django.utils.datastructures import MultiValueDict - -try: - # The mod_python version is more efficient, so try importing it first. - from mod_python.util import parse_qsl -except ImportError: - from cgi import parse_qsl - -class HttpRequest(object): # needs to be new-style class because subclasses define "property"s - "A basic HTTP request" - def __init__(self): - self.GET, self.POST, self.COOKIES, self.META, self.FILES = {}, {}, {}, {}, {} - self.path = '' - - def __repr__(self): - return '<HttpRequest\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' % \ - (pformat(self.GET), pformat(self.POST), pformat(self.COOKIES), - pformat(self.META)) - - def __getitem__(self, key): - for d in (self.POST, self.GET): - if d.has_key(key): - return d[key] - raise KeyError, "%s not found in either POST or GET" % key - - def has_key(self, key): - return self.GET.has_key(key) or self.POST.has_key(key) - - def get_full_path(self): - return '' - -def parse_file_upload(header_dict, post_data): - "Returns a tuple of (POST MultiValueDict, FILES MultiValueDict)" - import email, email.Message - from cgi import parse_header - raw_message = '\r\n'.join(['%s:%s' % pair for pair in header_dict.items()]) - raw_message += '\r\n\r\n' + post_data - msg = email.message_from_string(raw_message) - POST = MultiValueDict() - FILES = MultiValueDict() - for submessage in msg.get_payload(): - if isinstance(submessage, email.Message.Message): - name_dict = parse_header(submessage['Content-Disposition'])[1] - # name_dict is something like {'name': 'file', 'filename': 'test.txt'} for file uploads - # or {'name': 'blah'} for POST fields - # We assume all uploaded files have a 'filename' set. - if name_dict.has_key('filename'): - assert type([]) != type(submessage.get_payload()), "Nested MIME messages are not supported" - if not name_dict['filename'].strip(): - continue - # IE submits the full path, so trim everything but the basename. - # (We can't use os.path.basename because it expects Linux paths.) - filename = name_dict['filename'][name_dict['filename'].rfind("\\")+1:] - FILES.appendlist(name_dict['name'], { - 'filename': filename, - 'content-type': (submessage.has_key('Content-Type') and submessage['Content-Type'] or None), - 'content': submessage.get_payload(), - }) - else: - POST.appendlist(name_dict['name'], submessage.get_payload()) - return POST, FILES - -class QueryDict(MultiValueDict): - """A specialized MultiValueDict that takes a query string when initialized. - This is immutable unless you create a copy of it.""" - def __init__(self, query_string, mutable=False): - MultiValueDict.__init__(self) - self._mutable = True - for key, value in parse_qsl((query_string or ''), True): # keep_blank_values=True - self.appendlist(key, value) - self._mutable = mutable - - def _assert_mutable(self): - if not self._mutable: - raise AttributeError, "This QueryDict instance is immutable" - - def __setitem__(self, key, value): - self._assert_mutable() - MultiValueDict.__setitem__(self, key, value) - - def __copy__(self): - result = self.__class__('', mutable=True) - for key, value in dict.items(self): - dict.__setitem__(result, key, value) - return result - - def __deepcopy__(self, memo={}): - import copy - result = self.__class__('', mutable=True) - memo[id(self)] = result - for key, value in dict.items(self): - dict.__setitem__(result, copy.deepcopy(key, memo), copy.deepcopy(value, memo)) - return result - - def setlist(self, key, list_): - self._assert_mutable() - MultiValueDict.setlist(self, key, list_) - - def appendlist(self, key, value): - self._assert_mutable() - MultiValueDict.appendlist(self, key, value) - - def update(self, other_dict): - self._assert_mutable() - MultiValueDict.update(self, other_dict) - - def pop(self, key): - self._assert_mutable() - return MultiValueDict.pop(self, key) - - def popitem(self): - self._assert_mutable() - return MultiValueDict.popitem(self) - - def clear(self): - self._assert_mutable() - MultiValueDict.clear(self) - - def setdefault(self, *args): - self._assert_mutable() - return MultiValueDict.setdefault(self, *args) - - def copy(self): - "Returns a mutable copy of this object." - return self.__deepcopy__() - - def urlencode(self): - output = [] - for k, list_ in self.lists(): - output.extend([urlencode({k: v}) for v in list_]) - return '&'.join(output) - -def parse_cookie(cookie): - if cookie == '': - return {} - c = SimpleCookie() - c.load(cookie) - cookiedict = {} - for key in c.keys(): - cookiedict[key] = c.get(key).value - return cookiedict - -class HttpResponse(object): - "A basic HTTP response, with content and dictionary-accessed headers" - def __init__(self, content='', mimetype=None): - from django.conf import settings - self._charset = settings.DEFAULT_CHARSET - if not mimetype: - mimetype = "%s; charset=%s" % (settings.DEFAULT_CONTENT_TYPE, settings.DEFAULT_CHARSET) - if hasattr(content, '__iter__'): - self._iterator = content - self._is_string = False - else: - self._iterator = [content] - self._is_string = True - self.headers = {'Content-Type': mimetype} - self.cookies = SimpleCookie() - self.status_code = 200 - - def __str__(self): - "Full HTTP message, including headers" - return '\n'.join(['%s: %s' % (key, value) - for key, value in self.headers.items()]) \ - + '\n\n' + self.content - - def __setitem__(self, header, value): - self.headers[header] = value - - def __delitem__(self, header): - try: - del self.headers[header] - except KeyError: - pass - - def __getitem__(self, header): - return self.headers[header] - - def has_header(self, header): - "Case-insensitive check for a header" - header = header.lower() - for key in self.headers.keys(): - if key.lower() == header: - return True - return False - - def set_cookie(self, key, value='', max_age=None, expires=None, path='/', domain=None, secure=None): - self.cookies[key] = value - for var in ('max_age', 'path', 'domain', 'secure', 'expires'): - val = locals()[var] - if val is not None: - self.cookies[key][var.replace('_', '-')] = val - - def delete_cookie(self, key): - try: - self.cookies[key]['max_age'] = 0 - except KeyError: - pass - - def _get_content(self): - content = ''.join(self._iterator) - if isinstance(content, unicode): - content = content.encode(self._charset) - return content - - def _set_content(self, value): - self._iterator = [value] - self._is_string = True - - content = property(_get_content, _set_content) - - def _get_iterator(self): - "Output iterator. Converts data into client charset if necessary." - for chunk in self._iterator: - if isinstance(chunk, unicode): - chunk = chunk.encode(self._charset) - yield chunk - - iterator = property(_get_iterator) - - # The remaining methods partially implement the file-like object interface. - # See http://docs.python.org/lib/bltin-file-objects.html - def write(self, content): - if not self._is_string: - raise Exception, "This %s instance is not writable" % self.__class__ - self._iterator.append(content) - - def flush(self): - pass - - def tell(self): - if not self._is_string: - raise Exception, "This %s instance cannot tell its position" % self.__class__ - return sum([len(chunk) for chunk in self._iterator]) - -class HttpResponseRedirect(HttpResponse): - def __init__(self, redirect_to): - HttpResponse.__init__(self) - self['Location'] = redirect_to - self.status_code = 302 - -class HttpResponsePermanentRedirect(HttpResponse): - def __init__(self, redirect_to): - HttpResponse.__init__(self) - self['Location'] = redirect_to - self.status_code = 301 - -class HttpResponseNotModified(HttpResponse): - def __init__(self): - HttpResponse.__init__(self) - self.status_code = 304 - -class HttpResponseNotFound(HttpResponse): - def __init__(self, *args, **kwargs): - HttpResponse.__init__(self, *args, **kwargs) - self.status_code = 404 - -class HttpResponseForbidden(HttpResponse): - def __init__(self, *args, **kwargs): - HttpResponse.__init__(self, *args, **kwargs) - self.status_code = 403 - -class HttpResponseGone(HttpResponse): - def __init__(self, *args, **kwargs): - HttpResponse.__init__(self, *args, **kwargs) - self.status_code = 410 - -class HttpResponseServerError(HttpResponse): - def __init__(self, *args, **kwargs): - HttpResponse.__init__(self, *args, **kwargs) - self.status_code = 500 diff --git a/django/utils/termcolors.py b/django/utils/termcolors.py new file mode 100644 index 0000000000..3ce1d5bb6b --- /dev/null +++ b/django/utils/termcolors.py @@ -0,0 +1,70 @@ +""" +termcolors.py +""" + +import types + +color_names = ('black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white') +foreground = dict([(color_names[x], '3%s' % x) for x in range(8)]) +background = dict([(color_names[x], '4%s' % x) for x in range(8)]) +del color_names + +RESET = '0' +opt_dict = {'bold': '1', 'underscore': '4', 'blink': '5', 'reverse': '7', 'conceal': '8'} + +def colorize(text='', opts=(), **kwargs): + """ + Returns your text, enclosed in ANSI graphics codes. + + Depends on the keyword arguments 'fg' and 'bg', and the contents of + the opts tuple/list. + + Returns the RESET code if no parameters are given. + + Valid colors: + 'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white' + + Valid options: + 'bold' + 'underscore' + 'blink' + 'reverse' + 'conceal' + 'noreset' - string will not be auto-terminated with the RESET code + + Examples: + colorize('hello', fg='red', bg='blue', opts=('blink',)) + colorize() + colorize('goodbye', opts=('underscore',)) + print colorize('first line', fg='red', opts=('noreset',)) + print 'this should be red too' + print colorize('and so should this') + print 'this should not be red' + """ + text = str(text) + code_list = [] + if text == '' and len(opts) == 1 and opts[0] == 'reset': + return '\x1b[%sm' % RESET + for k, v in kwargs.iteritems(): + if k == 'fg': + code_list.append(foreground[v]) + elif k == 'bg': + code_list.append(background[v]) + for o in opts: + if o in opt_dict: + code_list.append(opt_dict[o]) + if 'noreset' not in opts: + text = text + '\x1b[%sm' % RESET + return ('\x1b[%sm' % ';'.join(code_list)) + text + +def make_style(opts=(), **kwargs): + """ + Returns a function with default parameters for colorize() + + Example: + bold_red = make_style(opts=('bold',), fg='red') + print bold_red('hello') + KEYWORD = make_style(fg='yellow') + COMMENT = make_style(fg='blue', opts=('bold',)) + """ + return lambda text: colorize(text, opts, **kwargs) diff --git a/django/utils/text.py b/django/utils/text.py index ce1225b83c..7b6e1182ab 100644 --- a/django/utils/text.py +++ b/django/utils/text.py @@ -1,6 +1,6 @@ import re -from django.conf.settings import DEFAULT_CHARSET +from django.conf import settings # Capitalizes the first letter of a string. capfirst = lambda x: x and x[0].upper() + x[1:] @@ -100,7 +100,7 @@ def javascript_quote(s): return r"\u%04x" % ord(match.group(1)) if type(s) == str: - s = s.decode(DEFAULT_CHARSET) + s = s.decode(settings.DEFAULT_CHARSET) elif type(s) != unicode: raise TypeError, s s = s.replace('\\', '\\\\') diff --git a/django/utils/timesince.py b/django/utils/timesince.py index a16616584b..bc4f969dc4 100644 --- a/django/utils/timesince.py +++ b/django/utils/timesince.py @@ -11,6 +11,7 @@ def timesince(d, now=None): chunks = ( (60 * 60 * 24 * 365, lambda n: ngettext('year', 'years', n)), (60 * 60 * 24 * 30, lambda n: ngettext('month', 'months', n)), + (60 * 60 * 24 * 7, lambda n : ngettext('week', 'weeks', n)), (60 * 60 * 24, lambda n : ngettext('day', 'days', n)), (60 * 60, lambda n: ngettext('hour', 'hours', n)), (60, lambda n: ngettext('minute', 'minutes', n)) diff --git a/django/utils/translation.py b/django/utils/translation.py index 56cd5426f0..a877f60009 100644 --- a/django/utils/translation.py +++ b/django/utils/translation.py @@ -115,7 +115,7 @@ def translation(language): if sys.version_info < (2, 4): klass = DjangoTranslation23 - globalpath = os.path.join(os.path.dirname(settings.__file__), 'locale') + globalpath = os.path.join(os.path.dirname(sys.modules[settings.__module__].__file__), 'locale') parts = settings.SETTINGS_MODULE.split('.') project = __import__(parts[0], {}, {}, []) @@ -209,8 +209,8 @@ def get_language(): except AttributeError: pass # If we don't have a real translation object, assume it's the default language. - from django.conf.settings import LANGUAGE_CODE - return LANGUAGE_CODE + from django.conf import settings + return settings.LANGUAGE_CODE def catalog(): """ @@ -275,7 +275,7 @@ def check_for_language(lang_code): only used for language codes from either the cookies or session. """ from django.conf import settings - globalpath = os.path.join(os.path.dirname(settings.__file__), 'locale') + globalpath = os.path.join(os.path.dirname(sys.modules[settings.__module__].__file__), 'locale') if gettext_module.find('django', globalpath, [to_locale(lang_code)]) is not None: return True else: @@ -289,7 +289,7 @@ def get_language_from_request(request): """ global _accepted from django.conf import settings - globalpath = os.path.join(os.path.dirname(settings.__file__), 'locale') + globalpath = os.path.join(os.path.dirname(sys.modules[settings.__module__].__file__), 'locale') supported = dict(settings.LANGUAGES) if hasattr(request, 'session'): @@ -346,16 +346,16 @@ def get_date_formats(): technical message ID to store date and time formats. If it doesn't contain one, the formats provided in the settings will be used. """ - from django.conf.settings import DATE_FORMAT, DATETIME_FORMAT, TIME_FORMAT + from django.conf import settings date_format = _('DATE_FORMAT') datetime_format = _('DATETIME_FORMAT') time_format = _('TIME_FORMAT') if date_format == 'DATE_FORMAT': - date_format = DATE_FORMAT + date_format = settings.DATE_FORMAT if datetime_format == 'DATETIME_FORMAT': - datetime_format = DATETIME_FORMAT + datetime_format = settings.DATETIME_FORMAT if time_format == 'TIME_FORMAT': - time_format = TIME_FORMAT + time_format = settings.TIME_FORMAT return (date_format, datetime_format, time_format) def install(): @@ -384,7 +384,7 @@ def templatize(src): does so by translating the Django translation tags into standard gettext function invocations. """ - from django.core.template import Lexer, TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK + from django.template import Lexer, TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK out = StringIO() intrans = False inplural = False @@ -457,3 +457,13 @@ def templatize(src): else: out.write(blankout(t.contents, 'X')) return out.getvalue() + +def string_concat(*strings): + """" + lazy variant of string concatenation, needed for translations that are + constructed from multiple parts. Handles lazy strings and non-strings by + first turning all arguments to strings, before joining them. + """ + return ''.join([str(el) for el in strings]) + +string_concat = lazy(string_concat, str) |
