Jinja: FileSystemLoader no funciona con rutas de Windows (barras invertidas)

Creado en 7 sept. 2017  ·  9Comentarios  ·  Fuente: pallets/jinja

Comportamiento esperado

En Windows FileSystemLoader debería permitir rutas de estilo Windows (con \ ) y estilo UNIX (con / ).

Comportamiento real

En Windows, al usar FileSystemLoader lo siguiente falla:
jinjaEnvironment.get_template('.\source\architecture\ARM\ARMv6-M-ARMv7-M\bo ardTemplates\ARMv6-M-ARMv7-M.metadata')
con
jinja2.exceptions.TemplateNotFound: .\source\architecture\ARM\ARMv6-M-ARMv7-M\bo ardTemplates\ARMv6-M-ARMv7-M.metadata

mientras que este jinjaEnvironment.get_template('.\source\architecture\ARM\ARMv6-M-ARMv7-M\bo ardTemplates\ARMv6-M-ARMv7-M.metadata'.replace('\\', '/')) funciona perfectamente bien.

Rastreo completo

Traceback (most recent call last):
  File "./scripts/generateBoard.py", line 186, in <module>
    metadata = jinjaEnvironment.get_template(metadataFile).render(dictionary = d
ictionary)
  File "C:\Python27\lib\site-packages\jinja2\environment.py", line 830, in get_t
emplate
    return self._load_template(name, self.make_globals(globals))
  File "C:\Python27\lib\site-packages\jinja2\environment.py", line 804, in _load
_template
    template = self.loader.load(self, name, globals)
  File "C:\Python27\lib\site-packages\jinja2\loaders.py", line 113, in load
    source, filename, uptodate = self.get_source(environment, name)
  File "C:\Python27\lib\site-packages\jinja2\loaders.py", line 168, in get_sourc
e
    pieces = split_template_path(template)
  File "C:\Python27\lib\site-packages\jinja2\loaders.py", line 31, in split_temp
late_path
    raise TemplateNotFound(template)
jinja2.exceptions.TemplateNotFound: .\source\architecture\ARM\ARMv6-M-ARMv7-M\bo
ardTemplates\ARMv6-M-ARMv7-M.metadata

tu entorno

  • Versión de Python: 2.7.12
  • Versión Jinja: 2.9.6

Comentario más útil

Creo que esta información debe indicarse explícitamente en la documentación con letra en negrita si no tiene la intención de solucionarlo. No es que haya usado barras diagonales inversas a propósito: al generar el nombre de archivo usando algún otro código de Python (por ejemplo, al buscar en las carpetas las extensiones apropiadas), la cadena tiene barras diagonales nativas del sistema (por lo tanto, barras diagonales inversas en Windows). En ese caso, debe reemplazar explícitamente las barras...

Por cierto, no estoy usando Windows, pero estoy escribiendo un script que debería funcionar en todas las plataformas.

Todos 9 comentarios

Los nombres de las plantillas de Jinja no son rutas del sistema de archivos (aunque se asignan a rutas del sistema de archivos cuando se usa solo un FileSystemLoader). Siempre usan barras diagonales, por lo que funciona según lo previsto.

Creo que esta información debe indicarse explícitamente en la documentación con letra en negrita si no tiene la intención de solucionarlo. No es que haya usado barras diagonales inversas a propósito: al generar el nombre de archivo usando algún otro código de Python (por ejemplo, al buscar en las carpetas las extensiones apropiadas), la cadena tiene barras diagonales nativas del sistema (por lo tanto, barras diagonales inversas en Windows). En ese caso, debe reemplazar explícitamente las barras...

Por cierto, no estoy usando Windows, pero estoy escribiendo un script que debería funcionar en todas las plataformas.

@FreddieChopin
¿Este está arreglado? parece que todavía me afecta en 2019.

Lo que tengo que hacer es reemplazar manualmente todos los \\ a / .

¿Este está arreglado?

"No es un error, ¡es una característica!" (;

¡Este es realmente extraño! ¿Podría esto cambiarse o mencionarse (en letras grandes) en los documentos, por favor?

Bienvenida a relaciones públicas (para los documentos)

Empecé con uno, pero luego encontré esto: https://github.com/pallets/jinja/blob/master/jinja2/loaders.py#L43 -L61

A very basic example for a loader that looks up templates on the file
system could look like this::
    from jinja2 import BaseLoader, TemplateNotFound
    from os.path import join, exists, getmtime
    class MyLoader(BaseLoader):
        def __init__(self, path):
            self.path = path
        def get_source(self, environment, template):
            path = join(self.path, template)
            if not exists(path):
                raise TemplateNotFound(template)
            mtime = getmtime(path)
            with file(path) as f:
                source = f.read().decode('utf-8')
            return source, path, lambda: mtime == getmtime(path)

Los documentos dicen explícitamente que os.path.join debería/podría usarse. ¿Quizás un tema es más apropiado?

El verdadero problema es que el módulo de ruta de Python devuelve la ruta con '\' en Windows de forma predeterminada. Sé que es el comportamiento correcto de alguna manera, pero la función integrada como "abrir" acepta tanto la barra diagonal como la barra diagonal inversa, por lo que "a veces funciona, a veces no" suceden cosas. Probablemente necesitemos alguna forma de tratar los caminos independientemente de las plataformas... algún tipo de "convención de Python para tratar los caminos internamente"... Creo que esto no es un problema de jinja2 (Este es un problema de convención histórica entre Unix y Windows y parece durar para siempre), pero se agradecen algunos "avisos" en los documentos de inicio.

El problema fundamental es que una "ruta" de plantilla no es realmente una ruta de sistema operativo, pero se espera que contenga solo separadores "/". Pero en loaders.get_source() , self.searchpath también piensa que el separador de ruta siempre es "/".

Las rutas de plantilla no deben cambiarse para que sean rutas de sistema operativo reales, porque aparentemente hay otros lugares donde se usa esta suposición canónica.

La solución es cambiar tanto la plantilla como la ruta de búsqueda para que contengan separadores de ruta de sistema operativo reales, en el punto donde se necesitan rutas de sistema operativo:

    def get_source(self, environment, template):
        _searchpaths = self.searchpath
        if path.sep != '/':
            template = template.replace('/', path.sep)
            _searchpaths = [p.replace('/', path.sep) for p in self.searchpath]
        pieces = os.path.split(template)
        for searchpath in _searchpaths:
            # Existing code continues here ...

Probé este código brevemente en Windows 10/Python 2.7 y corrigió mi error "Plantilla no encontrada".

¿Fue útil esta página
0 / 5 - 0 calificaciones