diff options
author | Ian Bicking <ianb@colorstudy.com> | 2010-06-21 01:52:27 -0500 |
---|---|---|
committer | Ian Bicking <ianb@colorstudy.com> | 2010-06-21 01:52:27 -0500 |
commit | e4639e8dbc11c7ab40acb85f34271c6d4515aa6e (patch) | |
tree | 915075a298834374e161c7a305b31c7a82c759b2 | |
parent | 6f90b846e02c3a6ae699b04d52ff7eb54ffeb873 (diff) | |
download | tempita-e4639e8dbc11c7ab40acb85f34271c6d4515aa6e.tar.gz |
a bunch of changes to make Python 3 compatibility easier
-rw-r--r-- | tempita/__init__.py | 109 | ||||
-rw-r--r-- | tempita/_looper.py | 24 | ||||
-rw-r--r-- | tempita/compat3.py | 43 |
3 files changed, 114 insertions, 62 deletions
diff --git a/tempita/__init__.py b/tempita/__init__.py index ebb624d..7048580 100644 --- a/tempita/__init__.py +++ b/tempita/__init__.py @@ -32,11 +32,12 @@ If there are syntax errors ``TemplateError`` will be raised. import re import sys import cgi -import urllib +from urllib import quote as url_quote import os import tokenize from cStringIO import StringIO from tempita._looper import looper +from tempita.compat3 import bytes, basestring_, next, is_unicode, coerce_text __all__ = ['TemplateError', 'Template', 'sub', 'HTMLTemplate', 'sub_html', 'html', 'bunch'] @@ -89,7 +90,7 @@ class Template(object): def __init__(self, content, name=None, namespace=None, stacklevel=None, get_template=None, default_inherit=None, line_offset=0): self.content = content - self._unicode = isinstance(content, unicode) + self._unicode = is_unicode(content) if name is None and stacklevel is not None: try: caller = sys._getframe(stacklevel) @@ -177,7 +178,7 @@ class Template(object): position=None, name=self.name) templ = self.get_template(inherit_template, self) self_ = TemplateObject(self.name) - for name, value in defs.items(): + for name, value in defs.iteritems(): setattr(self_, name, value) self_.body = body ns = ns.copy() @@ -187,7 +188,7 @@ class Template(object): def _interpret_codes(self, codes, ns, out, defs): __traceback_hide__ = True for item in codes: - if isinstance(item, basestring): + if isinstance(item, basestring_): out.append(item) else: self._interpret_code(item, ns, out, defs) @@ -258,7 +259,7 @@ class Template(object): __traceback_hide__ = True # @@: if/else/else gets through for part in parts: - assert not isinstance(part, basestring) + assert not isinstance(part, basestring_) name, pos = part[0], part[1] if name == 'else': result = True @@ -283,7 +284,7 @@ class Template(object): if getattr(e, 'args', None): arg0 = e.args[0] else: - arg0 = str(e) + arg0 = text(e) e.args = (self._add_line_info(arg0, pos),) raise exc_info[0], e, exc_info[2] @@ -309,14 +310,11 @@ class Template(object): try: value = unicode(value) except UnicodeDecodeError: - value = str(value) + value = bytes(value) else: - if not isinstance(value, basestring): - if hasattr(value, '__unicode__'): - value = unicode(value) - else: - value = str(value) - if (isinstance(value, unicode) + if not isinstance(value, basestring_): + value = coerce_text(value) + if (is_unicode(value) and self.default_encoding): value = value.encode(self.default_encoding) except: @@ -325,10 +323,10 @@ class Template(object): e.args = (self._add_line_info(e.args[0], pos),) raise exc_info[0], e, exc_info[2] else: - if self._unicode and isinstance(value, str): + if self._unicode and isinstance(value, bytes): if not self.default_encoding: raise UnicodeDecodeError( - 'Cannot decode str value %r into unicode ' + 'Cannot decode bytes value %r into unicode ' '(no default_encoding provided)' % value) try: value = value.decode(self.default_encoding) @@ -339,14 +337,13 @@ class Template(object): e.start, e.end, e.reason + ' in string %r' % value) - elif not self._unicode and isinstance(value, unicode): + elif not self._unicode and is_unicode(value): if not self.default_encoding: raise UnicodeEncodeError( - 'Cannot encode unicode value %r into str ' + 'Cannot encode unicode value %r into bytes ' '(no default_encoding provided)' % value) value = value.encode(self.default_encoding) return value - def _add_line_info(self, msg, pos): msg = "%s at line %s column %s" % ( @@ -367,7 +364,7 @@ def paste_script_template_renderer(content, vars, filename=None): class bunch(dict): def __init__(self, **kw): - for name, value in kw.items(): + for name, value in kw.iteritems(): setattr(self, name, value) def __setattr__(self, name, value): @@ -390,7 +387,7 @@ class bunch(dict): def __repr__(self): items = [ - (k, v) for k, v in self.items()] + (k, v) for k, v in self.iteritems()] items.sort() return '<%s %s>' % ( self.__class__.__name__, @@ -416,28 +413,26 @@ def html_quote(value, force=True): return value.__html__() if value is None: return '' - if not isinstance(value, basestring): - if hasattr(value, '__unicode__'): - value = unicode(value) - else: - value = str(value) - value = cgi.escape(value, 1) - if isinstance(value, unicode): - value = value.encode('ascii', 'xmlcharrefreplace') + if not isinstance(value, basestring_): + value = coerce_text(value) + if sys.version >= "3" and isinstance(value, bytes): + value = cgi.escape(value.decode('latin1'), 1) + value = value.encode('latin1') + else: + value = cgi.escape(value, 1) + if sys.version < "3": + if is_unicode(value): + value = value.encode('ascii', 'xmlcharrefreplace') return value def url(v): - if not isinstance(v, basestring): - if hasattr(v, '__unicode__'): - v = unicode(v) - else: - v = str(v) - if isinstance(v, unicode): + v = coerce_text(v) + if is_unicode(v): v = v.encode('utf8') - return urllib.quote(v) + return url_quote(v) def attr(**kw): - kw = kw.items() + kw = list(kw.iteritems()) kw.sort() parts = [] for name, value in kw: @@ -518,7 +513,7 @@ class TemplateDef(object): values = {} sig_args, var_args, var_kw, defaults = self._func_signature extra_kw = {} - for name, value in kw.items(): + for name, value in kw.iteritems(): if not var_kw and name not in sig_args: raise TypeError( 'Unexpected argument %s' % name) @@ -539,9 +534,9 @@ class TemplateDef(object): break else: raise TypeError( - 'Extra position arguments: %s' + 'Extra position arguments: %s' % ', '.join(repr(v) for v in args)) - for name, value_expr in defaults.items(): + for name, value_expr in defaults.iteritems(): if name not in values: values[name] = self._template._eval( value_expr, self._ns, self._pos) @@ -571,17 +566,25 @@ class TemplateObjectGetter(object): class _Empty(object): def __call__(self, *args, **kw): return self + def __str__(self): return '' + def __repr__(self): return 'Empty' + def __unicode__(self): return u'' + def __iter__(self): return iter(()) - def __nonzero__(self): + + def __bool__(self): return False + if sys.version < "3": + __nonzero__ = __bool__ + Empty = _Empty() del _Empty @@ -662,7 +665,7 @@ def trim_lex(tokens): """ for i in range(len(tokens)): current = tokens[i] - if isinstance(tokens[i], basestring): + if isinstance(tokens[i], basestring_): # we don't trim this continue item = current[0] @@ -676,8 +679,8 @@ def trim_lex(tokens): next = '' else: next = tokens[i+1] - if (not isinstance(next, basestring) - or not isinstance(prev, basestring)): + if (not isinstance(next, basestring_) + or not isinstance(prev, basestring_)): continue if ((not prev or trail_whitespace_re.search(prev) or (i == 1 and not prev.strip())) @@ -699,7 +702,7 @@ def trim_lex(tokens): next = next[m.end():] tokens[i+1] = next return tokens - + def find_position(string, index, line_offset): """Given a string and index, return (line, column)""" @@ -726,7 +729,7 @@ def parse(s, name=None, line_offset=0): [('cond', (1, 3), ('if', (1, 3), 'x', ['a']), ('elif', (1, 12), 'y', ['b']), ('else', (1, 23), None, ['c']))] Some exceptions:: - + >>> parse('{{continue}}') Traceback (most recent call last): ... @@ -764,7 +767,7 @@ def parse(s, name=None, line_offset=0): return result def parse_expr(tokens, name, context=()): - if isinstance(tokens[0], basestring): + if isinstance(tokens[0], basestring_): return tokens[0], tokens[1:] expr, pos = tokens[0] expr = expr.strip() @@ -855,7 +858,7 @@ def parse_one_cond(tokens, name, context): return part, tokens next, tokens = parse_expr(tokens, name, context) content.append(next) - + def parse_for(tokens, name, context): first, pos = tokens[0] tokens = tokens[1:] @@ -956,7 +959,7 @@ def parse_signature(sig_text, name, pos): def get_token(pos=False): try: - tok_type, tok_string, (srow, scol), (erow, ecol), line = tokens.next() + tok_type, tok_string, (srow, scol), (erow, ecol), line = next(tokens) except StopIteration: return tokenize.ENDMARKER, '' if pos: @@ -1003,7 +1006,7 @@ def parse_signature(sig_text, name, pos): if tok_type == tokenize.ENDMARKER and nest_count: raise TemplateError('Invalid signature: (%s)' % sig_text, position=pos, name=name) - if (not nest_count and + if (not nest_count and (tok_type == tokenize.ENDMARKER or (tok_type == tokenize.OP and tok_string == ','))): default_expr = isolate_expression(sig_text, start_pos, end_pos) defaults[var_name] = default_expr @@ -1050,7 +1053,7 @@ def fill_command(args=None): args = sys.argv[1:] dist = pkg_resources.get_distribution('Paste') parser = optparse.OptionParser( - version=str(dist), + version=text(dist), usage=_fill_command_usage) parser.add_option( '-o', '--output', @@ -1069,7 +1072,7 @@ def fill_command(args=None): help="Put the environment in as top-level variables") options, args = parser.parse_args(args) if len(args) < 1: - print 'You must give a template filename' + print('You must give a template filename') sys.exit(2) template_name = args[0] args = args[1:] @@ -1078,7 +1081,7 @@ def fill_command(args=None): vars.update(os.environ) for value in args: if '=' not in value: - print 'Bad argument: %r' % value + print('Bad argument: %r' % value) sys.exit(2) name, value = value.split('=', 1) if name.startswith('py:'): @@ -1107,5 +1110,3 @@ def fill_command(args=None): if __name__ == '__main__': fill_command() - - diff --git a/tempita/_looper.py b/tempita/_looper.py index a25e6dd..d051d0e 100644 --- a/tempita/_looper.py +++ b/tempita/_looper.py @@ -18,14 +18,17 @@ looper you can get a better sense of the context. Use like:: """ +import sys +from tempita.compat3 import basestring_ + __all__ = ['looper'] class looper(object): """ Helper for looping (particularly in templates) - + Use this like:: - + for loop, item in looper(seq): if loop.first: ... @@ -50,13 +53,16 @@ class looper_iter(object): def __iter__(self): return self - def next(self): + def __next__(self): if self.pos >= len(self.seq): raise StopIteration result = loop_pos(self.seq, self.pos), self.seq[self.pos] self.pos += 1 return result + if sys.version < "3": + next = __next__ + class loop_pos(object): def __init__(self, seq, pos): @@ -79,12 +85,15 @@ class loop_pos(object): return self.seq[self.pos] item = property(item) - def next(self): + def __next__(self): try: return self.seq[self.pos+1] except IndexError: return None - next = property(next) + __next__ = property(__next__) + + if sys.version < "3": + next = __next__ def previous(self): if self.pos == 0: @@ -132,12 +141,12 @@ class loop_pos(object): """ if self.last: return True - return self._compare_group(self.item, self.next, getter) + return self._compare_group(self.item, self.__next__, getter) def _compare_group(self, item, other, getter): if getter is None: return item != other - elif (isinstance(getter, basestring) + elif (isinstance(getter, basestring_) and getter.startswith('.')): getter = getter[1:] if getter.endswith('()'): @@ -149,4 +158,3 @@ class loop_pos(object): return getter(item) != getter(other) else: return item[getter] != other[getter] - diff --git a/tempita/compat3.py b/tempita/compat3.py new file mode 100644 index 0000000..4317c3c --- /dev/null +++ b/tempita/compat3.py @@ -0,0 +1,43 @@ +import sys + +__all__ = ['b', 'basestring_', 'getnext', 'bytes', 'next', 'is_unicode'] + +if sys.version < "3": + b = bytes = str + basestring_ = basestring +else: + + def b(s): + if isinstance(s, str): + return s.encode('latin1') + return bytes(s) + basestring_ = (bytes, str) +text = str + +if sys.version < "3": + + def next(obj): + return obj.next() +else: + next = next + +if sys.version < "3": + + def is_unicode(obj): + return isinstance(obj, unicode) +else: + + def is_unicode(obj): + return isinstance(obj, str) + +def coerce_text(v): + if not isinstance(v, basestring_): + if sys.version < "3": + attr = '__unicode__' + else: + attr = '__str__' + if hasattr(v, attr): + return unicode(v) + else: + return bytes(v) + return v |