0001"""Utility functions used by generated kid modules.
0002"""
0003
0004from __future__ import generators
0005
0006__revision__ = "$Rev: 314 $"
0007__date__ = "$Date: 2006-04-18 00:37:38 +0000 (Tue, 18 Apr 2006) $"
0008__author__ = "Ryan Tomayko (rtomayko@gmail.com)"
0009__copyright__ = "Copyright 2004-2005, Ryan Tomayko"
0010__license__ = "MIT <http://www.opensource.org/licenses/mit-license.php>"
0011
0012import inspect
0013import sys
0014from types import TypeType, ModuleType
0015from os.path import join, normpath, abspath, dirname
0016
0017# these are for use by template code
0018import kid
0019from kid.pull import XML, document, ElementStream,                        Element, SubElement, Comment, ProcessingInstruction,                        START, END, TEXT, START_NS, COMMENT, PI, DOCTYPE,                        XML_DECL, to_unicode
0023
0024class TemplateNotFound(Exception): pass
0025
0026_local_excludes = ['generate', 'module', 'pull', 'serialize', 'transform', 'write']
0027def get_locals(inst, _locals=None):
0028    if _locals is None:
0029        _locals = {}
0030    ls = []
0031    local_excludes = _local_excludes # local copy
0032    for var, value in inspect.getmembers(inst):
0033        if not var.startswith('_') and not var in local_excludes                   and var not in _locals:
0035            ls.append('%s=self.%s' % (var, var))
0036    return ';'.join(ls)
0037
0038def get_base_class(thing, from_file):
0039    if thing is None or thing is kid.BaseTemplate:
0040        cls = kid.BaseTemplate
0041    elif isinstance(thing, basestring):
0042        path = kid.path.find(thing, from_file)
0043        cls = kid.load_template(path).Template
0044    elif isinstance(thing, TypeType):
0045        cls = thing
0046    elif isinstance(thing, ModuleType):
0047        cls = thing.Template
0048    else:
0049        raise TemplateNotFound("Could not find template: %r" % thing)
0050    return cls
0051
0052def make_attrib(attrib, encoding=None):
0053    if attrib is None:
0054        return {}
0055    if encoding is None:
0056        encoding = sys.getdefaultencoding()
0057
0058    for (k, v) in attrib.items():
0059        if isinstance(v, list):
0060            ls = [to_unicode(i, encoding) for i in v if i is not None]
0061            if not ls:
0062                del attrib[k]
0063            else:
0064                attrib[k] = ''.join(ls)
0065        else:
0066            attrib[k] = to_unicode(v, encoding)
0067    return attrib
0068
0069def generate_content(content, parent=None):
0070    if content is None:
0071        return []
0072    if isinstance(content, basestring):
0073        return [(TEXT, content)]
0074    elif hasattr(content, 'tag') and hasattr(content, 'attrib'):
0075        # if we get an Element back, make it an ElementStream
0076        return ElementStream(content)
0077    elif hasattr(content, '__iter__'):
0078        # if we get an iterable back, there are two cases:
0079        if hasattr(content, '__getitem__'):
0080            # if we get a sequence back, we simply flatten it
0081            def flatten(seq):
0082                for i in seq:
0083                    for ev, item in generate_content(i):
0084                        yield ev, item
0085            return flatten(content)
0086        else:
0087            # if we get a generator back, pray it's an ElementStream
0088            return content
0089    else:
0090        return [(TEXT, unicode(content))]
0091
0092def filter_names(names, omit_list):
0093    for ns in names.keys():
0094        if ns in omit_list:
0095            del names[ns]
0096    return names
0097
0098def update_dict(a, s, globals, locals):
0099    """Update dictionary a from keyword argument string s."""
0100    try:
0101        strings = None
0102        try:
0103            b = eval('dict(%s)' % s, globals, locals)
0104        except (TypeError, SyntaxError):
0105            # TypeErrror happens with Python <2.3, because building
0106            # dictionaries from keyword arguments was not supported.
0107            # SyntaxError can happen if one of the keyword arguments
0108            # is the same as a Python keyword (e.g. "class") or if it is a
0109            # qualified name containing a namespace prefixed with a colon.
0110            # So in these cases we parse the keyword arguments manually:
0111            try:
0112                from cStringIO import StringIO
0113            except ImportError:
0114                from StringIO import StringIO
0115            from tokenize import generate_tokens
0116            from token import NAME, OP
0117            depth, types, strings = 0, [], []
0118            for token in generate_tokens(StringIO(s).readline):
0119                type_, string = token[:2]
0120                if type_ == OP:
0121                    if string == '=':
0122                        if depth == 0:
0123                            if len(types) > 0                                   and types[-1] == NAME and strings[-1]:
0125                                if len(types) > 2                                       and types[-2] == OP and strings[-2] == ':'                                       and types[-3] == NAME and strings[-3]:
0128                                    strings[-3:] = ["'%s'" % ''.join(strings[-3:])]
0129                                else:
0130                                    strings[-1] = "'%s'" % strings[-1]
0131                                string = ':'
0132                    elif string in '([{':
0133                        depth += 1
0134                    elif depth > 0 and string in ')]}':
0135                        depth -= 1
0136                types.append(type_)
0137                strings.append(string)
0138            b = eval('{%s}' % ''.join(strings), globals, locals)
0139    except Exception:
0140        exc_type, exc_obj, tb = sys.exc_info()
0141        if strings is None:
0142            code = s
0143        else:
0144            code = "%s -> %s" % (s, ''.join(strings))
0145        raise exc_type("%s in %s" % (exc_obj, code))
0146    for k in b.keys():
0147        if b[k] is None:
0148            del b[k]
0149            if k in a:
0150                del a[k]
0151    a.update(b)
0152    return a
0153
0154__all__ = ['XML', 'document', 'ElementStream',
0155           'Element', 'SubElement', 'Comment', 'ProcessingInstruction',
0156           'START', 'END', 'TEXT', 'START_NS', 'COMMENT',
0157           'PI', 'DOCTYPE', 'XML_DECL']