📄 ezt.py
字号:
elif cmd in _block_cmds: if len(args) > _block_cmd_specs[cmd] + 1: raise ArgCountSyntaxError(str(args[1:])) ### this assumes arg1 is always a ref args[1] = _prepare_ref(args[1], for_names, file_args) # handle arg2 for the 'is' command if cmd == 'is': args[2] = _prepare_ref(args[2], for_names, file_args) elif cmd == 'for': for_names.append(args[1][0]) # remember the cmd, current pos, args, and a section placeholder stack.append([cmd, len(program), args[1:], None]) elif cmd == 'include': if args[1][0] == '"': include_filename = args[1][1:-1] f_args = [ ] for arg in args[2:]: f_args.append(_prepare_ref(arg, for_names, file_args)) program.extend(self._parse(reader.read_other(include_filename), for_names, f_args)) else: if len(args) != 2: raise ArgCountSyntaxError(str(args)) program.append((self._cmd_include, (_prepare_ref(args[1], for_names, file_args), reader))) elif cmd == 'if-any': f_args = [ ] for arg in args[1:]: f_args.append(_prepare_ref(arg, for_names, file_args)) stack.append(['if-any', len(program), f_args, None]) else: # implied PRINT command if len(args) > 1: f_args = [ ] for arg in args: f_args.append(_prepare_ref(arg, for_names, file_args)) program.append((self._cmd_format, (f_args[0], f_args[1:]))) else: program.append((self._cmd_print, _prepare_ref(args[0], for_names, file_args))) if stack: ### would be nice to say which blocks... raise UnclosedBlocksError() return program def _execute(self, program, fp, ctx): """This private helper function takes a 'program' sequence as created by the method '_parse' and executes it step by step. strings are written to the file object 'fp' and functions are called. """ for step in program: if isinstance(step, StringType): fp.write(step) else: step[0](step[1], fp, ctx) def _cmd_print(self, valref, fp, ctx): value = _get_value(valref, ctx) # if the value has a 'read' attribute, then it is a stream: copy it if hasattr(value, 'read'): while 1: chunk = value.read(16384) if not chunk: break fp.write(chunk) else: fp.write(value) def _cmd_format(self, (valref, args), fp, ctx): fmt = _get_value(valref, ctx) parts = _re_subst.split(fmt) for i in range(len(parts)): piece = parts[i] if i%2 == 1 and piece != '%': idx = int(piece) if idx < len(args): piece = _get_value(args[idx], ctx) else: piece = '<undef>' fp.write(piece) def _cmd_include(self, (valref, reader), fp, ctx): fname = _get_value(valref, ctx) ### note: we don't have the set of for_names to pass into this parse. ### I don't think there is anything to do but document it. self._execute(self._parse(reader.read_other(fname)), fp, ctx) def _cmd_if_any(self, args, fp, ctx): "If any value is a non-empty string or non-empty list, then T else F." (valrefs, t_section, f_section) = args value = 0 for valref in valrefs: if _get_value(valref, ctx): value = 1 break self._do_if(value, t_section, f_section, fp, ctx) def _cmd_if_index(self, args, fp, ctx): ((valref, value), t_section, f_section) = args list, idx = ctx.for_index[valref[0]] if value == 'even': value = idx % 2 == 0 elif value == 'odd': value = idx % 2 == 1 elif value == 'first': value = idx == 0 elif value == 'last': value = idx == len(list)-1 else: value = idx == int(value) self._do_if(value, t_section, f_section, fp, ctx) def _cmd_is(self, args, fp, ctx): ((left_ref, right_ref), t_section, f_section) = args value = _get_value(right_ref, ctx) value = string.lower(_get_value(left_ref, ctx)) == string.lower(value) self._do_if(value, t_section, f_section, fp, ctx) def _do_if(self, value, t_section, f_section, fp, ctx): if t_section is None: t_section = f_section f_section = None if value: section = t_section else: section = f_section if section is not None: self._execute(section, fp, ctx) def _cmd_for(self, args, fp, ctx): ((valref,), unused, section) = args list = _get_value(valref, ctx) if isinstance(list, StringType): raise NeedSequenceError() refname = valref[0] ctx.for_index[refname] = idx = [ list, 0 ] for item in list: self._execute(section, fp, ctx) idx[1] = idx[1] + 1 del ctx.for_index[refname]def boolean(value): "Return a value suitable for [if-any bool_var] usage in a template." if value: return 'yes' return Nonedef _prepare_ref(refname, for_names, file_args): """refname -> a string containing a dotted identifier. example:"foo.bar.bang" for_names -> a list of active for sequences. Returns a `value reference', a 3-Tupel made out of (refname, start, rest), for fast access later. """ # is the reference a string constant? if refname[0] == '"': return None, refname[1:-1], None # if this is an include-argument, then just return the prepared ref if refname[:3] == 'arg': try: idx = int(refname[3:]) except ValueError: pass else: if idx < len(file_args): return file_args[idx] parts = string.split(refname, '.') start = parts[0] rest = parts[1:] while rest and (start in for_names): # check if the next part is also a "for name" name = start + '.' + rest[0] if name in for_names: start = name del rest[0] else: break return refname, start, restdef _get_value((refname, start, rest), ctx): """(refname, start, rest) -> a prepared `value reference' (see above). ctx -> an execution context instance. Does a name space lookup within the template name space. Active for blocks take precedence over data dictionary members with the same name. """ if rest is None: # it was a string constant return start if ctx.for_index.has_key(start): list, idx = ctx.for_index[start] ob = list[idx] elif ctx.data.has_key(start): ob = ctx.data[start] else: raise UnknownReference(refname) # walk the rest of the dotted reference for attr in rest: try: ob = getattr(ob, attr) except AttributeError: raise UnknownReference(refname) # make sure we return a string instead of some various Python types if isinstance(ob, IntType) or isinstance(ob, FloatType): return str(ob) if ob is None: return '' # string or a sequence return obclass _context: """A container for the execution context"""class Reader: "Abstract class which allows EZT to detect Reader objects."class _FileReader(Reader): """Reads templates from the filesystem.""" def __init__(self, fname): self.text = open(fname, 'rb').read() self._dir = os.path.dirname(fname) def read_other(self, relative): return _FileReader(os.path.join(self._dir, relative))class _TextReader(Reader): """'Reads' a template from provided text.""" def __init__(self, text): self.text = text def read_other(self, relative): raise BaseUnavailableError()class EZTException(Exception): """Parent class of all EZT exceptions."""class ArgCountSyntaxError(EZTException): """A bracket directive got the wrong number of arguments."""class UnknownReference(EZTException): """The template references an object not contained in the data dictionary."""class NeedSequenceError(EZTException): """The object dereferenced by the template is no sequence (tuple or list)."""class UnclosedBlocksError(EZTException): """This error may be simply a missing [end]."""class UnmatchedEndError(EZTException): """This error may be caused by a misspelled if directive."""class BaseUnavailableError(EZTException): """Base location is unavailable, which disables includes."""# --- standard test environment ---def test_parse(): assert _re_parse.split('[a]') == ['', '[a]', None, ''] assert _re_parse.split('[a] [b]') == \ ['', '[a]', None, ' ', '[b]', None, ''] assert _re_parse.split('[a c] [b]') == \ ['', '[a c]', None, ' ', '[b]', None, ''] assert _re_parse.split('x [a] y [b] z') == \ ['x ', '[a]', None, ' y ', '[b]', None, ' z'] assert _re_parse.split('[a "b" c "d"]') == \ ['', '[a "b" c "d"]', None, ''] assert _re_parse.split(r'["a \"b[foo]" c.d f]') == \ ['', '["a \\"b[foo]" c.d f]', None, '']def _test(argv): import doctest, ezt verbose = "-v" in argv return doctest.testmod(ezt, verbose=verbose)if __name__ == "__main__": # invoke unit test for this module: import sys sys.exit(_test(sys.argv)[0])
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -