view treepkg/cmdexpand.py @ 575:87a0be76b5b7

Revert ugly hack for the command from config file problem
author Bjoern Ricks <bricks@intevation.de>
date Sat, 03 Sep 2011 10:55:32 +0000
parents 19d10022b2de
children
line wrap: on
line source
# 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):
    """Split the string into 'words' and expand variable references.

The string is first split into words with shlex.split.  Each of the
words is then subjected to either word expansion or list expansion.
Word expansion is very similar to what the Template class in Python's
string module provides:

  '$$' is expanded to '$'

  '$@' is expanded to '@'

  '$identifier' is expanded to the value of the variable given by
  identifier.  The identifier has the same syntax as a normal Python
  identifier.  The identifier stops at the first non-identifier
  character.  The value is converted to a string with str.

  '${identifier}' is treated like '$identifier' and provides a way to
  delimit the identifier in cases where the identifier is followed by
  characters that would otherwise be interpreted as part of the
  identifier.

A word will remain a single word after the expansion even if the
expanded string would be treated as multiple words by shlex.

A list expansion is applied to words that consist of a '@' followed by
an identifier.  Nothing else must be in the word.  The variable the
identifier refers to must be a sequence and the word will be replaced by
the sequence with each element of the sequence converted to a string
with str.

The variables known to the function are the keyword arguments.

Examples:

  >>> from cmdexpand import cmdexpand
  >>> cmdexpand("ssh $user$@$host", user="john", host="python")
  ['ssh', 'john@python']

  >>> cmdexpand("scp @files $user$@$host:$remotedir", user="john",
  ...           host="python", files=["main.py", "cmdexpand.py"],
  ...           remotedir="/home/john/files")
  ['scp', 'main.py', 'cmdexpand.py', 'john@python:/home/john/files']
"""
    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
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)