Edgewall Software
Modify

Opened 5 weeks ago

Last modified 4 weeks ago

#13745 assigned defect

Babel issue KeyError: 'dgettext'

Reported by: clemens Owned by: Jun Omae
Priority: normal Milestone: 1.6.1
Component: rendering Version: 1.6
Severity: normal Keywords:
Cc: c.feige@… Branch:
Release Notes:
API Changes:
Internal Changes:

Description

There seems to be a problem with babel with the CcSelectorPlugin. This plugin produces an error message if Babel is not installed. But since this root cause seems to be with the TRAC core, I am submitting the ticket here (and not at the Track-Hacks site).

Plugin error when Babel is not installed

I have been using the CcSelectorPlugin for many years. Now I re-installed my TRAC server (now with TRAC 1.6). Intentionally I decided to install TRAC without Babel because we do not want multi-lingual support.

After installing CcSelectorPlugin (Version 0.3, TrackHacks Revision 18597, from Nov. 2023, installation via PIP) it presents the following error when I click the "CC Selection" button:

Trac detected an internal error:
KeyError: 'dgettext'

The Python Traceback points to site-packages/trac/util/translation.py.

Python Traceback
File "/data/trac/python/lib/python3.11/site-packages/trac/web/main.py", line 609, in dispatch_request dispatcher.dispatch(req)
File "/data/trac/python/lib/python3.11/site-packages/trac/web/main.py", line 301, in dispatch raise e
File "/data/trac/python/lib/python3.11/site-packages/trac/web/main.py", line 265, in dispatch output = chrome.render_template(req, template, data, metadata)
File "/data/trac/python/lib/python3.11/site-packages/trac/web/chrome.py", line 1381, in render_template template, data = self.prepare_template(req, filename, data, text, 
File "/data/trac/python/lib/python3.11/site-packages/trac/web/chrome.py", line 1487, in prepare_template domain_functions = translation.domain_functions(domain, symbols)
File "/data/trac/python/lib/python3.11/site-packages/trac/util/translation.py", line 92, in domain_functions return [_functions[s] for s in symbols]
File "/data/trac/python/lib/python3.11/site-packages/trac/util/translation.py", line 92, in <listcomp> return [_functions[s] for s in symbols] 

Discussion

Of course the solution is to install Babel, which will solve the problem.

Nevertheless IMHO either a plug-in should clearly state its dependencies during installation (or during run-time or in the documentation). Best of course would be would be, if the plug-in could run without Babel. After all, Babel is just optional for TRAC.

Although I am reporting an issue with a plugin this is supposed to be an issue with the TRAC core. This issue was discussed on the TRAC mailing list and I was told to open a ticket here.

Attachments (0)

Change History (7)

comment:1 by Clemens <c.feige@…>, 5 weeks ago

I want to contribute some details about my TRAC where I found this issue:

  • Trac 1.6 running on Linux
  • Babel 2.14.0
  • Jinja2 3.1.3
  • Python 3.11.2

comment:2 by Clemens <c.feige@…>, 5 weeks ago

This is the answer I received from Jun Omae on the mailing list. He already made some experiments and statements which I am quoting here:

QUOTE:

That is an issue of Trac core since 1.4.x. It is reproduced with Trac 1.4.4 and 1.6 without Babel. Please report it to trac.edgewall.org/newticket.

TRAC 1.6:

$ python3.11 -m venv /dev/shm/trac-1.6
$ /dev/shm/trac-1.6/bin/pip install -q Trac~=1.6.0
$ /dev/shm/trac-1.6/bin/python
Python 3.11.8 (main, Feb 25 2024, 16:41:26) [GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.

>>> import babel
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'babel'
>>> import trac
>>> trac.__version__
'1.6'
>>> from trac.test import EnvironmentStub, MockRequest
>>> from trac.web.chrome import Chrome
>>> env = EnvironmentStub()
>>> req = MockRequest(env)
>>> chrome = Chrome(env)
>>> chrome.prepare_template(req, 'layout.html', {}, domain='messages')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/dev/shm/trac-1.6/lib/python3.11/site-packages/trac/web/chrome.py", line 1487, in prepare_template
    domain_functions = translation.domain_functions(domain, symbols)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/dev/shm/trac-1.6/lib/python3.11/site-packages/trac/util/translation.py", line 92, in domain_functions
    return [_functions[s] for s in symbols]
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/dev/shm/trac-1.6/lib/python3.11/site-packages/trac/util/translation.py", line 92, in <listcomp>
    return [_functions[s] for s in symbols]
            ~~~~~~~~~~^^^
KeyError: 'dgettext'
>>>

TRAC 1.4:

$ virtualenv -p /usr/bin/python2.7 /dev/shm/trac-1.4
$ /dev/shm/trac-1.4/bin/pip install -q Trac~=1.4.0
$ /dev/shm/trac-1.4/bin/python
Python 2.7.18 (default, Jul  1 2022, 12:27:04)
[GCC 9.4.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import babel
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named babel
>>> import trac
>>> trac.__version__
'1.4.4'
>>> from trac.test import EnvironmentStub, MockRequest
>>> from trac.web.chrome import Chrome
>>> env = EnvironmentStub()
>>> req = MockRequest(env)
>>> chrome = Chrome(env)
>>> chrome.prepare_template(req, 'layout.html', {}, domain='messages')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/dev/shm/trac-1.4/lib/python2.7/site-packages/trac/web/chrome.py", line 1614, in prepare_template
    domain_functions = translation.domain_functions(domain, symbols)
  File "/dev/shm/trac-1.4/lib/python2.7/site-packages/trac/util/translation.py", line 92, in domain_functions
    return [_functions[s] for s in symbols]
KeyError: 'dngettext'
>>>

comment:3 by Jun Omae, 5 weeks ago

This issue can be reproduced with/without Babel.

>>> import trac, babel
>>> (trac.__version__, babel.__version__)
('1.6', '2.14.0')
>>> from trac.env import Environment
>>> from trac.test import MockRequest
>>> from trac.web.chrome import Chrome
>>> from trac.util import read_file
>>>
>>> env = Environment('/dev/shm/tracenv-1.6')
>>> req = MockRequest(env)
>>> chrome = Chrome(env)
>>> print(read_file('/dev/shm/tracenv-1.6/templates/test.html'))
${dgettext("messages", "Your changes have been saved.")}

>>> t = chrome.render_template(req, 'test.html', {'data': 42}, {'domain': 'messages', 'iterable': False})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/dev/shm/trac-1.6/lib/python3.11/site-packages/trac/web/chrome.py", line 1411, in render_template
    return self.generate_template_stream(template, data, text,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/dev/shm/trac-1.6/lib/python3.11/site-packages/trac/web/chrome.py", line 1533, in generate_template_stream
    return b''.join(generate())
           ^^^^^^^^^^^^^^^^^^^^
  File "/dev/shm/trac-1.6/lib/python3.11/site-packages/trac/web/chrome.py", line 1531, in generate
    for chunk in stream:
  File "/dev/shm/trac-1.6/lib/python3.11/site-packages/jinja2/environment.py", line 1662, in __next__
    return self._next()  # type: ignore
           ^^^^^^^^^^^^
  File "/dev/shm/trac-1.6/lib/python3.11/site-packages/jinja2/environment.py", line 1639, in _buffered_generator
    c = next(self._gen)
        ^^^^^^^^^^^^^^^
  File "/dev/shm/trac-1.6/lib/python3.11/site-packages/jinja2/environment.py", line 1354, in generate
    yield self.environment.handle_exception()
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/dev/shm/trac-1.6/lib/python3.11/site-packages/jinja2/environment.py", line 936, in handle_exception
    raise rewrite_traceback_stack(source=source)
  File "/dev/shm/tracenv-1.6/templates/test.html", line 1, in top-level template code
    ${dgettext("messages", "Your changes have been saved.")}
    ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/dev/shm/trac-1.6/lib/python3.11/site-packages/trac/util/translation.py", line 295, in <lambda>
    return lambda *args, **kw: _functions[symbol](domain, *args, **kw)
                               ~~~~~~~~~~^^^^^^^^
KeyError: 'dgettext'
>>>

comment:4 by Jun Omae, 5 weeks ago

Component: generalrendering
Milestone: 1.6.1
Owner: set to Jun Omae
Status: newassigned

comment:5 by Dirk Stöcker, 4 weeks ago

The current version of CCSelectorPlugin from trunk has nowhere "dgettext" in the code. Which means you're using an old version.

Correct plugin for ≥ 1.6 can be found here: https://trac-hacks.org/svn/ccselectorplugin/trunk/

I don't think there is anything wrong in core.

comment:6 by Jun Omae, 4 weeks ago

Any plugins don't have the root cause. It occurs when process_request returns domain in metadata parameter. Chrome class injects domain functions (_, gettext, dgettext, …) to the template instance on rendering process, however, KeyError is raised.

The issue is absolutely in Trac core.

>>> import babel
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'babel'
>>> from trac.env import Environment
>>> from trac.test import EnvironmentStub, MockRequest
>>> from trac.web.api import IRequestHandler
>>> from trac.core import Component, implements
>>> from trac.web.main import RequestDispatcher
>>>
>>> class Foo(Component):
...   implements(IRequestHandler)
...   def match_request(self, req):
...     return req.path_info == '/test'
...   def process_request(self, req):
...     return 'layout.html', {'data': 42}, {'domain': 'messages'}
...
>>> env = EnvironmentStub()
>>> req = MockRequest(env, path_info='/test')
>>> dispatcher = RequestDispatcher(env)
>>> dispatcher.dispatch(req)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/jun66j5/src/tracdev/git/trac/web/main.py", line 301, in dispatch
    raise e
  File "/home/jun66j5/src/tracdev/git/trac/web/main.py", line 265, in dispatch
    output = chrome.render_template(req, template, data, metadata)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/jun66j5/src/tracdev/git/trac/web/chrome.py", line 1381, in render_template
    template, data = self.prepare_template(req, filename, data, text,
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/jun66j5/src/tracdev/git/trac/web/chrome.py", line 1487, in prepare_template
    domain_functions = translation.domain_functions(domain, symbols)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/jun66j5/src/tracdev/git/trac/util/translation.py", line 92, in domain_functions
    return [_functions[s] for s in symbols]
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/jun66j5/src/tracdev/git/trac/util/translation.py", line 92, in <listcomp>
    return [_functions[s] for s in symbols]
            ~~~~~~~~~~^^^
KeyError: 'dgettext'
>>>

in reply to:  5 comment:7 by Clemens <c.feige@…>, 4 weeks ago

Replying to Dirk Stöcker:

The current version of CCSelectorPlugin from trunk has nowhere "dgettext" in the code. Which means you're using an old version.

Correct plugin for ≥ 1.6 can be found here: https://trac-hacks.org/svn/ccselectorplugin/trunk/

I don't think there is anything wrong in core.

I am quite sure that I am using the most recent version of the CCSelectorPlugin (Version 0.3, TrackHacks Revision 18597, from Nov. 2023) because I just installed it myself few days ago with this command:

pip install svn+https://trac-hacks.org/svn/ccselectorplugin/trunk/

Yes, you are right, the text dgettextdoes not appear in the plug-in code. This was something I checked when I came across this error, too. I am not sure if this tells us where this root problem is, in the plugin or in the core.

Seems that Jun has found something…

Modify Ticket

Change Properties
Set your email in Preferences
Action
as assigned The owner will remain Jun Omae.
The ticket will be disowned. Next status will be 'new'.
as The resolution will be set. Next status will be 'closed'.
to The owner will be changed from Jun Omae to the specified user.

Add Comment


E-mail address and name can be saved in the Preferences .
 
Note: See TracTickets for help on using tickets.