Mercurial > treepkg > treepkg
diff treepkg/cmdexpand.py @ 44:a2ce575ce82b
add cmdexpand function and tests
author | Bernhard Herzog <bh@intevation.de> |
---|---|
date | Mon, 19 Mar 2007 20:14:07 +0100 |
parents | |
children | 83e1aa122ad0 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/treepkg/cmdexpand.py Mon Mar 19 20:14:07 2007 +0100 @@ -0,0 +1,73 @@ +# Copyright (C) 2007 by Intevation GmbH +# Authors: +# Bernhard Herzog <bh@intevation.de> +# +# This program is free software under the GPL (>=v2) +# Read the file COPYING coming with the software for details. + +"""Shell like string splitting and expansion""" + +import re +import shlex + + +# helper for the other regular expression matching a python identifier +match_identifier = "[_a-zA-Z][_a-zA-Z0-9]*" + +# regular expression to use for word expansion matching a dollar +# followed by exactly one of these: +# a) another dollar sign or the at-sign (for quoting of these characters) +# b) a python identifier +# c) a python identifier enclosed in braces +# d) something else which indicates invalid use of the dollar sign +rx_word_expansion = re.compile(r"\$((?P<delim>[$@])" + r"|(?P<named>%(identifier)s)" + r"|\{(?P<braced>%(identifier)s)\}" + r"|(?P<invalid>))" + % dict(identifier=match_identifier)) + +# regular expression matching an entire word that has to be list +# expanded. The regex matches if the word starts with an at-sign. The +# part of the word that followes the at-sign either matches an +# identifier with the named group "named" or anything else which +# indicates invalid use the at-sign. +rx_list_expansion = re.compile(r"^@((?P<named>%(identifier)s)|(?P<invalid>.+))$" + % dict(identifier=match_identifier)) + +# match an unquoted at-sign. +rx_unquoted_at = re.compile("[^$]@") + +def expandword(word, mapping): + def replacment(match): + key = match.group("named") or match.group("braced") + if key: + return str(mapping[key]) + + delim = match.group("delim") + if delim: + return delim + + # otherwise invalid has matched and we raise a value error + assert match.group("invalid") != None + raise ValueError + + return rx_word_expansion.sub(replacment, word) + +def cmdexpand(string, **kw): + words = shlex.split(string) + for index, word in reversed(list(enumerate(words))): + match = rx_unquoted_at.search(word) + if match: + raise ValueError("%r contains an unquoted '@'" % word) + match = rx_list_expansion.match(word) + if match: + key = match.group("named") + if key: + words[index:index + 1] = (str(item) for item in kw[key]) + else: + assert match.group("invalid") != None + raise ValueError("In %r the characters after the '@'" + " do not match a python identifier" % word) + else: + words[index] = expandword(word, kw) + return words