lyx2lyx: use parseopt instead of getopt (the documentation of options

is kept in sync with its usage)

LyX.py:  make source more readable, place whitespaces, break long
         lines, add documentation strings, etc.



git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@20279 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
José Matox 2007-09-14 14:37:14 +00:00
parent 97811b4116
commit dbe7b13f65
2 changed files with 158 additions and 154 deletions

View File

@ -17,7 +17,9 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
from parser_tools import get_value, check_token, find_token,\ " The LyX module has all the rules related with different lyx file formats."
from parser_tools import get_value, check_token, find_token, \
find_tokens, find_end_of find_tokens, find_end_of
import os.path import os.path
import gzip import gzip
@ -28,11 +30,11 @@ import time
try: try:
import lyx2lyx_version import lyx2lyx_version
version_lyx2lyx = lyx2lyx_version.version version__ = lyx2lyx_version.version
except: # we are running from build directory so assume the last version except: # we are running from build directory so assume the last version
version_lyx2lyx = '1.6.0svn' version__ = '1.6.0svn'
default_debug_level = 2 default_debug__ = 2
#################################################################### ####################################################################
# Private helper functions # Private helper functions
@ -41,13 +43,13 @@ def find_end_of_inset(lines, i):
" Find beginning of inset, where lines[i] is included." " Find beginning of inset, where lines[i] is included."
return find_end_of(lines, i, "\\begin_inset", "\\end_inset") return find_end_of(lines, i, "\\begin_inset", "\\end_inset")
def generate_minor_versions(major, last_minor_version): def minor_versions(major, last_minor_version):
""" Generate minor versions, using major as prefix and minor """ Generate minor versions, using major as prefix and minor
versions from 0 until last_minor_version, plus the generic version. versions from 0 until last_minor_version, plus the generic version.
Example: Example:
generate_minor_versions("1.2", 4) -> minor_versions("1.2", 4) ->
[ "1.2", "1.2.0", "1.2.1", "1.2.2", "1.2.3"] [ "1.2", "1.2.0", "1.2.1", "1.2.2", "1.2.3"]
""" """
return [major] + [major + ".%d" % i for i in range(last_minor_version + 1)] return [major] + [major + ".%d" % i for i in range(last_minor_version + 1)]
@ -65,20 +67,20 @@ original_version = re.compile(r".*?LyX ([\d.]*)")
## ##
# file format information: # file format information:
# file, supported formats, stable release versions # file, supported formats, stable release versions
format_relation = [("0_06", [200], generate_minor_versions("0.6" , 4)), format_relation = [("0_06", [200], minor_versions("0.6" , 4)),
("0_08", [210], generate_minor_versions("0.8" , 6) + ["0.7"]), ("0_08", [210], minor_versions("0.8" , 6) + ["0.7"]),
("0_10", [210], generate_minor_versions("0.10", 7) + ["0.9"]), ("0_10", [210], minor_versions("0.10", 7) + ["0.9"]),
("0_12", [215], generate_minor_versions("0.12", 1) + ["0.11"]), ("0_12", [215], minor_versions("0.12", 1) + ["0.11"]),
("1_0", [215], generate_minor_versions("1.0" , 4)), ("1_0", [215], minor_versions("1.0" , 4)),
("1_1", [215], generate_minor_versions("1.1" , 4)), ("1_1", [215], minor_versions("1.1" , 4)),
("1_1_5", [216], ["1.1.5","1.1.5.1","1.1.5.2","1.1"]), ("1_1_5", [216], ["1.1.5","1.1.5.1","1.1.5.2","1.1"]),
("1_1_6_0", [217], ["1.1.6","1.1.6.1","1.1.6.2","1.1"]), ("1_1_6_0", [217], ["1.1.6","1.1.6.1","1.1.6.2","1.1"]),
("1_1_6_3", [218], ["1.1.6.3","1.1.6.4","1.1"]), ("1_1_6_3", [218], ["1.1.6.3","1.1.6.4","1.1"]),
("1_2", [220], generate_minor_versions("1.2" , 4)), ("1_2", [220], minor_versions("1.2" , 4)),
("1_3", [221], generate_minor_versions("1.3" , 7)), ("1_3", [221], minor_versions("1.3" , 7)),
("1_4", range(222,246), generate_minor_versions("1.4" , 5)), ("1_4", range(222,246), minor_versions("1.4" , 5)),
("1_5", range(246,277), generate_minor_versions("1.5" , 1)), ("1_5", range(246,277), minor_versions("1.5" , 1)),
("1_6", range(277,286), generate_minor_versions("1.6" , 0))] ("1_6", range(277,286), minor_versions("1.6" , 0))]
def formats_list(): def formats_list():
@ -114,6 +116,7 @@ def trim_eol(line):
def get_encoding(language, inputencoding, format, cjk_encoding): def get_encoding(language, inputencoding, format, cjk_encoding):
" Returns enconding of the lyx file"
if format > 248: if format > 248:
return "utf8" return "utf8"
# CJK-LyX encodes files using the current locale encoding. # CJK-LyX encodes files using the current locale encoding.
@ -122,7 +125,7 @@ def get_encoding(language, inputencoding, format, cjk_encoding):
# argument. # argument.
if cjk_encoding == 'auto': if cjk_encoding == 'auto':
return locale.getpreferredencoding() return locale.getpreferredencoding()
elif cjk_encoding != '': elif cjk_encoding:
return cjk_encoding return cjk_encoding
from lyx2lyx_lang import lang from lyx2lyx_lang import lang
if inputencoding == "auto" or inputencoding == "default": if inputencoding == "auto" or inputencoding == "default":
@ -137,11 +140,11 @@ def get_encoding(language, inputencoding, format, cjk_encoding):
## ##
# Class # Class
# #
class LyX_Base: class LyX_base:
"""This class carries all the information of the LyX file.""" """This class carries all the information of the LyX file."""
def __init__(self, end_format = 0, input = "", output = "", error def __init__(self, end_format = 0, input = "", output = "", error = "",
= "", debug = default_debug_level, try_hard = 0, cjk_encoding = '', debug = default_debug__, try_hard = 0, cjk_encoding = '',
language = "english", encoding = "auto"): language = "english", encoding = "auto"):
"""Arguments: """Arguments:
@ -182,8 +185,9 @@ class LyX_Base:
self.language = language self.language = language
def warning(self, message, debug_level= default_debug_level): def warning(self, message, debug_level= default_debug__):
" Emits warning to self.error, if the debug_level is less than the self.debug." """ Emits warning to self.error, if the debug_level is less
than the self.debug."""
if debug_level <= self.debug: if debug_level <= self.debug:
self.err.write("Warning: " + message + "\n") self.err.write("Warning: " + message + "\n")
@ -199,9 +203,10 @@ class LyX_Base:
def read(self): def read(self):
"""Reads a file into the self.header, self.manifest and self.body parts, from self.input.""" """Reads a file into the self.header, self.manifest and
self.body parts, from self.input."""
while 1: while True:
line = self.input.readline() line = self.input.readline()
if not line: if not line:
self.error("Invalid LyX file.") self.error("Invalid LyX file.")
@ -217,9 +222,13 @@ class LyX_Base:
if check_token(line, '\\end_preamble'): if check_token(line, '\\end_preamble'):
break break
if line.split()[:0] in ("\\layout", "\\begin_layout", "\\begin_body"): if line.split()[:0] in ("\\layout",
self.warning("Malformed LyX file: Missing '\\end_preamble'.") "\\begin_layout", "\\begin_body"):
self.warning("Adding it now and hoping for the best.")
self.warning("Malformed LyX file:"
"Missing '\\end_preamble'."
"\nAdding it now and hoping"
"for the best.")
self.preamble.append(line) self.preamble.append(line)
@ -236,9 +245,11 @@ class LyX_Base:
if check_token(line, "\\end_manifest"): if check_token(line, "\\end_manifest"):
break break
if not line.startswith('\\filename') and not line.startswith('\\inzipName') \ if not line.startswith('\\filename') and \
and not line.startswith('\\embed'): not line.startswith('\\inzipName') and \
self.warning("Malformed LyX file: Missing '\\end_manifest'.") not line.startswith('\\embed'):
self.warning("Malformed LyX file: Missing"
"'\\end_manifest'.")
self.manifest.append(line) self.manifest.append(line)
@ -249,7 +260,8 @@ class LyX_Base:
if not line: if not line:
continue continue
if line.split()[0] in ("\\layout", "\\begin_layout", "\\begin_body", "\\begin_deeper"): if line.split()[0] in ("\\layout", "\\begin_layout",
"\\begin_body", "\\begin_deeper"):
self.body.append(line) self.body.append(line)
break break
@ -258,9 +270,13 @@ class LyX_Base:
self.textclass = get_value(self.header, "\\textclass", 0) self.textclass = get_value(self.header, "\\textclass", 0)
self.backend = get_backend(self.textclass) self.backend = get_backend(self.textclass)
self.format = self.read_format() self.format = self.read_format()
self.language = get_value(self.header, "\\language", 0, default = "english") self.language = get_value(self.header, "\\language", 0,
self.inputencoding = get_value(self.header, "\\inputencoding", 0, default = "auto") default = "english")
self.encoding = get_encoding(self.language, self.inputencoding, self.format, self.cjk_encoding) self.inputencoding = get_value(self.header, "\\inputencoding",
0, default = "auto")
self.encoding = get_encoding(self.language,
self.inputencoding, self.format,
self.cjk_encoding)
self.initial_version = self.read_version() self.initial_version = self.read_version()
# Second pass over header and preamble, now we know the file encoding # Second pass over header and preamble, now we know the file encoding
@ -283,8 +299,8 @@ class LyX_Base:
self.set_format() self.set_format()
self.set_textclass() self.set_textclass()
if self.encoding == "auto": if self.encoding == "auto":
self.encoding = get_encoding(self.language, self.encoding, self.format, self.cjk_encoding) self.encoding = get_encoding(self.language, self.encoding,
self.format, self.cjk_encoding)
if self.preamble: if self.preamble:
i = find_token(self.header, '\\textclass', 0) + 1 i = find_token(self.header, '\\textclass', 0) + 1
preamble = ['\\begin_preamble'] + self.preamble + ['\\end_preamble'] preamble = ['\\begin_preamble'] + self.preamble + ['\\end_preamble']
@ -346,8 +362,9 @@ class LyX_Base:
def read_version(self): def read_version(self):
""" Searchs for clues of the LyX version used to write the file, returns the """ Searchs for clues of the LyX version used to write the
most likely value, or None otherwise.""" file, returns the most likely value, or None otherwise."""
for line in self.header: for line in self.header:
if line[0] != "#": if line[0] != "#":
return None return None
@ -370,7 +387,8 @@ class LyX_Base:
def set_version(self): def set_version(self):
" Set the header with the version used." " Set the header with the version used."
self.header[0] = "#LyX %s created this file. For more info see http://www.lyx.org/" % version_lyx2lyx self.header[0] = " ".join(["#LyX %s created this file." % version__,
"For more info see http://www.lyx.org/"])
if self.header[1][0] == '#': if self.header[1][0] == '#':
del self.header[1] del self.header[1]
@ -426,9 +444,11 @@ class LyX_Base:
for step in convertion_chain: for step in convertion_chain:
steps = getattr(__import__("lyx_" + step), mode) steps = getattr(__import__("lyx_" + step), mode)
self.warning("Convertion step: %s - %s" % (step, mode), default_debug_level + 1) self.warning("Convertion step: %s - %s" % (step, mode),
default_debug__ + 1)
if not steps: if not steps:
self.error("The convertion to an older format (%s) is not implemented." % self.format) self.error("The convertion to an older "
"format (%s) is not implemented." % self.format)
multi_conv = len(steps) != 1 multi_conv = len(steps) != 1
for version, table in steps: for version, table in steps:
@ -442,24 +462,26 @@ class LyX_Base:
try: try:
conv(self) conv(self)
except: except:
self.warning("An error ocurred in %s, %s" % (version, str(conv)), self.warning("An error ocurred in %s, %s" %
default_debug_level) (version, str(conv)),
default_debug__)
if not self.try_hard: if not self.try_hard:
raise raise
self.status = 2 self.status = 2
else: else:
self.warning("%lf: Elapsed time on %s" % (time.time() - init_t, str(conv)), self.warning("%lf: Elapsed time on %s" %
default_debug_level + 1) (time.time() - init_t,
str(conv)), default_debug__ +
1)
self.format = version self.format = version
if self.end_format == self.format: if self.end_format == self.format:
return return
def chain(self): def chain(self):
""" This is where all the decisions related with the convertion are taken. """ This is where all the decisions related with the
It returns a list of modules needed to convert the LyX file from convertion are taken. It returns a list of modules needed to
self.format to self.end_format""" convert the LyX file from self.format to self.end_format"""
self.start = self.format self.start = self.format
format = self.format format = self.format
@ -474,7 +496,9 @@ class LyX_Base:
if not correct_version: if not correct_version:
if format <= 215: if format <= 215:
self.warning("Version does not match file format, discarding it. (Version %s, format %d)" %(self.initial_version, self.format)) self.warning("Version does not match file format, "
"discarding it. (Version %s, format %d)" %
(self.initial_version, self.format))
for rel in format_relation: for rel in format_relation:
if format in rel[1]: if format in rel[1]:
initial_step = rel[0] initial_step = rel[0]
@ -522,10 +546,12 @@ class LyX_Base:
def get_toc(self, depth = 4): def get_toc(self, depth = 4):
" Returns the TOC of this LyX document." " Returns the TOC of this LyX document."
paragraphs_filter = {'Title' : 0,'Chapter' : 1, 'Section' : 2, 'Subsection' : 3, 'Subsubsection': 4} paragraphs_filter = {'Title' : 0,'Chapter' : 1, 'Section' : 2,
'Subsection' : 3, 'Subsubsection': 4}
allowed_insets = ['Quotes'] allowed_insets = ['Quotes']
allowed_parameters = '\\paragraph_spacing', '\\noindent', '\\align', '\\labelwidthstring', "\\start_of_appendix", "\\leftindent" allowed_parameters = ('\\paragraph_spacing', '\\noindent',
'\\align', '\\labelwidthstring',
"\\start_of_appendix", "\\leftindent")
sections = [] sections = []
for section in paragraphs_filter.keys(): for section in paragraphs_filter.keys():
sections.append('\\begin_layout %s' % section) sections.append('\\begin_layout %s' % section)
@ -550,8 +576,9 @@ class LyX_Base:
k = i + 1 k = i + 1
# skip paragraph parameters # skip paragraph parameters
while not self.body[k].strip() or self.body[k].split()[0] in allowed_parameters: while not self.body[k].strip() or self.body[k].split()[0] \
k = k +1 in allowed_parameters:
k += 1
while k < j: while k < j:
if check_token(self.body[k], '\\begin_inset'): if check_token(self.body[k], '\\begin_inset'):
@ -565,7 +592,7 @@ class LyX_Base:
k = end + 1 k = end + 1
else: else:
par.append(self.body[k]) par.append(self.body[k])
k = k + 1 k += 1
# trim empty lines in the end. # trim empty lines in the end.
while par and par[-1].strip() == '': while par and par[-1].strip() == '':
@ -578,19 +605,23 @@ class LyX_Base:
return toc_par return toc_par
class File(LyX_Base): class File(LyX_base):
" This class reads existing LyX files." " This class reads existing LyX files."
def __init__(self, end_format = 0, input = "", output = "", error = "", debug = default_debug_level, try_hard = 0, cjk_encoding = ''):
LyX_Base.__init__(self, end_format, input, output, error, debug, try_hard, cjk_encoding) def __init__(self, end_format = 0, input = "", output = "", error = "",
debug = default_debug__, try_hard = 0, cjk_encoding = ''):
LyX_base.__init__(self, end_format, input, output, error,
debug, try_hard, cjk_encoding)
self.read() self.read()
class NewFile(LyX_Base): class NewFile(LyX_base):
" This class is to create new LyX files." " This class is to create new LyX files."
def set_header(self, **params): def set_header(self, **params):
# set default values # set default values
self.header.extend([ self.header.extend([
"#LyX xxxx created this file. For more info see http://www.lyx.org/", "#LyX xxxx created this file."
"For more info see http://www.lyx.org/",
"\\lyxformat xxx", "\\lyxformat xxx",
"\\begin_document", "\\begin_document",
"\\begin_header", "\\begin_header",
@ -639,7 +670,8 @@ class NewFile(LyX_Base):
class Paragraph: class Paragraph:
# unfinished implementation, it is missing the Text and Insets representation. # unfinished implementation, it is missing the Text and Insets
# representation.
" This class represents the LyX paragraphs." " This class represents the LyX paragraphs."
def __init__(self, name, body=[], settings = [], child = []): def __init__(self, name, body=[], settings = [], child = []):
""" Parameters: """ Parameters:
@ -653,7 +685,9 @@ class Paragraph:
self.child = child self.child = child
def asLines(self): def asLines(self):
" Converts the paragraph to a list of strings, representing it in the LyX file." """ Converts the paragraph to a list of strings, representing
it in the LyX file."""
result = ['','\\begin_layout %s' % self.name] result = ['','\\begin_layout %s' % self.name]
result.extend(self.settings) result.extend(self.settings)
result.append('') result.append('')
@ -669,13 +703,3 @@ class Paragraph:
result.append('\\end_deeper') result.append('\\end_deeper')
return result return result
class Inset:
" This class represents the LyX insets."
pass
class Text:
" This class represents simple chuncks of text."
pass

View File

@ -1,6 +1,7 @@
#! /usr/bin/env python #! /usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2002-2004 José Matos <jamatos@lyx.org> # Copyright (C) 2002-2007 José Matos <jamatos@lyx.org>
# Copyright (C) 2002-2004 Dekel Tsur <dekel@lyx.org>
# #
# This program is free software; you can redistribute it and/or # This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License # modify it under the terms of the GNU General Public License
@ -16,86 +17,65 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
import getopt " Program used to convert between different versions of the lyx file format."
import optparse
import sys import sys
import LyX import LyX
def usage():
print """Usage: lyx2lyx [options] [file]
Convert old lyx file <file> to newer format, files can be compressed with gzip.
If there no file is specified then the standard input is assumed, in this case
gziped files are not handled.
Options:
-h, --help this information
-v, --version output version information and exit
-l, --list list all available formats
-d, --debug level level=0..2 (O_ no debug information, 2_verbose)
default: level=1
-e, --err error_file name of the error file or else goes to stderr
-f, --from version initial version (optional)
-t, --to version final version (optional)
-o, --output name name of the output file or else goes to stdout
-n, --try-hard try hard (ignore any convertion errors)
-c, --cjk [encoding] files in format 248 and lower are read and
written in the format of CJK-LyX.
If encoding is not given or 'auto' the encoding
is determined from the locale.
-q, --quiet same as --debug=0"""
def parse_options(argv):
_options = ["help", "version", "list", "debug=", "err=", "from=", "to=", "output=", "try-hard", "cjk", "quiet"]
try:
opts, args = getopt.getopt(argv[1:], "c:d:e:f:hlno:qt:v", _options)
except getopt.error:
usage()
sys.exit(2)
end_format, input, output, error, debug, try_hard = 0, "", "", "", LyX.default_debug_level, 0
cjk_encoding = ''
for o, a in opts:
if o in ("-h", "--help"):
usage()
sys.exit()
if o in ("-v", "--version"):
print "lyx2lyx, version %s" %(LyX.version_lyx2lyx)
print "Copyright (C) 2002-2004 José Matos and Dekel Tsur"
sys.exit()
if o in ("-d", "--debug"):
debug = int(a)
if o in ("-q", "--quiet"):
debug = 0
if o in ("-l", "--list"):
print LyX.formats_list()
sys.exit()
if o in ("-o", "--output"):
output = a
if o in ("-t", "--to"):
end_format = a
if o in ("-e","--err"):
error = a
if o in ("-n", "--try-hard"):
try_hard = 1
if o in ("-c", "--cjk"):
if a == '':
cjk_encoding = 'auto'
else:
cjk_encoding = a
if args:
input = args[0]
return end_format, input, output, error, debug, try_hard, cjk_encoding
def main(argv):
end_format, input, output, error, debug, try_hard, cjk_encoding = parse_options(argv)
file = LyX.File(end_format, input, output, error, debug, try_hard, cjk_encoding)
file.convert()
file.write()
return file.status
if __name__ == "__main__": if __name__ == "__main__":
sys.exit(main(sys.argv))
args = {}
args["usage"] = "usage: %prog [options] [file]"
args["version"] = """lyx2lyx, version %s
Copyright (C) 2007 José Matos and Dekel Tsur""" % LyX.version__
args["description"] = """Convert old lyx file <file> to newer format,
files can be compressed with gzip. If there no file is specified then
the standard input is assumed, in this case gziped files are not
handled."""
parser = optparse.OptionParser(**args)
parser.set_defaults(debug=LyX.default_debug__, cjk_encoding = 'auto')
parser.add_option("-d", "--debug", type="int",
help="level=0..2 (O_ quiet, 2_verbose) default: 1")
parser.add_option("-q", "--quiet",
action="store_const", const=0, dest="debug")
parser.add_option("-v", "--verbose",
action="store_const", const=1, dest="debug")
parser.add_option("--noisy",
action="store_const", const=2, dest="debug")
parser.add_option("-c", "--encoding", dest="cjk_encoding",
help="files in format 248 and lower are read and"
" written in the format of CJK-LyX."
"If encoding is not given or 'auto' the encoding"
"is determined from the locale.")
parser.add_option("-e", "--err", dest="error",
help= "file name of the error file else goes to stderr")
parser.add_option("-o", "--output",
help= "name of the output file else goes to stdout")
parser.add_option("-t", "--to", dest= "end_format",
help= "destination file format, default (latest)")
parser.add_option("-l", "--list", action="store_true",
help = "list all available formats")
parser.add_option("-n", "--try-hard", action="store_true",
help = "try hard (ignore any convertion errors)")
(options, args) = parser.parse_args()
if args:
options.input = args[0]
else:
options.input = None
if options.list:
print LyX.formats_list()
sys.exit()
else:
del options.list
doc = LyX.File(**options.__dict__)
doc.convert()
doc.write()
sys.exit(doc.status)