A bunch of changes, primarily to command line argument parsing

(inspired by Detlef Lannert).  Specifically,

    -k/--keyword no longer takes an optional argument to clear the
    default keywords.  Instead, use -K/--no-default-keywords to clear
    them.

    -n/--add-location also no longer takes an optional argument to set
    the comment style.  Instead, use -S/--style to set the comment
    style to GNU or Solaris.

    -o/--output can take `-' as the filename, meaning write to
    standard output.

    The inputfile name can also be `-' meaning read from standard in.

A few other changes include

    Kludge to mark the file docstring as translatable.  Since the
    marking is to place _() around the docstring, and because we
    actually have to define the _() function before we use it, this
    means that we have to manually assign to __doc__ the output of
    _().  This doesn't seem too bad because you'll only use this idiom
    when translating a script's docstring (you really don't need to
    translate most module docstrings).

    Convert everything to string methods and do not import the string
    module.

    Bump the version number to 1.1
This commit is contained in:
Barry Warsaw 2000-03-08 15:18:35 +00:00
parent 34d11f0670
commit a17e0f1b61
1 changed files with 101 additions and 65 deletions

View File

@ -4,7 +4,15 @@
# minimally patched to make it even more xgettext compatible # minimally patched to make it even more xgettext compatible
# by Peter Funk <pf@artcom-gmbh.de> # by Peter Funk <pf@artcom-gmbh.de>
"""pygettext -- Python equivalent of xgettext(1) # for selftesting
try:
import fintl
_ = fintl.gettext
except ImportError:
def _(s): return s
__doc__ = _("""pygettext -- Python equivalent of xgettext(1)
Many systems (Solaris, Linux, Gnu) provide extensive tools that ease the Many systems (Solaris, Linux, Gnu) provide extensive tools that ease the
internationalization of C programs. Most of these tools are independent of internationalization of C programs. Most of these tools are independent of
@ -39,9 +47,11 @@ below for how to augment this.
NOTE: pygettext attempts to be option and feature compatible with GNU xgettext NOTE: pygettext attempts to be option and feature compatible with GNU xgettext
where ever possible. However some options are still missing or are not fully where ever possible. However some options are still missing or are not fully
implemented. implemented. Also, xgettext's use of command line switches with option
arguments is broken, and in these cases, pygettext just defines additional
switches.
Usage: pygettext [options] filename ... Usage: pygettext [options] inputfile ...
Options: Options:
@ -61,33 +71,42 @@ Options:
--help --help
print this help message and exit print this help message and exit
-k [word] -k word
--keyword[=word] --keyword=word
Additional keywords to look for. Without `word' means not to use the Keywords to look for in addition to the default set, which are:
default keywords. The default keywords, which are always looked for %(DEFAULTKEYWORDS)s
if not explicitly disabled: _
The default keyword list is different than GNU xgettext. You can have You can have multiple -k flags on the command line.
multiple -k flags on the command line.
-K
--no-default-keywords
Disable the default set of keywords (see above). Any keywords
explicitly added with the -k/--keyword option are still recognized.
--no-location --no-location
Do not write filename/lineno location comments Do not write filename/lineno location comments.
-n [style] -n
--add-location[=style] --add-location
Write filename/lineno location comments indicating where each Write filename/lineno location comments indicating where each
extracted string is found in the source. These lines appear before extracted string is found in the source. These lines appear before
each msgid. Two styles are supported: each msgid. The style of comments is controlled by the -S/--style
option. This is the default.
-S stylename
--style stylename
Specify which style to use for location comments. Two styles are
supported:
Solaris # File: filename, line: line-number Solaris # File: filename, line: line-number
Gnu #: filename:line GNU #: filename:line
If style is omitted, Gnu is used. The style name is case The style name is case insensitive. GNU style is the default.
insensitive. By default, locations are included.
-o filename -o filename
--output=filename --output=filename
Rename the default output file from messages.pot to filename. Rename the default output file from messages.pot to filename. If
filename is `-' then the output is sent to standard out.
-p dir -p dir
--output-dir=dir --output-dir=dir
@ -111,27 +130,25 @@ Options:
extracted from the input files. Each string to be excluded must extracted from the input files. Each string to be excluded must
appear on a line by itself in the file. appear on a line by itself in the file.
""" If `inputfile' is -, standard input is read.
""")
import os import os
import sys import sys
import string
import time import time
import getopt import getopt
import tokenize import tokenize
__version__ = '1.0' __version__ = '1.1'
default_keywords = ['_']
DEFAULTKEYWORDS = ', '.join(default_keywords)
EMPTYSTRING = ''
# for selftesting
try:
import fintl
_ = fintl.gettext
except ImportError:
def _(s): return s
# The normal pot-file header. msgmerge and EMACS' po-mode work better if # The normal pot-file header. msgmerge and EMACS' po-mode work better if
# it's there. # it's there.
pot_header = _('''\ pot_header = _('''\
@ -189,7 +206,7 @@ def escape(s):
s = list(s) s = list(s)
for i in range(len(s)): for i in range(len(s)):
s[i] = escapes[ord(s[i])] s[i] = escapes[ord(s[i])]
return string.join(s, '') return EMPTYSTRING.join(s)
def safe_eval(s): def safe_eval(s):
@ -200,7 +217,7 @@ def safe_eval(s):
def normalize(s): def normalize(s):
# This converts the various Python string types into a format that is # This converts the various Python string types into a format that is
# appropriate for .po files, namely much closer to C style. # appropriate for .po files, namely much closer to C style.
lines = string.split(s, '\n') lines = s.split('\n')
if len(lines) == 1: if len(lines) == 1:
s = '"' + escape(s) + '"' s = '"' + escape(s) + '"'
else: else:
@ -209,7 +226,8 @@ def normalize(s):
lines[-1] = lines[-1] + '\n' lines[-1] = lines[-1] + '\n'
for i in range(len(lines)): for i in range(len(lines)):
lines[i] = escape(lines[i]) lines[i] = escape(lines[i])
s = '""\n"' + string.join(lines, '\\n"\n"') + '"' lineterm = '\\n"\n"'
s = '""\n"' + lineterm.join(lines) + '"'
return s return s
@ -245,7 +263,7 @@ class TokenEater:
# of messages seen. Reset state for the next batch. If there # of messages seen. Reset state for the next batch. If there
# were no strings inside _(), then just ignore this entry. # were no strings inside _(), then just ignore this entry.
if self.__data: if self.__data:
msg = string.join(self.__data, '') msg = EMPTYSTRING.join(self.__data)
if not msg in self.__options.toexclude: if not msg in self.__options.toexclude:
entry = (self.__curfile, self.__lineno) entry = (self.__curfile, self.__lineno)
linenos = self.__messages.get(msg) linenos = self.__messages.get(msg)
@ -271,12 +289,14 @@ class TokenEater:
# as that generated by xgettext... # as that generated by xgettext...
print pot_header % {'time': timestamp, 'version': __version__} print pot_header % {'time': timestamp, 'version': __version__}
for k, v in self.__messages.items(): for k, v in self.__messages.items():
if not options.writelocations:
pass
# location comments are different b/w Solaris and GNU: # location comments are different b/w Solaris and GNU:
if options.location == options.SOLARIS: elif options.locationstyle == options.SOLARIS:
for filename, lineno in v: for filename, lineno in v:
d = {'filename': filename, 'lineno': lineno} d = {'filename': filename, 'lineno': lineno}
print _('# File: %(filename)s, line: %(lineno)d') % d print _('# File: %(filename)s, line: %(lineno)d') % d
elif options.location == options.GNU: elif options.locationstyle == options.GNU:
# fit as many locations on one line, as long as the # fit as many locations on one line, as long as the
# resulting line length doesn't exceeds 'options.width' # resulting line length doesn't exceeds 'options.width'
locline = '#:' locline = '#:'
@ -298,14 +318,15 @@ class TokenEater:
def main(): def main():
default_keywords = ['_'] global default_keywords
try: try:
opts, args = getopt.getopt( opts, args = getopt.getopt(
sys.argv[1:], sys.argv[1:],
'ad:Ehk:n:o:p:Vvw:x:', 'ad:Ehk:Kno:p:S:Vvw:x:',
['extract-all', 'default-domain', 'escape', 'help', 'keyword', ['extract-all', 'default-domain', 'escape', 'help',
'keyword=', 'no-default-keywords',
'add-location', 'no-location', 'output=', 'output-dir=', 'add-location', 'no-location', 'output=', 'output-dir=',
'verbose', 'version', 'width=', 'exclude-file=', 'style=', 'verbose', 'version', 'width=', 'exclude-file=',
]) ])
except getopt.error, msg: except getopt.error, msg:
usage(1, msg) usage(1, msg)
@ -321,7 +342,8 @@ def main():
keywords = [] keywords = []
outpath = '' outpath = ''
outfile = 'messages.pot' outfile = 'messages.pot'
location = GNU writelocations = 1
locationstyle = GNU
verbose = 0 verbose = 0
width = 78 width = 78
excludefilename = '' excludefilename = ''
@ -342,19 +364,17 @@ def main():
elif opt in ('-E', '--escape'): elif opt in ('-E', '--escape'):
options.escape = 1 options.escape = 1
elif opt in ('-k', '--keyword'): elif opt in ('-k', '--keyword'):
if arg is None:
default_keywords = []
options.keywords.append(arg) options.keywords.append(arg)
elif opt in ('-K', '--no-default-keywords'):
default_keywords = []
elif opt in ('-n', '--add-location'): elif opt in ('-n', '--add-location'):
if arg is None: options.writelocations = 1
arg = 'gnu'
try:
options.location = locations[string.lower(arg)]
except KeyError:
d = {'arg':arg}
usage(1, _('Invalid value for --add-location: %(arg)s') % d)
elif opt in ('--no-location',): elif opt in ('--no-location',):
options.location = 0 options.writelocations = 0
elif opt in ('-S', '--style'):
options.locationstyle = locations.get(arg.lower())
if options.locationstyle is None:
usage(1, _('Invalid value for --style: %s') % arg)
elif opt in ('-o', '--output'): elif opt in ('-o', '--output'):
options.outfile = arg options.outfile = arg
elif opt in ('-p', '--output-dir'): elif opt in ('-p', '--output-dir'):
@ -368,9 +388,7 @@ def main():
try: try:
options.width = int(arg) options.width = int(arg)
except ValueError: except ValueError:
d = {'arg':arg} usage(1, _('--width argument must be an integer: %s') % arg)
usage(1, _('Invalid value for --width: %(arg)s, must be int')
% d)
elif opt in ('-x', '--exclude-file'): elif opt in ('-x', '--exclude-file'):
options.excludefilename = arg options.excludefilename = arg
@ -396,20 +414,38 @@ def main():
# slurp through all the files # slurp through all the files
eater = TokenEater(options) eater = TokenEater(options)
for filename in args: for filename in args:
if filename == '-':
if options.verbose: if options.verbose:
print _('Working on %(filename)s') % {'filename':filename} print _('Reading standard input')
fp = sys.stdin
closep = 0
else:
if options.verbose:
print _('Working on %s') % filename
fp = open(filename) fp = open(filename)
closep = 1
try:
eater.set_filename(filename) eater.set_filename(filename)
tokenize.tokenize(fp.readline, eater) tokenize.tokenize(fp.readline, eater)
finally:
if closep:
fp.close() fp.close()
# write the output
if options.outfile == '-':
fp = sys.stdout
closep = 0
else:
if options.outpath: if options.outpath:
options.outfile = os.path.join(options.outpath, options.outfile) options.outfile = os.path.join(options.outpath, options.outfile)
fp = open(options.outfile, 'w') fp = open(options.outfile, 'w')
closep = 1
try:
eater.write(fp) eater.write(fp)
finally:
if closep:
fp.close() fp.close()
if __name__ == '__main__': if __name__ == '__main__':
main() main()