Code Monkey home page Code Monkey logo

kajiki's People

Contributors

alfredodeza avatar amol- avatar castixgithub avatar ddaanet avatar drocco007 avatar fahhem avatar frou avatar jackrosenthal avatar moreati avatar mouchar avatar nandoflorestan avatar ollyc avatar pedersen avatar pokoli avatar ralphbean avatar rick446 avatar scmmmh avatar timgates42 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

kajiki's Issues

Python2 support using nine

Python2 counter is ticking, but kajiki still needs python-nine to run on python3. Looks like there is no release of nine after year 2016. Do you think, that kajiki still needs python-nine?

Currently build of kajiki in Fedora 32 (development) is broken due to problems with python-nine. I suggest to release a new version of kajiki, which will not rely on python-nine.

CDATA sections not preserved

Unfortunately, Kajiki doesn't handle CDATA sections correctly. This causes problems with non-HTML XML documents. At the moment the CDATA markers are omitted and valid content may get translated to HTML entity references:

>>> import kajiki
>>> kajiki.XMLTemplate(u'''<myxml><data><![CDATA[&gt;&#240; $foo]]></data></myxml>''')(dict(foo='bar')).render()
u'<myxml><data>&amp;gt;&amp;#240; bar</data></myxml>'

To avoid unexpected behavior Kajiki should preserve CDATA markers and the content of CDATA sections shouldn't be modified, except for the expansion of variables.

Many HTML entities don't work

Many HTML entities i.e. … cause KeyError:

Try:
kajiki.XMLTemplate(u'<div>&copy; &hellip;</div>')

Result:
Traceback (most recent call last): File "<input>", line 1, in <module> Template = kajiki.XMLTemplate(u'<div>&copy; &hellip;</div>') File "/usr/lib/python2.7/dist-packages/kajiki/xml_template.py", line 52, in XMLTemplate doc = _Parser(filename, source).parse() File "/usr/lib/python2.7/dist-packages/kajiki/xml_template.py", line 629, in parse parser.parse(source) File "/usr/lib/python2.7/xml/sax/expatreader.py", line 110, in parse xmlreader.IncrementalParser.parse(self, source) File "/usr/lib/python2.7/xml/sax/xmlreader.py", line 123, in parse self.feed(buffer) File "/usr/lib/python2.7/xml/sax/expatreader.py", line 213, in feed self._parser.Parse(data, isFinal) File "/usr/lib/python2.7/xml/sax/expatreader.py", line 416, in skipped_entity_handler self._cont_handler.skippedEntity(name) File "/usr/lib/python2.7/dist-packages/kajiki/xml_template.py", line 675, in skippedEntity return self.characters(html5[name]) KeyError: u'hellip'

Kajiki splits text blocks apart into individual lines for translating

When I extract translatable strings from templates, Kajiki splits text blocks into individual lines (destroying paragraph context). Take a template containing a paragraph like this one:

...
  <p>
    A cookie is a small text file that a website saves on your computer or
    mobile device when you visit the site. It enables the website to remember
    your actions and preferences (such as login, language, font size and
    other display preferences) over a period of time, so you don’t have to
    keep re-entering them whenever you come back to the site or browse from
    one page to another.
  </p>
...

Here's what python setup.py extract_messages produces with Genshi:

...
#: project/templates/cookies.html:19
msgid ""
"A cookie is a small text file that a website saves on your computer or\n"
"    mobile device when you visit the site. It enables the website to "
"remember\n"
"    your actions and preferences (such as login, language, font size and\n"
"    other display preferences) over a period of time, so you don’t have to\n"
"    keep re-entering them whenever you come back to the site or browse from\n"
"    one page to another."
msgstr ""
...

The whitespace and newlines aren't that pretty, but it's one continuous string. Here's what Kajiki makes of it:

...
#: project/templates/cookies.xhtml:18
msgid "    A cookie is a small text file that a website saves on your computer or"
msgstr ""

#: project/templates/cookies.xhtml:19
msgid "    mobile device when you visit the site. It enables the website to remember"
msgstr ""

#: project/templates/cookies.xhtml:20
msgid "    your actions and preferences (such as login, language, font size and"
msgstr ""

#: project/templates/cookies.xhtml:21
msgid "    other display preferences) over a period of time, so you don’t have to"
msgstr ""

#: project/templates/cookies.xhtml:22
msgid "    keep re-entering them whenever you come back to the site or browse from"
msgstr ""

#: project/templates/cookies.xhtml:23
msgid "    one page to another."
msgstr ""
...

Tools like poedit don't honor the order in which msgids are in a PO file. That really makes it unusable for me. :(

Kajiki needs a new owner?

@amol-

A few issues were added today by @jackrosenthal which are pretty essential for the health of the Kajiki project. I am not coming back as a maintainer of Kajiki, but I know it deserves to be well maintained. Essentially I wish to responsibly remove myself. Are you interested in a transfer of ownership to you, or should we open this up for takers?

missing the transformation filters

hello friends,

and many thanks for providing kajiki.

with genshi, i was using transformation filters,
that allowed me to give a last touch to some tags' attributes.

how could i hook a callback in kajiki template _render_attrs?
how could _render_attrs know the tag name it was called for?
should such callback be hooked somewhere else?

thanks in advance,
alex

migrate to pytest?

nose v1 hasn't seen any new updates since 2016, and compatibility appears to be broken in Python 3.10.

pytest is the hot thing these days, and appears to already find and run the tests just fine...

switch the test runner from nose to pytest?

XML comments outside root node cause py:else to raise XMLTemplateCompileError

Adding XML comment using <!-- comment --> outside the root node seems to cause an unnecessary and confusing exception in py:else handling.

d="""
<!-- stuff -->
<a>
  <py:switch test="val">
    <py:case value="1">2</py:case>
    <py:else>1</py:else>
  </py:switch>
</a>
"""
>>> dd=kajiki.XMLTemplate(d)
...
kajiki.xml_template.XMLTemplateCompileError: [<string>:6] py:else directive must be inside a py:switch or directly after py:if without text or spaces in between

Without py:else this works fine:

>>> e="""
<!-- stuff -->
<a>
  <py:switch test="val">
    <py:case value="1">2</py:case>
  </py:switch>
</a>
"""
>>> ee=kajiki.XMLTemplate(e)
>>> print(ee(dict(val=1)).render())
<a>
  2
