• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

roundup-tracker / roundup / 22333158062

24 Feb 2026 01:16AM UTC coverage: 74.641% (+0.009%) from 74.632%
22333158062

push

github

rouilj
chore(build): build(deps): bump anchore/scan-action from 7.3.1 to 7.3.2 pull #82

19126 of 25624 relevant lines covered (74.64%)

4.39 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/roundup/scripts/roundup_gettext.py
1
#! /usr/bin/env python
2
#
3
# Copyright 2004 Richard Jones (richard@mechanicalcat.net)
4

5
"""Extract translatable strings from tracker templates and detectors/extensions"""
6

7
from __future__ import print_function
×
8

9
import os
×
10
import os.path as osp
×
11
import sys
×
12
import tempfile
×
13

14
# --- patch sys.path to make sure 'import roundup' finds correct version
15
thisdir = osp.dirname(osp.abspath(__file__))
×
16
rootdir = osp.dirname(osp.dirname(thisdir))
×
17
if (osp.exists(thisdir + '/__init__.py') and
×
18
        osp.exists(rootdir + '/roundup/__init__.py')):
19
    # the script is located inside roundup source code
20
    sys.path.insert(0, rootdir)
×
21
# --/
22

23
from roundup import configuration  # noqa: E402
×
24
from roundup.cgi.TAL import talgettext  # noqa: E402
×
25
from roundup.i18n import _  # noqa: E402
×
26
from roundup.pygettext import TokenEater, make_escapes, tokenize  # noqa: E402
×
27

28
try:
×
29
    import polib
×
30
except ImportError:
×
31
    print(_("\nExtracting translatable strings only from html templates.\n"
×
32
            "Because the 'polib' module is missing, unable to extract\n"
33
            "translations from detectors or extensions.\n"
34
            "The 'polib' module can be installed with pip.\n"))
35
    polib = None
×
36

37

38
# from pygettext's main():
39
class Options:
×
40
    # constants
41
    GNU = 1
×
42
    SOLARIS = 2
×
43
    # defaults
44
    extractall = 0 # FIXME: currently this option has no effect at all.
×
45
    escape = 0
×
46
    keywords = ["_", "gettext", "ngettext", "ugettext"]
×
47
    outpath = ''
×
48
    outfile = ''
×
49
    writelocations = 1
×
50
    locationstyle = GNU
×
51
    verbose = 0
×
52
    width = 10
×
53
    excludefilename = ''
×
54
    docstrings = 0
×
55
    nodocstrings = {}
×
56
    toexclude = [] # TODO we should exclude all strings already found in some template
×
57

58

59
tokeneater_options = Options()
×
60

61
# name of message template file.
62
# i don't think this will ever need to be changed, but still...
63
TEMPLATE_FILE = "messages.pot"
×
64

65

66
def run():
×
67
    # return unless command line arguments contain single directory path
68
    if (len(sys.argv) != 2) or (sys.argv[1] in ("-h", "--help")):
×
69
        print(_("Usage: %(program)s <tracker home>") %
×
70
              {"program": sys.argv[0]})
71
        return
×
72

73
    home = os.path.abspath(sys.argv[1])
×
74
    # collect file paths of html templates from config
75
    config = configuration.CoreConfig(home)
×
76
    htmldir = config["TEMPLATES"]
×
77
    if os.path.isdir(htmldir):
×
78
        # glob is not used because i want to match file names
79
        # without case sensitivity, and that is easier done this way.
80
        htmlfiles = [e.name for e in os.scandir(htmldir)
×
81
                     if e.is_file()
82
                     and e.name.lower().endswith(".html")]
83
    else:
84
        htmlfiles = []
×
85
    # return if no html files found
86
    if not htmlfiles:
×
87
        print(_("No tracker templates found in directory %s") % htmldir)
×
88
        return
×
89
    # change to locale dir to have relative source references
90
    locale = os.path.join(home, "locale")
×
91
    if not os.path.isdir(locale):
×
92
        os.mkdir(locale)
×
93
    os.chdir(locale)
×
94

95
    # compute relative path to template directory from locale directory
96
    relpath = os.path.relpath(htmldir)
×
97

98
    # tweak sys.argv as this is the only way to tell talgettext what to do
99
    # Note: unix-style paths used instead of os.path.join deliberately
100
    sys.argv[1:] = ["-o", TEMPLATE_FILE] \
×
101
        + [relpath + "/" + filename for filename in htmlfiles]
102
    # run
103
    talgettext.main()
×
104

105
    if not polib:
×
106
        return
×
107

108
    # we have now everything from the templates in the TEMPLATE_FILE
109
    # now we search in home/detectors and home/extensions *.py files for
110
    # tokeneater_options.keywords
111
    # this is partly assembled from pygettext's main()
112
    make_escapes(not tokeneater_options.escape)
×
113

114
    pyfiles = []
×
115
    for source in ["detectors", "extensions"]:
×
116
        for root, _dirs, files in os.walk(os.path.join("..", source)):
×
117
            pyfiles.extend([os.path.join(root, f) for f in files if f.endswith(".py")])
×
118

119
    eater = TokenEater(tokeneater_options)
×
120

121
    for filename in pyfiles:
×
122
        eater.set_filename(filename)
×
123
        with open(filename, "r") as f:
×
124
            try:
×
125
                for token in tokenize.generate_tokens(f.readline):
×
126
                    eater(*token)
×
127
            except tokenize.TokenError as e:
×
128
                print('%s: %s, line %d, column %d' % (
×
129
                    e[0], filename, e[1][0], e[1][1]), file=sys.stderr)
130

131
    with tempfile.NamedTemporaryFile("w") as tf:
×
132
        eater.write(tf)
×
133
        tf.seek(0)
×
134
        p1 = polib.pofile(TEMPLATE_FILE)
×
135
        p2 = polib.pofile(tf.name)
×
136

137
        p2_msg_ids = {e.msgid for e in p2}
×
138
        for e in p1:
×
139
            if e.msgid in p2_msg_ids:
×
140
                p2_e = p2.find(e.msgid)
×
141
                e.occurrences.extend(p2_e.occurrences)
×
142
                p2_msg_ids.remove(e.msgid)
×
143

144
        for msgid in p2_msg_ids:
×
145
            p1.append(p2.find(msgid))
×
146
        p1.save()
×
147

148

149
if __name__ == "__main__":
×
150
    run()
×
151

152
# vim: set et sts=4 sw=4 :
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc