pythongenshi

Genshi: Can't access imported modules from directives in HTML


Background: I'm using Genshi to generate HTML reports.

import genshi
import os
from genshi.template import MarkupTemplate

files = [
    r"a\b\c.txt",
    r"d/e/f.txt",
]

html = '''
    <html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/">
        <head>
        </head>
        <body>
            <p py:for="f in sorted(files, key=lambda x: x.lower().split(os.path.sep))">
                ${f}
            </p>
        </body>
    </html>
'''
template = MarkupTemplate(html)
stream = template.generate(
    files = files
)
print(stream.render('html'))

Problem: Genshi throws an UndefinedError exception because it doesn't know about modules I've imported.

D:\SVN\OSI_SVT\0.0.0.0_swr65430\srcPy\OSI_SVT>python36 test.py
Traceback (most recent call last):
  File "test.py", line 26, in <module>
    print(stream.render('html'))
  File "C:\Python36\lib\site-packages\genshi\core.py", line 184, in render
    return encode(generator, method=method, encoding=encoding, out=out)
  ...
genshi.template.eval.UndefinedError: "os" not defined

Question: Is there some way to make Genshi automatically aware of imported modules?

If this isn't possible natively in Genshi, I'd accept an answer that programmatically creates a collection of modules have been imported so they can be passed to the generate() call. For example: generate(**args)

What I've tried:


Solution

  • I found a way to do this outside of Genshi. This approach adds all global and local objects (imports as well as global and local variables) and adds them to a dictionary. The dictionary is then passed to generate() as keyword args (see this answer if you aren't familiar with this).

    import genshi
    import os
    import types
    from genshi.template import MarkupTemplate
    
    html = '''
        <html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://genshi.edgewall.org/">
            <head>
            </head>
            <body>
                <p py:for="f in sorted(files, key=lambda x: x.lower().split(os.path.sep))">
                    ${f}
                </p>
            </body>
        </html>
    '''
    
    def main():
    
        files = [
            r"a\b\c.txt",
            r"d/e/f.txt",
        ]
    
        generation_args = {}
        for scope in [globals(), locals()]:
            for name, value in scope.items():
                generation_args[name] = value
    
        template = MarkupTemplate(html)
        stream = template.generate(**generation_args)
    
        print(stream.render('html'))
    
    main()