</a>
>>>

With py:else and without XML comment also works fine

>>> f="""
<a>
 <py:switch test="val">
   <py:case value="1">2</py:case>
   <py:else>1</py:else>
 </py:switch>
</a>"""
>>> ff=kajiki.XMLTemplate(f)
>>> print(ff(dict(val=1)).render())
<a>
   2
</a>
>>> print(ff(dict(val=7)).render())
<a>
   1
</a>

py:switch always outputs the content of py:else

Hi,

I've run into a problem with the py:switch code. If I define a py:switch with more than two py:case and then a py:else, the content of the py:else is always output, except if the final py:case is True.

txt = u'''<div>
<py:switch test="category">
  <py:case value="'a'">A</py:case>
  <py:case value="'b'">B</py:case>
  <py:else>O</py:else>
</py:switch>
</div>'''
tmpl = XMLTemplate(txt)

With that base code

tmpl({'category': 'a'}).render()

should output "

A
", but outputs "
AO
". However

tmpl({'category': 'b'}).render()

correctly outputs "

B
" as it should. It seems that only the last py:case is being considered to determine whether to run the py:else. Possibly all py:case are outputting "if(condition)" rather than "elif(condition)" for everything but the first py:case.

drop support for Python 2.6, 2.7, 3.0-3.3?

There's considerable cleanup that could be done if we stop supporting these versions of Python.

These days they are probably mostly antiques... any oppositions to dropping them?

travis-ci.org shutting down

The CI system appears to have a scary banner on it that it may be shutting down in a few days:

Hi! It seems there are still plenty of builds running. We're pushing back the shutdown date to June 15th. Please consider migrating to travis-ci.com.

Does something need done by the repo admins here to move it to travis-ci.com?

KajikiSyntaxError not raised when a function gets called like ${echo('hello'))}

I wrote a test in TestFunction in test_xml.py

def test_function_call_double_parenthesis(self):
    perform('''<div py:strip="True"
><py:def function="echo(x)">$x</py:def
>${echo('hello'))}</div>''', 'hello')

it fails in this way:

FAIL: test_function_double_parenthesis (kajiki.tests.test_xml.TestFunction)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/castix/axant/kajiki/kajiki/tests/test_xml.py", line 375, in test_function_double_parenthesis
    >${echo('hello'))}</div>''', 'hello')
  File "/home/castix/axant/kajiki/kajiki/tests/test_xml.py", line 78, in perform
    assert rsp == expected_output, (rsp, expected_output)
AssertionError: (u'hello}', u'hello')
-------------------- >> begin captured stdout << ---------------------

class template:
    @kajiki.expose
    def __main__():
        if not (True): yield u'<div>'
        yield self.__kj__.escape(echo('hello'))
        yield local.__kj__.gettext(u'}')
        if not (True): yield u'</div>'
    @kajiki.expose
    def echo(x):
        yield self.__kj__.escape(x)
template = kajiki.Template(template)

as the call is ${echo('hello'))} i would expect a KajikiSyntaxError to be raised instead of have that extra }
Thanks :)

Lack of code documentation

I've been using Kajiki on two projects recently and found it really nice to use. However, at the moment there is a significant lack of code documentation, which makes it a lot harder to contribute to the project, particularly when I run into bugs. It would be nice if some time could be spent on the documentation, then I could provide pull-requests with fixes rather than just bug-reports.

Python 3.10 compatibility error

Unable to use kajiki on python3.10b2 (b1 works, b2 don't).
Here is an example:

import kajiki

tpl = kajiki.TextTemplate('''%def quote(caller, speaker)
    %for i in range(2)
Quoth $speaker, "${caller(i)}."
    %end
%end
%call(n) quote(%caller ,'the raven')
Nevermore $n\\
%end''')

Raises an exception:

Traceback (most recent call last):
  File "/root/Kajiki-0.8.2/kajiki/template.py", line 323, in from_ir
    exec(py_text, dct)
  File "<string>", line 17
    yield caller(i)}.
                   ^
SyntaxError: unmatched '}'

Looks like problem is in kajiki/text.py:
compile(self.source[self.pos:], '', 'eval'),
and python3.10 better error handling. I don't know, how to fix this, compile raises error from end of string instead of from start.

Short python example:

Python 3.10.0b1 (default, May  4 2021, 00:00:00) [GCC 11.1.1 20210428 (Red Hat 11.1.1-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> compile('caller(i)}."', '', 'eval')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "", line 1
    caller(i)}."
             ^
SyntaxError: unmatched '}'
Python 3.10.0b2 (default, Jun  1 2021, 00:00:00) [GCC 11.1.1 20210531 (Red Hat 11.1.1-3)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> compile('caller(i)}."', '', 'eval')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "", line 1
    caller(i)}."
               ^
SyntaxError: unterminated string literal (detected at line 1)
>>>

Any volunteer, who can fix this? Fedora's kajiki build is broken. :-(

Command Line Interface

This is a feature request. I'm willing to work on this feature and submit a PR if the maintainers would find this valuable.

Essentially, it seems that on occasion it's convenient to use Kajiki from a bash script (or something similar) and would be convenient to have an entry point for this. For example, it could be used to format emails from mutt.

I was thinking of some sort of interface like this:

kajiki [--xml] [input_file] [output_file]

input_file and output_file could default to stdin and stdout respectively.

Is this in the maintainers' interest?

Can be attribute values generated by function?

During my evaluation of Kajiki benefits (compared to Genshi) I have found following problem:

>>> Template = kajiki.XMLTemplate(u'<html><py:def function="attrtest()">Some value</py:def><img src="${attrtest()}"/></html>')
>>> print(Template().render())
<html><img src="&lt;kajiki.util.flattener object at 0xf82a50&gt;"/></html>
>>> Expected result was:
<html><img src="Some value"/></html>

Kajiki apparently tried to evaluate attrtest() function, but it didn't went well...
My env.: Python 2.6.6, Kajiki==0.5.5

XML loader broken on Python 2.x

It looks like the File loader broke during the porting process.

https://github.com/nandoflorestan/kajiki/blob/master/kajiki/loader.py#L92 opens the file in binary mode, this leads to a "str" type being returned on Python2.

Then the resulting object gets passed to the XMLTemplate initialization function which builds _Parser with the given source.

The _Parser init method checks for the source being an instance of str at https://github.com/nandoflorestan/kajiki/blob/master/kajiki/xml_template.py#L395
but at https://github.com/nandoflorestan/kajiki/blob/master/kajiki/xml_template.py#L9 str has been patched to actually mean "unicode" so the check fails.

This has been found due to the TurboGears2 test suite failing with Kajiki 0.4 https://travis-ci.org/TurboGears/tg2/jobs/9749531#L813

Python 3.8 support

  File "/home/travis/virtualenv/python3.8-dev/lib/python3.8/site-packages/kajiki/loader.py", line 78, in import_
    return super(FileLoader, self).import_(name, *args, **kwargs)
  File "/home/travis/virtualenv/python3.8-dev/lib/python3.8/site-packages/kajiki/loader.py", line 21, in import_
    mod = self._load(name, *args, **kwargs)
  File "/home/travis/virtualenv/python3.8-dev/lib/python3.8/site-packages/kajiki/loader.py", line 97, in _load
    return XMLTemplate(filename=filename,
  File "/home/travis/virtualenv/python3.8-dev/lib/python3.8/site-packages/kajiki/xml_template.py", line 56, in XMLTemplate
    t = template.from_ir(ir_, base_globals=base_globals)
  File "/home/travis/virtualenv/python3.8-dev/lib/python3.8/site-packages/kajiki/template.py", line 323, in from_ir
    tpl.annotate_lnotab(py_linenos)
  File "/home/travis/virtualenv/python3.8-dev/lib/python3.8/site-packages/kajiki/template.py", line 257, in annotate_lnotab
    meth.annotate_lnotab(cls.filename, py_to_tpl, dict(py_to_tpl))
  File "/home/travis/virtualenv/python3.8-dev/lib/python3.8/site-packages/kajiki/template.py", line 389, in annotate_lnotab
    new_code = patch_code_file_lines(
  File "/home/travis/virtualenv/python3.8-dev/lib/python3.8/site-packages/kajiki/template.py", line 413, in patch_code_file_lines
    return types.CodeType(code.co_argcount,
TypeError: an integer is required (got type bytes)

py:if and "lower than" comparison

Hi,

after searching for a while I found the solution my self. However, I believe this should make it to the docs.

I was struggling with implementing a py:if comparison like x < 2. The naive way

Template = kajiki.XMLTemplate('''
<html><body>
  <div py:if="x < 2">$title</div>
