# HG changeset patch # User cmlenz # Date 1154451963 0 # Node ID 88ac4c6801207dcb1f82abcb215ad084b96922a6 # Parent d4ea684655d97c51099a0c1b00958c3f7c5352dc Merged [135:138/branches/experimental/cspeedups]. diff --git a/examples/bench/basic.py b/examples/bench/basic.py --- a/examples/bench/basic.py +++ b/examples/bench/basic.py @@ -121,7 +121,7 @@ print render() return render -def run(engines, verbose=False): +def run(engines, number=2000, verbose=False): basepath = os.path.abspath(os.path.dirname(__file__)) for engine in engines: dirname = os.path.join(basepath, engine) @@ -133,7 +133,7 @@ t = timeit.Timer(setup='from __main__ import %s; render = %s("%s", %s)' % (engine, engine, dirname, verbose), stmt='render()') - time = t.timeit(number=2000) / 2000 + time = t.timeit(number=number) / number if verbose: print '--------------------------------------------------------' print '%.2f ms' % (1000 * time) @@ -151,7 +151,7 @@ if '-p' in sys.argv: import hotshot, hotshot.stats prof = hotshot.Profile("template.prof") - benchtime = prof.runcall(run, engines, verbose=verbose) + benchtime = prof.runcall(run, engines, number=100, verbose=verbose) stats = hotshot.stats.load("template.prof") stats.strip_dirs() stats.sort_stats('time', 'calls') diff --git a/markup/core.py b/markup/core.py --- a/markup/core.py +++ b/markup/core.py @@ -247,6 +247,15 @@ return TEXT, u''.join([x[1] for x in self]), (None, -1, -1) +def plaintext(text, keeplinebreaks=True): + """Returns the text as a `unicode` string with all entities and tags + removed. + """ + text = stripentities(striptags(text)) + if not keeplinebreaks: + text = text.replace(u'\n', u' ') + return text + def stripentities(text, keepxmlentities=False): """Return a copy of the given text with any character or numeric entities replaced by the equivalent UTF-8 characters. @@ -279,6 +288,10 @@ return re.sub(r'&(?:#((?:\d+)|(?:[xX][0-9a-fA-F]+));?|(\w+);)', _replace_entity, text) +def striptags(text): + """Return a copy of the text with all XML/HTML tags removed.""" + return re.sub(r'<[^>]*?>', '', text) + class Markup(unicode): """Marks a string as being safe for inclusion in HTML/XML output without @@ -310,20 +323,6 @@ return Markup(unicode(self).join([escape(item, quotes=escape_quotes) for item in seq])) - def stripentities(self, keepxmlentities=False): - """Return a copy of the text with any character or numeric entities - replaced by the equivalent UTF-8 characters. - - If the `keepxmlentities` parameter is provided and evaluates to `True`, - the core XML entities (&, ', >, < and ") are not - stripped. - """ - return Markup(stripentities(self, keepxmlentities=keepxmlentities)) - - def striptags(self): - """Return a copy of the text with all XML/HTML tags removed.""" - return Markup(re.sub(r'<[^>]*?>', '', self)) - def escape(cls, text, quotes=True): """Create a Markup instance from a string and escape special characters it may contain (<, >, & and \"). @@ -353,14 +352,19 @@ .replace('<', '<') \ .replace('&', '&') - def plaintext(self, keeplinebreaks=True): - """Returns the text as a `unicode` string with all entities and tags - removed. + def stripentities(self, keepxmlentities=False): + """Return a copy of the text with any character or numeric entities + replaced by the equivalent UTF-8 characters. + + If the `keepxmlentities` parameter is provided and evaluates to `True`, + the core XML entities (&, ', >, < and ") are not + stripped. """ - text = unicode(self.striptags().stripentities()) - if not keeplinebreaks: - text = text.replace(u'\n', u' ') - return text + return Markup(stripentities(self, keepxmlentities=keepxmlentities)) + + def striptags(self): + """Return a copy of the text with all XML/HTML tags removed.""" + return Markup(striptags(self)) escape = Markup.escape diff --git a/markup/eval.py b/markup/eval.py --- a/markup/eval.py +++ b/markup/eval.py @@ -71,7 +71,7 @@ @param source: the expression as string """ self.source = source - self.code = self._compile(source, filename, lineno) + self.code = _compile(source, filename, lineno) def __repr__(self): return '' % self.source @@ -87,54 +87,51 @@ retval = retval() return retval - def _compile(self, source, filename, lineno): - tree = parse(self.source, 'eval') - xform = ExpressionASTTransformer() - tree = xform.visit(tree) - - if isinstance(filename, unicode): - # pycodegen doesn't like unicode in the filename - filename = filename.encode('utf-8', 'replace') - tree.filename = filename or '' - - gen = ExpressionCodeGenerator(tree) - if lineno >= 0: - gen.emit('SET_LINENO', lineno) - - return gen.getCode() - def _lookup_name(data, name, locals=None): - val = data.get(name) - if val is None and locals: - val = locals.get(name) - if val is None: - val = getattr(__builtin__, name, None) - return val - _lookup_name = staticmethod(_lookup_name) +def _compile(source, filename=None, lineno=-1): + tree = parse(source, 'eval') + xform = ExpressionASTTransformer() + tree = xform.visit(tree) - def _lookup_attribute(data, obj, key): - if hasattr(obj, key): - return getattr(obj, key) - try: - return obj[key] - except (KeyError, TypeError): - return None - _lookup_attribute = staticmethod(_lookup_attribute) + if isinstance(filename, unicode): + # pycodegen doesn't like unicode in the filename + filename = filename.encode('utf-8', 'replace') + tree.filename = filename or '' - def _lookup_item(data, obj, key): - if len(key) == 1: - key = key[0] - try: - return obj[key] - except (KeyError, IndexError, TypeError), e: - pass - if isinstance(key, basestring): - try: - return getattr(obj, key) - except (AttributeError, TypeError), e: - pass - _lookup_item = staticmethod(_lookup_item) + gen = ExpressionCodeGenerator(tree) + if lineno >= 0: + gen.emit('SET_LINENO', lineno) + return gen.getCode() + +def _lookup_name(data, name, locals=None): + val = data.get(name) + if val is None and locals: + val = locals.get(name) + if val is None: + val = getattr(__builtin__, name, None) + return val + +def _lookup_attribute(data, obj, key): + if hasattr(obj, key): + return getattr(obj, key) + try: + return obj[key] + except (KeyError, TypeError): + return None + +def _lookup_item(data, obj, key): + if len(key) == 1: + key = key[0] + try: + return obj[key] + except (KeyError, IndexError, TypeError), e: + pass + if isinstance(key, basestring): + try: + return getattr(obj, key) + except (AttributeError, TypeError), e: + pass class ASTTransformer(object): """General purpose base class for AST transformations. @@ -251,8 +248,7 @@ """ def visitGetattr(self, node, *args, **kwargs): - return ast.CallFunc( - ast.Getattr(ast.Name('self'), '_lookup_attribute'), + return ast.CallFunc(ast.Name('_lookup_attribute'), [ast.Name('data'), self.visit(node.expr, *args, **kwargs), ast.Const(node.attrname)] ) @@ -269,14 +265,11 @@ func_args = [ast.Name('data'), ast.Const(node.name)] if kwargs.get('lookup_locals'): func_args.append(ast.CallFunc(ast.Name('locals'), [])) - return ast.CallFunc( - ast.Getattr(ast.Name('self'), '_lookup_name'), func_args - ) + return ast.CallFunc(ast.Name('_lookup_name'), func_args) return node def visitSubscript(self, node, *args, **kwargs): - return ast.CallFunc( - ast.Getattr(ast.Name('self'), '_lookup_item'), + return ast.CallFunc(ast.Name('_lookup_item'), [ast.Name('data'), self.visit(node.expr, *args, **kwargs), ast.Tuple(map(self.visit, node.subs, *args, **kwargs))] ) diff --git a/markup/template.py b/markup/template.py --- a/markup/template.py +++ b/markup/template.py @@ -936,9 +936,8 @@ for idx, (test, path, template, directives) in \ enumerate(match_templates): - result = test(kind, data, pos) - if result: + if test(kind, data, pos) is True: # Consume and store all events until an end event # corresponding to this start event is encountered content = [(kind, data, pos)] @@ -961,8 +960,8 @@ ctxt, match_templates[:idx] + match_templates[idx + 1:]): yield event + ctxt.pop() - break else: # no matches diff --git a/markup/tests/core.py b/markup/tests/core.py --- a/markup/tests/core.py +++ b/markup/tests/core.py @@ -20,6 +20,10 @@ class MarkupTestCase(unittest.TestCase): + def test_repr(self): + markup = Markup('foo') + self.assertEquals('', repr(markup)) + def test_escape(self): markup = escape('"&"') assert isinstance(markup, Markup) @@ -77,9 +81,9 @@ self.assertEquals('& j', markup) def test_stripentities_keepxml(self): - markup = Markup('fo
o
').striptags() + markup = Markup('& j').stripentities(keepxmlentities=True) assert isinstance(markup, Markup) - self.assertEquals('foo', markup) + self.assertEquals('& j', markup) def test_striptags_empty(self): markup = Markup('
').striptags()