Introducción

La cadena módulo dispone de una Plantilla de clase, que le permite realizar sustituciones en una cadena mediante un objeto de asignación, por ejemplo:

>>> string.Template('var is $var').substitute({'var': 1})
'var is 1'

El método de sustitución puede plantear una KeyError excepción, si se realiza un intento de sustituir un elemento que falta en la asignación, por ejemplo

>>> string.Template('var is $var and foo is $foo').substitute({'var': 1})
KeyError: 'foo'

o pueden provocar un ValueError, si la plantilla de la cadena no es válida, por ejemplo, contiene un $ carácter seguido por un espacio:

>>> string.Template('$ var is $var').substitute({'var': 1})
ValueError: Invalid placeholder in string: line 1, col 1

El Problema

Dada una cadena de plantilla y una asignación, quiero para determinar si todos los titulares en la plantilla debería ser sustituido. Para ello, me gustaría tratar de hacer la sustitución y la captura de cualquier KeyError excepción:

def check_substitution(template, mapping):
    try:
        string.Template(template).substitute(mapping)
    except KeyError:
        return False
    except ValueError:
        pass
    return True

Pero esto no funciona, porque si la plantilla no es válida y un ValueError es elevado, posterior KeyErrors no son capturados:

>>> check_substitution('var is $var and foo is $foo', {'var': 1})
False
>>> check_substitution('$ var is $var and foo is $foo', {'var': 1})
True

pero me no importa sobre ValueErrors. Así que, ¿cuál sería el enfoque correcto para este problema?

Las plantillas tienen un safe_substitute método que ignora cualquier ValueError y va a hacer las sustituciones. El problema es que también se ignora KeyErrors.
La posible ValueError casos son todos los relacionados con los usos ilegales de la $ carácter, ¿verdad? Así que ¿por qué no hacer un poco de preprocesamiento de la cadena para escapar de los usos ilegales de $?
Esto podría funcionar, pero que habría que averiguar todos los casos que desencadenan una ValueError.

OriginalEl autor Ernest A | 2012-10-07

3 Comentarios

  1. 5

    Los médicos dicen que puede reemplazar el patrón de tiempo que contiene todos los grupos con nombre:

    import re
    from string import Template
    
    
    class TemplateIgnoreInvalid(Template):
        # override pattern to make sure `invalid` never matches
        pattern = r"""
        %(delim)s(?:
          (?P<escaped>%(delim)s) |   # Escape sequence of two delimiters
          (?P<named>%(id)s)      |   # delimiter and a Python identifier
          {(?P<braced>%(id)s)}   |   # delimiter and a braced identifier
          (?P<invalid>^$)            # never matches (the regex is not multilined)
        )
        """ % dict(delim=re.escape(Template.delimiter), id=Template.idpattern)
    
    
    def check_substitution(template, **mapping):
        try:
            TemplateIgnoreInvalid(template).substitute(mapping)
        except KeyError:
            return False
        else:
            return True

    Pruebas

    f = check_substitution
    assert f('var is $var', var=1)
    assert f('$ var is $var', var=1)
    assert     f('var is $var and foo is $foo', var=1, foo=2)
    assert not f('var is $var and foo is $foo', var=1)
    assert     f('$ var is $var and foo is $foo', var=1, foo=2)
    assert not f('$ var is $var and foo is $foo', var=1)
    # support all invalid patterns
    assert f('var is $var and foo is ${foo', var=1)
    assert f('var is $var and foo is ${foo', var=1, foo=2) #NOTE: problematic API
    assert     f('var is $var and foo is ${foo and ${baz}', var=1, baz=3)
    assert not f('var is $var and foo is ${foo and ${baz}', var=1)

    Funciona para todos válidos ocurrencias de el delimitador ($).

    Los ejemplos muestran que ignorar los patrones no válidos oculta simples erratas en la plantilla por lo que no es una buena API.

    Perfecto! Voy a tener que pensar acerca de lo que usted dice que es una mala idea para apoyar patrones no válidos. Gracias.

    OriginalEl autor jfs

  2. 3

    Esta es una solución Rápida (Usando recursividad):

    def check_substitution(tem, m):
        try:
            string.Template(tem).substitute(m)
        except KeyError:
            return False
        except ValueError:
            return check_substitution(tem.replace('$ ', '$'), m) #strip spaces after $
        return True

    Sé que es tomar un largo tiempo si hay más de Un Espacio entre $ y var , así que usted puede mejorar mediante el uso de la Expresión Regular.

    EDITAR

    escapar $ en $$ tiene más sentido [ Gracias @Pedro ] así que usted puede coger ValueError por esta declaración:

    return check_substitution(tem.replace('$ ', '$$ '), m) #escaping $ by $$
    Esta solución no es genérico. Aun así no funciona si el personaje después de que el espacio no es una carta y se supone que el espacio después de la $ es falso y que la intención original era un marcador de posición compuesta de la $ y la palabra después de que el espacio. Escapando de la solitaria $ doblando tendría más sentido.
    Gracias, voy a editar mi respuesta.
    Gracias. El escape requeriría más trabajo, sin embargo. Por ejemplo, la plantilla de ${foo también provoca una ValueError, debido a la desequilibrada {… es por eso que yo quería evitar el análisis de la plantilla de mí, pero tal vez no hay alternativa.
    mi respuesta corrige ${foo problema.

    OriginalEl autor MBarsi

  3. -1

    Python no va a hacer la cadena de sustitución a través de varias líneas

    Si usted tiene esta cadena

    criterion = """
        <criteria>
        <order>{order}</order>
          <body><![CDATA[{code}]]></body>
        </criteria>
    """
    
    criterion.format(dict(order="1",code="Hello")

    resultados en:

    KeyError: 'order'

    Una solución es el uso de la cadena.Plantilla de módulo

    from string import Template
    
    criterion = """
        <criteria>
        <order>$order</order>
          <body><![CDATA[$code]]></body>
        </criteria>
    """
    
    Template(criterion).substitute(dict(order="1",code="hello")

    NOTA: usted tiene el prefijo de las palabras clave con una $ no envuelva en {}

    la salida es:

     <criteria>
        <order>1</order>
          <body><![CDATA[hello]]></body>
        </criteria>

    Completo docs son: https://docs.python.org/2/library/string.html#template-strings

    «Python no va a hacer la cadena de sustitución a través de varias líneas» – Falso. Uso criterion.format(**dict(order="1", code="Hello") o simplemente criterion.format(order="1", code="Hello")

    OriginalEl autor knowingpark

Dejar respuesta

Please enter your comment!
Please enter your name here