</body></html>''')

yields an XMLTemplateParseError. This is perfectly correct since the < is a reserves letter in XML. Realizing that, the solution is as simple as replacing < with &lt;.

In doing so I get:

print(Template(dict(title='Kajiki is teh awesome!', x=1)).render())
<html><body>
  <div>Kajiki is teh awesome!</div>
</body></html>

i18n strings extraction wrongly picking CSS style tags

Got

#: ticketslist/templates/master.xhtml:12
msgid "    .project-navbar {"
msgstr ""

#: ticketslist/templates/master.xhtml:13
msgid "      background-color: #323A45;"
msgstr ""

#: ticketslist/templates/master.xhtml:14 ticketslist/templates/master.xhtml:18
#: ticketslist/templates/master.xhtml:22
msgid "      color: white;"
msgstr ""

#: ticketslist/templates/master.xhtml:15
msgid "      border-bottom: 4px solid #D72749;"
msgstr ""

#: ticketslist/templates/master.xhtml:16 ticketslist/templates/master.xhtml:20
#: ticketslist/templates/master.xhtml:23
#: ticketslist/templates/ticketlist/fill.xhtml:68
msgid "    }"
msgstr ""

when running on a master.xhtml containing

  <style>
    .project-navbar {
      background-color: #323A45;
      color: white;
      border-bottom: 4px solid #D72749;
    }
 </style>

 

Jinja & Django like template filters?

A commonly used template feature is filters in the form somevalue | filter | filter(option) to transform the value in various ways. Usually to format it properly for rendering.

In Jinja this is provided through a specific piece of parser and compiler that parse | to a filter.

The same seems to me can be achieved by not parsing filters at all and forcing them to subclass a common base class that relies on __ror__ like::

class TemplateFilter(object):
    """Base Class for all filters"""
    def apply(self, value, *args, **kwargs):
      raise NotImplementedError('Filters must implement this')
    def __init__(self, *args, **kwargs):
      self.args, self.kwargs = args, kwargs
    def __call__(self, *args, **kwargs):
      return self.__class__(*args, **kwargs)
    def __ror__(self, input):
      return self.apply(input, *self.args, **self.kwargs)


class WrapWithFilter(TemplateFilter):
    """Wraps the whole content in a given tag"""
    def apply(self, value, tag):
      return '<{tag}>{value}</{tag}>'.format(tag=tag, value=value)

Than "something" | WrapWithFilter('strong') will render to <strong>something</strong>

@nandoflorestan @rick446 do you think there is any use case we can only achieve by implementing filters at parser/compiler level? Because I see the implementation based on operator overloading far easier to achieve and maintain.

Top comment causes Kajiki to discard the rest of the document

In [1]: from genshi.template import MarkupTemplate

In [2]: from kajiki import XMLTemplate

In [3]: MarkupTemplate('<!-- whatever --><x>${1+1}</x>').generate().render()
Out[3]: '<!-- whatever --><x>2</x>'

In [4]: XMLTemplate('<!-- whatever --><x>${1+1}</x>')().render()
Out[4]: '<!--  whatever  -->'

XML template with declaration <?xml version="1.0"?> becomes invalid due to internal Kajiki manipulation

I use xmllint --format to automatically format XML files (much like black for Python code, csscomb for CSS, etc).

That autoformatter always inserts <?xml version="1.0"?> at the beginning of the XML file, which as far as I know is perfectly valid thing to do and is called the XML declaration:

<?xml version="1.0"?>
<html>
  <head>
    ...

The issue is that some internal manipulation of the XML by Kajiki inserts a custom DOCTYPE before that XML-Declaration, rather than after it, making the XML invalid:

kajiki.xml_template.XMLTemplateParseError: [resources/templates/home.xml:1] XML or text declaration not at start of entity
	     
	 --> <!DOCTYPE kajiki SYSTEM "kajiki.dtd"><?xml version="1.0"?>
	     <html>
	       <head>

If the result of the manipulation was instead:

<?xml version="1.0"?><!DOCTYPE kajiki SYSTEM "kajiki.dtd">
...

or:

<?xml version="1.0"?>
<!DOCTYPE kajiki SYSTEM "kajiki.dtd">
...

...then presumably the XML would still be valid and the template parsing would succeed.

0.9.0: pytest is failing

I'm trying to package your module as an rpm package. So I'm using the typical PEP517 based build, install and test cycle used on building packages from non-root account.

  • python3 -sBm build -w
  • install .whl file in </install/prefix>
  • run pytest with PYTHONPATH pointing to sitearch and sitelib inside </install/prefix>

I have alredy packaged almost 800 python modules ane to be honest I see first time such errors. Liooks like something is wrong with importing modules.
May I ask for some help?
Here is pytest output:

+ PYTHONPATH=/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib64/python3.8/site-packages:/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python3.8/site-packages
+ /usr/bin/pytest -ra --import-mode=importlib
=========================================================================== test session starts ============================================================================
platform linux -- Python 3.8.12, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /home/tkloczko/rpmbuild/BUILD/kajiki-0.9.0, configfile: pytest.ini
collected 158 items / 20 errors / 138 selected

================================================================================== ERRORS ==================================================================================
___________________________________________________________________ ERROR collecting kajiki/__main__.py ____________________________________________________________________
/usr/lib/python3.8/site-packages/_pytest/runner.py:311: in from_call
    result: Optional[TResult] = func()
/usr/lib/python3.8/site-packages/_pytest/runner.py:341: in <lambda>
    call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
/usr/lib/python3.8/site-packages/_pytest/doctest.py:532: in collect
    module = import_path(self.fspath)
/usr/lib/python3.8/site-packages/_pytest/pathlib.py:544: in import_path
    raise ImportPathMismatchError(module_name, module_file, path)
E   _pytest.pathlib.ImportPathMismatchError: ('kajiki.__main__', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python3.8/site-packages/kajiki/__main__.py', PosixPath('/home/tkloczko/rpmbuild/BUILD/kajiki-0.9.0/kajiki/__main__.py'))
____________________________________________________________________ ERROR collecting kajiki/doctype.py ____________________________________________________________________
/usr/lib/python3.8/site-packages/_pytest/runner.py:311: in from_call
    result: Optional[TResult] = func()
/usr/lib/python3.8/site-packages/_pytest/runner.py:341: in <lambda>
    call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
/usr/lib/python3.8/site-packages/_pytest/doctest.py:532: in collect
    module = import_path(self.fspath)
/usr/lib/python3.8/site-packages/_pytest/pathlib.py:544: in import_path
    raise ImportPathMismatchError(module_name, module_file, path)
E   _pytest.pathlib.ImportPathMismatchError: ('kajiki.doctype', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python3.8/site-packages/kajiki/doctype.py', PosixPath('/home/tkloczko/rpmbuild/BUILD/kajiki-0.9.0/kajiki/doctype.py'))
__________________________________________________________________ ERROR collecting kajiki/html_utils.py ___________________________________________________________________
/usr/lib/python3.8/site-packages/_pytest/runner.py:311: in from_call
    result: Optional[TResult] = func()
/usr/lib/python3.8/site-packages/_pytest/runner.py:341: in <lambda>
    call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
/usr/lib/python3.8/site-packages/_pytest/doctest.py:532: in collect
    module = import_path(self.fspath)
/usr/lib/python3.8/site-packages/_pytest/pathlib.py:544: in import_path
    raise ImportPathMismatchError(module_name, module_file, path)
E   _pytest.pathlib.ImportPathMismatchError: ('kajiki.html_utils', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python3.8/site-packages/kajiki/html_utils.py', PosixPath('/home/tkloczko/rpmbuild/BUILD/kajiki-0.9.0/kajiki/html_utils.py'))
_____________________________________________________________________ ERROR collecting kajiki/i18n.py ______________________________________________________________________
/usr/lib/python3.8/site-packages/_pytest/runner.py:311: in from_call
    result: Optional[TResult] = func()
/usr/lib/python3.8/site-packages/_pytest/runner.py:341: in <lambda>
    call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
/usr/lib/python3.8/site-packages/_pytest/doctest.py:532: in collect
    module = import_path(self.fspath)
/usr/lib/python3.8/site-packages/_pytest/pathlib.py:544: in import_path
    raise ImportPathMismatchError(module_name, module_file, path)
E   _pytest.pathlib.ImportPathMismatchError: ('kajiki.i18n', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python3.8/site-packages/kajiki/i18n.py', PosixPath('/home/tkloczko/rpmbuild/BUILD/kajiki-0.9.0/kajiki/i18n.py'))
______________________________________________________________________ ERROR collecting kajiki/ir.py _______________________________________________________________________
/usr/lib/python3.8/site-packages/_pytest/runner.py:311: in from_call
    result: Optional[TResult] = func()
/usr/lib/python3.8/site-packages/_pytest/runner.py:341: in <lambda>
    call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
/usr/lib/python3.8/site-packages/_pytest/doctest.py:532: in collect
    module = import_path(self.fspath)
/usr/lib/python3.8/site-packages/_pytest/pathlib.py:544: in import_path
    raise ImportPathMismatchError(module_name, module_file, path)
E   _pytest.pathlib.ImportPathMismatchError: ('kajiki.ir', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python3.8/site-packages/kajiki/ir.py', PosixPath('/home/tkloczko/rpmbuild/BUILD/kajiki-0.9.0/kajiki/ir.py'))
____________________________________________________________________ ERROR collecting kajiki/lnotab.py _____________________________________________________________________
/usr/lib/python3.8/site-packages/_pytest/runner.py:311: in from_call
    result: Optional[TResult] = func()
/usr/lib/python3.8/site-packages/_pytest/runner.py:341: in <lambda>
    call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
/usr/lib/python3.8/site-packages/_pytest/doctest.py:532: in collect
    module = import_path(self.fspath)
/usr/lib/python3.8/site-packages/_pytest/pathlib.py:544: in import_path
    raise ImportPathMismatchError(module_name, module_file, path)
E   _pytest.pathlib.ImportPathMismatchError: ('kajiki.lnotab', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python3.8/site-packages/kajiki/lnotab.py', PosixPath('/home/tkloczko/rpmbuild/BUILD/kajiki-0.9.0/kajiki/lnotab.py'))
____________________________________________________________________ ERROR collecting kajiki/loader.py _____________________________________________________________________
/usr/lib/python3.8/site-packages/_pytest/runner.py:311: in from_call
    result: Optional[TResult] = func()
/usr/lib/python3.8/site-packages/_pytest/runner.py:341: in <lambda>
    call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
/usr/lib/python3.8/site-packages/_pytest/doctest.py:532: in collect
    module = import_path(self.fspath)
/usr/lib/python3.8/site-packages/_pytest/pathlib.py:544: in import_path
    raise ImportPathMismatchError(module_name, module_file, path)
E   _pytest.pathlib.ImportPathMismatchError: ('kajiki.loader', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python3.8/site-packages/kajiki/loader.py', PosixPath('/home/tkloczko/rpmbuild/BUILD/kajiki-0.9.0/kajiki/loader.py'))
________________________________________________________________ ERROR collecting kajiki/markup_template.py ________________________________________________________________
/usr/lib/python3.8/site-packages/_pytest/runner.py:311: in from_call
    result: Optional[TResult] = func()
/usr/lib/python3.8/site-packages/_pytest/runner.py:341: in <lambda>
    call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
/usr/lib/python3.8/site-packages/_pytest/doctest.py:532: in collect
    module = import_path(self.fspath)
/usr/lib/python3.8/site-packages/_pytest/pathlib.py:544: in import_path
    raise ImportPathMismatchError(module_name, module_file, path)
E   _pytest.pathlib.ImportPathMismatchError: ('kajiki.markup_template', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python3.8/site-packages/kajiki/markup_template.py', PosixPath('/home/tkloczko/rpmbuild/BUILD/kajiki-0.9.0/kajiki/markup_template.py'))
___________________________________________________________________ ERROR collecting kajiki/template.py ____________________________________________________________________
/usr/lib/python3.8/site-packages/_pytest/runner.py:311: in from_call
    result: Optional[TResult] = func()
/usr/lib/python3.8/site-packages/_pytest/runner.py:341: in <lambda>
    call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
/usr/lib/python3.8/site-packages/_pytest/doctest.py:532: in collect
    module = import_path(self.fspath)
/usr/lib/python3.8/site-packages/_pytest/pathlib.py:544: in import_path
    raise ImportPathMismatchError(module_name, module_file, path)
E   _pytest.pathlib.ImportPathMismatchError: ('kajiki.template', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python3.8/site-packages/kajiki/template.py', PosixPath('/home/tkloczko/rpmbuild/BUILD/kajiki-0.9.0/kajiki/template.py'))
_____________________________________________________________________ ERROR collecting kajiki/text.py ______________________________________________________________________
/usr/lib/python3.8/site-packages/_pytest/runner.py:311: in from_call
    result: Optional[TResult] = func()
/usr/lib/python3.8/site-packages/_pytest/runner.py:341: in <lambda>
    call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
/usr/lib/python3.8/site-packages/_pytest/doctest.py:532: in collect
    module = import_path(self.fspath)
/usr/lib/python3.8/site-packages/_pytest/pathlib.py:544: in import_path
    raise ImportPathMismatchError(module_name, module_file, path)
E   _pytest.pathlib.ImportPathMismatchError: ('kajiki.text', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python3.8/site-packages/kajiki/text.py', PosixPath('/home/tkloczko/rpmbuild/BUILD/kajiki-0.9.0/kajiki/text.py'))
_____________________________________________________________________ ERROR collecting kajiki/util.py ______________________________________________________________________
/usr/lib/python3.8/site-packages/_pytest/runner.py:311: in from_call
    result: Optional[TResult] = func()
/usr/lib/python3.8/site-packages/_pytest/runner.py:341: in <lambda>
    call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
/usr/lib/python3.8/site-packages/_pytest/doctest.py:532: in collect
    module = import_path(self.fspath)
/usr/lib/python3.8/site-packages/_pytest/pathlib.py:544: in import_path
    raise ImportPathMismatchError(module_name, module_file, path)
E   _pytest.pathlib.ImportPathMismatchError: ('kajiki.util', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python3.8/site-packages/kajiki/util.py', PosixPath('/home/tkloczko/rpmbuild/BUILD/kajiki-0.9.0/kajiki/util.py'))
____________________________________________________________________ ERROR collecting kajiki/version.py ____________________________________________________________________
/usr/lib/python3.8/site-packages/_pytest/runner.py:311: in from_call
    result: Optional[TResult] = func()
/usr/lib/python3.8/site-packages/_pytest/runner.py:341: in <lambda>
    call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
/usr/lib/python3.8/site-packages/_pytest/doctest.py:532: in collect
    module = import_path(self.fspath)
/usr/lib/python3.8/site-packages/_pytest/pathlib.py:544: in import_path
    raise ImportPathMismatchError(module_name, module_file, path)
E   _pytest.pathlib.ImportPathMismatchError: ('kajiki.version', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python3.8/site-packages/kajiki/version.py', PosixPath('/home/tkloczko/rpmbuild/BUILD/kajiki-0.9.0/kajiki/version.py'))
_________________________________________________________________ ERROR collecting kajiki/xml_template.py __________________________________________________________________
/usr/lib/python3.8/site-packages/_pytest/runner.py:311: in from_call
    result: Optional[TResult] = func()
/usr/lib/python3.8/site-packages/_pytest/runner.py:341: in <lambda>
    call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
/usr/lib/python3.8/site-packages/_pytest/doctest.py:532: in collect
    module = import_path(self.fspath)
/usr/lib/python3.8/site-packages/_pytest/pathlib.py:544: in import_path
    raise ImportPathMismatchError(module_name, module_file, path)
E   _pytest.pathlib.ImportPathMismatchError: ('kajiki.xml_template', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python3.8/site-packages/kajiki/xml_template.py', PosixPath('/home/tkloczko/rpmbuild/BUILD/kajiki-0.9.0/kajiki/xml_template.py'))
________________________________________________________________ ERROR collecting kajiki/tests/test_cli.py _________________________________________________________________
/usr/lib/python3.8/site-packages/_pytest/runner.py:311: in from_call
    result: Optional[TResult] = func()
/usr/lib/python3.8/site-packages/_pytest/runner.py:341: in <lambda>
    call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
/usr/lib/python3.8/site-packages/_pytest/doctest.py:532: in collect
    module = import_path(self.fspath)
/usr/lib/python3.8/site-packages/_pytest/pathlib.py:544: in import_path
    raise ImportPathMismatchError(module_name, module_file, path)
E   _pytest.pathlib.ImportPathMismatchError: ('kajiki.tests.test_cli', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python3.8/site-packages/kajiki/tests/test_cli.py', PosixPath('/home/tkloczko/rpmbuild/BUILD/kajiki-0.9.0/kajiki/tests/test_cli.py'))
______________________________________________________________ ERROR collecting kajiki/tests/test_doctype.py _______________________________________________________________
/usr/lib/python3.8/site-packages/_pytest/runner.py:311: in from_call
    result: Optional[TResult] = func()
/usr/lib/python3.8/site-packages/_pytest/runner.py:341: in <lambda>
    call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
/usr/lib/python3.8/site-packages/_pytest/doctest.py:532: in collect
    module = import_path(self.fspath)
/usr/lib/python3.8/site-packages/_pytest/pathlib.py:544: in import_path
    raise ImportPathMismatchError(module_name, module_file, path)
E   _pytest.pathlib.ImportPathMismatchError: ('kajiki.tests.test_doctype', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python3.8/site-packages/kajiki/tests/test_doctype.py', PosixPath('/home/tkloczko/rpmbuild/BUILD/kajiki-0.9.0/kajiki/tests/test_doctype.py'))
________________________________________________________________ ERROR collecting kajiki/tests/test_e2e.py _________________________________________________________________
/usr/lib/python3.8/site-packages/_pytest/runner.py:311: in from_call
    result: Optional[TResult] = func()
/usr/lib/python3.8/site-packages/_pytest/runner.py:341: in <lambda>
    call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
/usr/lib/python3.8/site-packages/_pytest/doctest.py:532: in collect
    module = import_path(self.fspath)
/usr/lib/python3.8/site-packages/_pytest/pathlib.py:544: in import_path
    raise ImportPathMismatchError(module_name, module_file, path)
E   _pytest.pathlib.ImportPathMismatchError: ('kajiki.tests.test_e2e', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python3.8/site-packages/kajiki/tests/test_e2e.py', PosixPath('/home/tkloczko/rpmbuild/BUILD/kajiki-0.9.0/kajiki/tests/test_e2e.py'))
_________________________________________________________________ ERROR collecting kajiki/tests/test_ir.py _________________________________________________________________
/usr/lib/python3.8/site-packages/_pytest/runner.py:311: in from_call
    result: Optional[TResult] = func()
/usr/lib/python3.8/site-packages/_pytest/runner.py:341: in <lambda>
    call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
/usr/lib/python3.8/site-packages/_pytest/doctest.py:532: in collect
    module = import_path(self.fspath)
/usr/lib/python3.8/site-packages/_pytest/pathlib.py:544: in import_path
    raise ImportPathMismatchError(module_name, module_file, path)
E   _pytest.pathlib.ImportPathMismatchError: ('kajiki.tests.test_ir', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python3.8/site-packages/kajiki/tests/test_ir.py', PosixPath('/home/tkloczko/rpmbuild/BUILD/kajiki-0.9.0/kajiki/tests/test_ir.py'))
______________________________________________________________ ERROR collecting kajiki/tests/test_runtime.py _______________________________________________________________
/usr/lib/python3.8/site-packages/_pytest/runner.py:311: in from_call
    result: Optional[TResult] = func()
/usr/lib/python3.8/site-packages/_pytest/runner.py:341: in <lambda>
    call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
/usr/lib/python3.8/site-packages/_pytest/doctest.py:532: in collect
    module = import_path(self.fspath)
/usr/lib/python3.8/site-packages/_pytest/pathlib.py:544: in import_path
    raise ImportPathMismatchError(module_name, module_file, path)
E   _pytest.pathlib.ImportPathMismatchError: ('kajiki.tests.test_runtime', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python3.8/site-packages/kajiki/tests/test_runtime.py', PosixPath('/home/tkloczko/rpmbuild/BUILD/kajiki-0.9.0/kajiki/tests/test_runtime.py'))
________________________________________________________________ ERROR collecting kajiki/tests/test_text.py ________________________________________________________________
/usr/lib/python3.8/site-packages/_pytest/runner.py:311: in from_call
    result: Optional[TResult] = func()
/usr/lib/python3.8/site-packages/_pytest/runner.py:341: in <lambda>
    call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
/usr/lib/python3.8/site-packages/_pytest/doctest.py:532: in collect
    module = import_path(self.fspath)
/usr/lib/python3.8/site-packages/_pytest/pathlib.py:544: in import_path
    raise ImportPathMismatchError(module_name, module_file, path)
E   _pytest.pathlib.ImportPathMismatchError: ('kajiki.tests.test_text', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python3.8/site-packages/kajiki/tests/test_text.py', PosixPath('/home/tkloczko/rpmbuild/BUILD/kajiki-0.9.0/kajiki/tests/test_text.py'))
________________________________________________________________ ERROR collecting kajiki/tests/test_xml.py _________________________________________________________________
/usr/lib/python3.8/site-packages/_pytest/runner.py:311: in from_call
    result: Optional[TResult] = func()
/usr/lib/python3.8/site-packages/_pytest/runner.py:341: in <lambda>
    call = CallInfo.from_call(lambda: list(collector.collect()), "collect")
/usr/lib/python3.8/site-packages/_pytest/doctest.py:532: in collect
    module = import_path(self.fspath)
/usr/lib/python3.8/site-packages/_pytest/pathlib.py:544: in import_path
    raise ImportPathMismatchError(module_name, module_file, path)
E   _pytest.pathlib.ImportPathMismatchError: ('kajiki.tests.test_xml', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python3.8/site-packages/kajiki/tests/test_xml.py', PosixPath('/home/tkloczko/rpmbuild/BUILD/kajiki-0.9.0/kajiki/tests/test_xml.py'))
========================================================================= short test summary info ==========================================================================
ERROR kajiki/__main__.py - _pytest.pathlib.ImportPathMismatchError: ('kajiki.__main__', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/pyth...
ERROR kajiki/doctype.py - _pytest.pathlib.ImportPathMismatchError: ('kajiki.doctype', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python...
ERROR kajiki/html_utils.py - _pytest.pathlib.ImportPathMismatchError: ('kajiki.html_utils', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/...
ERROR kajiki/i18n.py - _pytest.pathlib.ImportPathMismatchError: ('kajiki.i18n', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python3.8/si...
ERROR kajiki/ir.py - _pytest.pathlib.ImportPathMismatchError: ('kajiki.ir', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python3.8/site-p...
ERROR kajiki/lnotab.py - _pytest.pathlib.ImportPathMismatchError: ('kajiki.lnotab', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python3....
ERROR kajiki/loader.py - _pytest.pathlib.ImportPathMismatchError: ('kajiki.loader', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python3....
ERROR kajiki/markup_template.py - _pytest.pathlib.ImportPathMismatchError: ('kajiki.markup_template', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_6...
ERROR kajiki/template.py - _pytest.pathlib.ImportPathMismatchError: ('kajiki.template', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/pyth...
ERROR kajiki/text.py - _pytest.pathlib.ImportPathMismatchError: ('kajiki.text', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python3.8/si...
ERROR kajiki/util.py - _pytest.pathlib.ImportPathMismatchError: ('kajiki.util', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python3.8/si...
ERROR kajiki/version.py - _pytest.pathlib.ImportPathMismatchError: ('kajiki.version', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/lib/python...
ERROR kajiki/xml_template.py - _pytest.pathlib.ImportPathMismatchError: ('kajiki.xml_template', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/usr/...
ERROR kajiki/tests/test_cli.py - _pytest.pathlib.ImportPathMismatchError: ('kajiki.tests.test_cli', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/...
ERROR kajiki/tests/test_doctype.py - _pytest.pathlib.ImportPathMismatchError: ('kajiki.tests.test_doctype', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35...
ERROR kajiki/tests/test_e2e.py - _pytest.pathlib.ImportPathMismatchError: ('kajiki.tests.test_e2e', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/...
ERROR kajiki/tests/test_ir.py - _pytest.pathlib.ImportPathMismatchError: ('kajiki.tests.test_ir', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/us...
ERROR kajiki/tests/test_runtime.py - _pytest.pathlib.ImportPathMismatchError: ('kajiki.tests.test_runtime', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35...
ERROR kajiki/tests/test_text.py - _pytest.pathlib.ImportPathMismatchError: ('kajiki.tests.test_text', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_6...
ERROR kajiki/tests/test_xml.py - _pytest.pathlib.ImportPathMismatchError: ('kajiki.tests.test_xml', '/home/tkloczko/rpmbuild/BUILDROOT/python-kajiki-0.9.0-5.fc35.x86_64/...
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Interrupted: 20 errors during collection !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
============================================================================ 20 errors in 1.75s ===========================================================================

Dot-notation for __getitem__ is not supported

In [1]: foo = dict(bar = 'baz')

In [2]: from genshi.template import NewTextTemplate

In [3]: NewTextTemplate('${foo.bar}').generate(foo=foo).render()
Out[3]: 'baz'
In [4]: from kajiki import TextTemplate

In [5]: TextTemplate('${foo.bar}')(dict(foo=foo)).render()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-5-b774b6cc6e8e> in <module>()
----> 1 TextTemplate('${foo.bar}')(dict(foo=foo)).render()

…/venv/lib/python3.6/site-packages/kajiki/template.py in render(self)
    103     def render(self):
    104         """Render the template to a string."""
--> 105         return ''.join(self)
    106 
    107     def _gettext(self, s):

…/venv/lib/python3.6/site-packages/kajiki/template.py in __iter__(self)
     98         Here, ``chunk`` can be the computed expression result.
     99         """
--> 100         for chunk in self.__main__():
    101             yield str(chunk)
    102 

…/venv/lib/python3.6/site-packages/kajiki/util.py in __iter__(self)
     75 
     76     def __iter__(self):
---> 77         for x in self.iterator:
     78             if type(x) == flattener:
     79                 for xx in x:

<string> in __main__()

AttributeError: 'dict' object has no attribute 'bar'

py:include function Not working

_func.xhtml

<py:def function="abc()">
    <p>Hello</p>
</py:def>

master.xhtml

<py:include href='phb.templates.masters._func' />
${abc()}

XMLTemplate not working

I have tried the most simple example... as described in the docs, and it's just not working:

>>> import kajiki
>>> Template = kajiki.XMLTemplate('index.html', mode='html5')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.8/site-packages/kajiki/xml_template.py", line 52, in XMLTemplate
    doc = _Parser(filename, source).parse()
  File "/usr/local/lib/python3.8/site-packages/kajiki/xml_template.py", line 695, in parse
    raise exc
kajiki.xml_template.XMLTemplateParseError: [<string>:1] syntax error
	 --> <!DOCTYPE kajiki SYSTEM "kajiki.dtd">index.html

My HTML file:

<!DOCTYPE html>
<html>
    <head><!--  Some stuff here  --></head>
    <body>
        <form>
            <input checked type="checkbox">
            <select>
                <option selected>One</option>
                <option>Two</option>
                <option>Three</option>
            </select>
        </form>
    </body>
</html>

This is on FreeBSD, Python 3.8, latest kajiki installed via pip.
I'd love to try this template engine... any ideas what's wrong?

Python API is under-documented

In specific, loader.py doesn't have FileLoader mentioned at all, nor do the docs spell out the accepted filename extensions for PackageLoader.

Also, the online docs seem to be from v0.4.

Are there any initiatives to improve the situation?

There is no code.co_endlinetable and code.co_columntable in python3.11

There is no code.co_endlinetable and code.co_columntable in python3.11. Upgrading python to 3.11 causes to trash tgext debugbar with exception:

File "/home/ondrejj/.local/lib/python3.11/site-packages/kajiki/template.py", line 419, in patch_code_file_lines
    code.co_endlinetable if version_info >= (3, 11) else "REMOVE",
AttributeError: 'code' object has no attribute 'co_endlinetable'

Looks that these attributes are missing in python 3.11.

Python 3.11.0 (main, Oct 24 2022, 00:00:00) [GCC 12.2.1 20220819 (Red Hat 12.2.1-2)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import code
>>> o = code.compile_command("a=1")
>>> o.co_
o.co_argcount         o.co_flags            o.co_names
o.co_cellvars         o.co_freevars         o.co_nlocals
o.co_code             o.co_kwonlyargcount   o.co_positions(
o.co_consts           o.co_lines(           o.co_posonlyargcount
o.co_exceptiontable   o.co_linetable        o.co_qualname
o.co_filename         o.co_lnotab           o.co_stacksize
o.co_firstlineno      o.co_name             o.co_varnames
>>> o.co_

py:import doesn't work with py:extends

I can't seem to use <py:import> within a template that is called via <py:extends>

Here's a test case to show what I'm talking about:

def test_extends_with_import(self):
    loader = MockLoader({
        'parent.html': XMLTemplate(
            '<div>'
            '<py:import href="lib.html" alias="lib"/>'
            '${lib.foo()}'
            '</div>'),
        'lib.html': XMLTemplate(
            '<div>'
            '<py:def function="foo()"><b>foo</b></py:def>'
            '</div>'),
        'child.html': XMLTemplate('<py:extends href="parent.html"/>')})

    child = loader.import_('child.html')
    r = child().render()
    assert r == '<div><b>foo</b></div>'

This fails with the error NameError: name 'lib' is not defined.

I can fix it easily enough with this patch:

diff --git a/kajiki/ir.py b/kajiki/ir.py
--- a/kajiki/ir.py
+++ b/kajiki/ir.py
@@ -96,10 +96,13 @@
         self.alias = alias

     def py(self):
-        yield self.line(
-            'local.__kj__.import_(%r, %r, self.__globals__)' % (
-                self.tpl_name, self.alias))
+        s = 'local.__kj__.import_(%r, %r, self.__globals__)' % (
+                self.tpl_name, self.alias)

+        if self.alias:
+            yield self.line('%s = %s' % (self.alias, s))
+        else:
+            yield self.line(s)

 class IncludeNode(Node):
     def __init__(self, tpl_name):
diff --git a/kajiki/tests/test_xml.py b/kajiki/tests/test_xml.py
--- a/kajiki/tests/test_xml.py
+++ b/kajiki/tests/test_xml.py
@@ -542,6 +542,23 @@

 </html>''', rsp

