Jinja: FileSystemLoader doesn't work with Windows paths (backslashes)

Created on 7 Sep 2017  ·  9Comments  ·  Source: pallets/jinja

Expected Behavior

On Windows FileSystemLoader should allow both Windows-style (with \) and UNIX-style (with /) paths.

Actual Behavior

On Windows, when using FileSystemLoader the following fails:
jinjaEnvironment.get_template('.\source\architecture\ARM\ARMv6-M-ARMv7-M\bo ardTemplates\ARMv6-M-ARMv7-M.metadata')
with
jinja2.exceptions.TemplateNotFound: .\source\architecture\ARM\ARMv6-M-ARMv7-M\bo ardTemplates\ARMv6-M-ARMv7-M.metadata

while this jinjaEnvironment.get_template('.\source\architecture\ARM\ARMv6-M-ARMv7-M\bo ardTemplates\ARMv6-M-ARMv7-M.metadata'.replace('\\', '/')) works perfectly fine.

Full Traceback

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

Your Environment

  • Python version: 2.7.12
  • Jinja version: 2.9.6

Most helpful comment

I think this information should be explicitly stated in the documentation with bold font if you don't intend to fix it. It's not that I have purposefully used backslashes - when generating the filename using some other python code (for example by searching the folders for appropriate extensions), the string has system's native slashes (so backslashes on Windows). In that case you have to explicitly replace the slashes...

BTW - I'm not using Windows, but I'm writing a script which should work on all platforms.

All 9 comments

Jinja template names are not fileystem paths (even though they map to filesystem paths when using just a FileSystemLoader). They always use forward slashes so this is working as intended.

I think this information should be explicitly stated in the documentation with bold font if you don't intend to fix it. It's not that I have purposefully used backslashes - when generating the filename using some other python code (for example by searching the folders for appropriate extensions), the string has system's native slashes (so backslashes on Windows). In that case you have to explicitly replace the slashes...

BTW - I'm not using Windows, but I'm writing a script which should work on all platforms.

@FreddieChopin
Is this one fixed? seems still affecting me in 2019.

What I have to do is to manually replace all \\ to /.

Is this one fixed?

"It is not a bug - it's a feature!" (;

This is a really weird one! Could this be changed or mentioned (in big letters) in the docs please?

PR welcome (for the docs)

I started on one, but then I found this: 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)

The docs explicitly say that os.path.join should/could be used. Perhaps an issue is more appropriate?

The real problem is python path module returns path with '\' on windows by default. I know it's right behaviour somehow but built-in function like "open" accepts both slash and backslash so "sometimes it works, sometimes it doesn't" kind of things happens. Probably we need some way to treat paths independent from platforms...some sort of "python convention to treat a paths internally" sort of things...I think this is not a problem of jinja2 (This is a problem of historical convention between Unix and Windows and it seems last forever) but some "heads up" in a starter documents are welcomed.

The fundamental problem is that a template "path" is not really an OS path, but is expected to contain only "/" separators. But in the loaders.get_source(), self.searchpath also thinks that the path separator is always "/".

The template paths should not be changed to be actual OS paths, because there are apparently other places where this canonical assumption is used.

The solution is to change both the template and the searchpath to contain actual OS path separators, at the point where OS paths are needed:

    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 ...

I have tested this code briefly in Windows 10/Python 2.7, and it corrected my "Template not found" error.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

guettli picture guettli  ·  5Comments

htgoebel picture htgoebel  ·  4Comments

priestc picture priestc  ·  5Comments

AMDmi3 picture AMDmi3  ·  4Comments

jp-costa picture jp-costa  ·  5Comments