But I'm not really happy with this. I don't completely understand what the role of self.__globals__ is here. I'm sure there must be a better fix than this!

HTML modes create "dirty" HTML

Kajiki's HTML modes default to produce "dirty" HTML, e.g. unclosed

  • tags. There's a hacky way to improve this, but it still doesn't use shortened end tags for e.g. <meta ... />, <img ... />, plays loose with inserting whitespace (e.g. two blank lines at the beginning of rendered documents):

    # make kajiki render nicer HTML
    from kajiki import html_utils
    html_utils.HTML_OPTIONAL_END_TAGS.clear()
    

    You can say many things about Genshi, but the (X)HTML it renders is beautiful. I realize that Kajiki uses a totally different way to create the templates which might make "staying close to the original template" difficult, but it should at least preserve closed and shortened end tags.

  • “Migrating from Genshi” lacks API part of migration process

    There's not even basic things covered, like “replace NewTextTemplate, MarkupTemplate, TemplateLoader with TextTemplate, XMLTemplate, FileLoader” or “substitude template.generate(foo=bar) for template(dict(foo=bar))”.

    By the way, is there any plan to bring current Kajiki APIs closer to Genshi ones? And should I report things like missing doctype and serializer options for Template.render(…) as separate issues here?

    Space-padding within expression fails in a variety of ways

    Within an element body:

    In [1]: from genshi.template import MarkupTemplate
    
    In [2]: from kajiki import XMLTemplate
    
    In [3]: MarkupTemplate('<x>${ 1+1 }</x>').generate().render()
    Out[3]: '<x>2</x>'
    
    In [4]: XMLTemplate('<x>${ 1+1 }</x>')().render()
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-4-70ec8b1a4b9a> in <module>()
    ----> 1 XMLTemplate('<x>${ 1+1 }</x>')().render()
    
    …/venv/lib/python3.6/site-packages/kajiki/template.py in render(self)
        103     def render(self):
        104         """Render the template to a string."""
    --> 105         return ''.join(self)
        106 
        107     def _gettext(self, s):
    
    …/venv/lib/python3.6/site-packages/kajiki/template.py in __iter__(self)
         98         Here, ``chunk`` can be the computed expression result.
         99         """
    --> 100         for chunk in self.__main__():
        101             yield str(chunk)
        102 
    
    …/venv/lib/python3.6/site-packages/kajiki/util.py in __iter__(self)
         75 
         76     def __iter__(self):
    ---> 77         for x in self.iterator:
         78             if type(x) == flattener:
         79                 for xx in x:
    
    <string> in __main__()
    
    TypeError: _escape() missing 1 required positional argument: 'value'
    

    Within an attribute:

    In [9]: MarkupTemplate("<x y=\"${ 1+1 }\" />").generate().render()
    Out[9]: '<x y="2"/>'
    
    In [10]: XMLTemplate("<x y=\"${ 1+1 }\" />")().render()
    Out[10]: '<x y="1+1 }"/>'
    

    Recommend Projects

    • React photo React

      A declarative, efficient, and flexible JavaScript library for building user interfaces.

    • Vue.js photo Vue.js

      🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

    • Typescript photo Typescript

      TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

    • TensorFlow photo TensorFlow

      An Open Source Machine Learning Framework for Everyone

    • Django photo Django

      The Web framework for perfectionists with deadlines.

    • D3 photo D3

      Bring data to life with SVG, Canvas and HTML. 📊📈🎉

    Recommend Topics

    • javascript

      JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

    • web

      Some thing interesting about web. New door for the world.

    • server

      A server is a program made to process requests and deliver data to clients.

    • Machine learning

      Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

    • Game

      Some thing interesting about game, make everyone happy.

    Recommend Org

    • Facebook photo Facebook

      We are working to build community through open source technology. NB: members must have two-factor auth.

    • Microsoft photo Microsoft

      Open source projects and samples from Microsoft.

    • Google photo Google

      Google ❤️ Open Source for everyone.

    • D3 photo D3

      Data-Driven Documents codes.