One Hat Cyber Team
Your IP :
216.73.217.15
Server IP :
157.15.65.100
Server :
Linux 157-15-65-100.cprapid.com 5.14.0-362.24.2.el9_3.x86_64 #1 SMP PREEMPT_DYNAMIC Sat Mar 30 14:11:54 EDT 2024 x86_64
Server Software :
Apache
PHP Version :
8.2.28
Buat File
|
Buat Folder
Eksekusi
Dir :
~
/
bin
/
Edit File:
yelp-build
#!/usr/bin/python3 # # yelp-build # Copyright (C) 2010-2020 Shaun McCance <shaunm@gnome.org> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. import configparser import os import sys import shutil import subprocess import tempfile import urllib.parse import uuid import lxml.etree import lxml.ElementInclude XSL_DB2HTML = '/usr/share/yelp-xsl/xslt/docbook/html/db2html.xsl' XSL_DB2XHTML = '/usr/share/yelp-xsl/xslt/docbook/html/db2xhtml.xsl' XSL_MALCACHE = '/usr/share/yelp-xsl/xslt/mallard/cache/mal-cache.xsl' XSL_MAL2HTML = '/usr/share/yelp-xsl/xslt/mallard/html/mal2html.xsl' XSL_MAL2XHTML = '/usr/share/yelp-xsl/xslt/mallard/html/mal2xhtml.xsl' XSL_MAL_OPF='/usr/share/yelp-tools/xslt/mal-opf.xsl' XSL_MAL_NCX='/usr/share/yelp-tools/xslt/mal-ncx.xsl' YELP_JS_DIR = '/usr/share/yelp-xsl/js' XSLCOMMON = (''' <xsl:variable name="yelp.internal.datadir" select="'{intdatadir}'"/> <xsl:param name="html.css.root" select="$yelp.internal.datadir"/> <xsl:param name="html.js.root" select="$yelp.internal.datadir"/> {includes} <xsl:template name="html.css"> <xsl:param name="node" select="."/> <xsl:variable name="yelp.locale"> <xsl:choose> <xsl:when test="$node/@xml:lang != ''"> <xsl:value-of select="$node/@xml:lang"/> </xsl:when> <xsl:when test="$node/@lang != ''"> <xsl:value-of select="$node/@lang"/> </xsl:when> <xsl:otherwise> <xsl:text>C</xsl:text> </xsl:otherwise> </xsl:choose> </xsl:variable> <exsl:document href="{{$yelp.internal.datadir}}{{$yelp.locale}}.css" method="text"> <xsl:call-template name="html.css.content"> <xsl:with-param name="node" select="$node"/> <xsl:with-param name="direction"> <xsl:call-template name="l10n.direction"> <xsl:with-param name="lang" select="$yelp.locale"/> </xsl:call-template> </xsl:with-param> </xsl:call-template> </exsl:document> <link rel="stylesheet" type="text/css" href="{{$html.css.root}}{{$yelp.locale}}.css"/> </xsl:template> <xsl:template name="html.js.script"> <xsl:param name="node" select="."/> <exsl:document href="{{$yelp.internal.datadir}}yelp.js" method="text"> <xsl:call-template name="html.js.content"> <xsl:with-param name="node" select="$node"/> </xsl:call-template> </exsl:document> <script type="text/javascript" src="{{$html.js.root}}yelp.js"/> </xsl:template> ''') DB2HTML = (''' <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" xmlns="http://www.w3.org/1999/xhtml" extension-element-prefixes="exsl" version="1.0"> <xsl:import href="file://{xslfile}"/> ''' + XSLCOMMON + ''' </xsl:stylesheet> ''') MAL2HTML = (''' <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:mal="http://projectmallard.org/1.0/" xmlns:cache="http://projectmallard.org/cache/1.0/" xmlns:exsl="http://exslt.org/common" xmlns="http://www.w3.org/1999/xhtml" exclude-result-prefixes="mal cache" extension-element-prefixes="exsl" version="1.0"> <xsl:import href="file://{xslfile}"/> <xsl:param name="mal.cache.file" select="'file://{cachefile}'"/> ''' + XSLCOMMON + ''' <xsl:template match="/"> <xsl:for-each select="cache:cache/mal:page | cache:cache/mal:stack"> <xsl:variable name="href" select="@cache:href"/> <xsl:for-each select="document(@cache:href)"> <xsl:for-each select="mal:page | mal:stack/mal:page"> <xsl:call-template name="html.output"/> </xsl:for-each> </xsl:for-each> </xsl:for-each> </xsl:template> </xsl:stylesheet> ''') class InputFile: def __init__(self, filepath, filename, sitedir=None): self.filepath = filepath self.filename = filename self.absfile = os.path.join(filepath, filename) self.absdir = os.path.dirname(self.absfile) self.sitedir = sitedir or '' self.sitefilename = self.sitedir + self.filename class PathResolver(lxml.etree.Resolver): def __init__(self, srcdir, path): if srcdir.endswith('/'): self.srcdir = srcdir else: self.srcdir = srcdir + '/' self.path = path def resolve(self, uri, id, context): if os.path.exists(uri): return self.resolve_filename(uri, context) if uri.startswith(self.srcdir): ref = uri[len(self.srcdir):] else: ref = uri for p in self.path: tryfile = os.path.join(p, ref) if os.path.exists(tryfile): return self.resolve_filename(tryfile, context) return None class Builder: name = None desc = None blurb = None formats = [] arguments = [] postblurb = None config = None def __init__(self, yelpbuild): self.yelpbuild = yelpbuild self.options = {} self.fileargs = [] self.tmpdir = None def __del__(self): if self.tmpdir is not None: shutil.rmtree(self.tmpdir) self.tmpdir = None def parse_args(self, args): while len(args) > 0: argdef = None if args[0].startswith('--'): for arg_ in self.arguments: if args[0] == '--' + arg_[0]: argdef = arg_ break if argdef is None: self.print_help() return 1 elif args[0].startswith('-'): for arg_ in self.arguments: if args[0] == arg_[1]: argdef = arg_ break if argdef is None: self.print_help() return 1 if argdef is not None: takesarg = (argdef[2] is not None) if takesarg: if len(args) < 2: self.print_help() return 1 self.options.setdefault(argdef[0], []) self.options[argdef[0]].append(args[1]) args = args[2:] else: self.options[argdef[0]] = True args = args[1:] else: self.fileargs.append(args[0]) args = args[1:] cfgfile = None if len(self.fileargs) > 0: cfgfile = os.path.join(os.path.dirname(self.fileargs[0]), '.yelp-tools.cfg') if not os.path.exists(cfgfile): cfgfile = None if cfgfile is None: cfgfile = os.path.join(os.getcwd(), '.yelp-tools.cfg') if os.path.exists(cfgfile): self.config = configparser.ConfigParser() try: self.config.read(cfgfile) except Exception as e: print(e, file=sys.stderr) sys.exit(1) return 0 def get_option_bool(self, arg): if arg in self.options: return self.options[arg] == True if self.config is not None: val = self.config.get('build:' + self.name, arg, fallback=None) if val is not None: return (val == 'true') val = self.config.get('build', arg, fallback=None) if val is not None: return (val == 'true') val = self.config.get('default', arg, fallback=None) if val is not None: return (val == 'true') return False def get_option_str(self, arg): if arg in self.options: if isinstance(self.options[arg], list): return self.options[arg][-1] if self.config is not None: val = self.config.get('build:' + self.name, arg, fallback=None) if val is not None: return val val = self.config.get('build', arg, fallback=None) if val is not None: return val val = self.config.get('default', arg, fallback=None) if val is not None: return val return None def get_option_list(self, arg): if arg in self.options: if isinstance(self.options[arg], list): ret = [] for opt in self.options[arg]: ret.extend(opt.replace(',', ' ').split()) return ret if self.config is not None: val = self.config.get('build:' + self.name, arg, fallback=None) if val is not None: return val.replace(',', ' ').split() val = self.config.get('build', arg, fallback=None) if val is not None: return val.replace(',', ' ').split() val = self.config.get('default', arg, fallback=None) if val is not None: return val.replace(',', ' ').split() return None def get_xml(self, infile, path): parser = lxml.etree.XMLParser() parser.resolvers.add(PathResolver(os.path.realpath(infile.absdir), path)) tree = lxml.etree.parse(infile.absfile, parser=parser) def pathloader(href, parse, encoding=None): usefile = os.path.join(infile.absdir, href) if not os.path.exists(href): usefile = None if usefile is None: absdir = infile.absdir if not absdir.endswith('/'): absdir = absdir + '/' ref = href if ref.startswith(absdir): ref = ref[len(absdir):] for p in path: tryfile = os.path.join(p, ref) if os.path.exists(tryfile): usefile = tryfile break if usefile is not None: if parse == 'xml': return lxml.etree.parse(usefile, parser=parser).getroot() elif parse == 'text': return open(usefile).read() return None lxml.ElementInclude.include(tree, loader=pathloader) return tree def iter_files(self, sitedir=None): issite = self.get_option_bool('site') if len(self.fileargs) == 0: self.fileargs.append('.') for filearg in self.fileargs: if os.path.isdir(filearg): if issite: for infile in self.iter_site(filearg, '/'): yield infile else: for fname in os.listdir(filearg): if fname.endswith('.page'): yield InputFile(filearg, fname) else: if issite: # FIXME: should do some normalization here, I guess. # It's hard to get this perfect without a defined start dir yield InputFile(os.getcwd(), filearg, '/' + os.path.dirname(filearg)) else: yield InputFile(os.getcwd(), filearg) def iter_site(self, filepath, sitedir): for fname in os.listdir(filepath): newpath = os.path.join(filepath, fname) if os.path.isdir(newpath): # FIXME https://github.com/projectmallard/pintail/issues/36 if fname == '__pintail__': continue for infile in self.iter_site(newpath, sitedir + fname + '/'): yield infile elif fname.endswith('.page'): yield InputFile(filepath, fname, sitedir) def create_tmpdir(self): if self.tmpdir is None: self.tmpdir = tempfile.mkdtemp() def print_help(self): print('Usage: yelp-build ' + self.name + ' [OPTIONS] [FILES]') print('Formats: ' + ' '.join(self.formats) + '\n') #FIXME: prettify names of formats if self.blurb is not None: print(self.blurb + '\n') print('Options:') maxarglen = 2 args = [] for arg in self.arguments: argkey = '--' + arg[0] if arg[1] is not None: argkey = arg[1] + ', ' + argkey if arg[2] is not None: argkey = argkey + ' ' + arg[2] args.append((argkey, arg[3])) for arg in args: maxarglen = max(maxarglen, len(arg[0]) + 1) for arg in args: print(' ' + (arg[0]).ljust(maxarglen) + ' ' + arg[1]) if self.postblurb is not None: print(self.postblurb) def main(self, args): pass class CacheBuilder (Builder): name = 'cache' desc = 'Convert a Mallard cache file' blurb = ('Create a Mallard cache file from the page files FILES.\n' + 'If FILES contains directories, all .page files in those\n' + 'directories will be used.') formats = ['mallard'] arguments = [ ('help', '-h', None, 'Show this help and exit'), ('output', '-o', 'OUT', 'Output files in the directory OUT'), ('path', '-p', 'PATH', 'Extra directories to search for files'), ('site', '-s', None, 'Treat pages as belonging to a Mallard site') ] def build_cache_in(self, filename): with open(filename, 'w') as cachein: print('<cache:cache xmlns:cache="http://projectmallard.org/cache/1.0/"' + ' xmlns:site="http://projectmallard.org/site/1.0/"' ' xmlns="http://projectmallard.org/1.0/">', file=cachein) for infile in self.iter_files(): if infile.filename.endswith('.page'): page = '<page' elif infile.filename.endswith('.stack'): page = '<stack' else: continue page += ' cache:href="file://' + urllib.parse.quote(os.path.realpath(infile.absfile)) + '"' if self.get_option_bool('site'): page += ' site:dir="' + infile.sitedir + '"' page += '/>' print(page, file=cachein) print('</cache:cache>', file=cachein) def main(self, args, output=None, path=None): if self.parse_args(args) != 0: return 1 if 'help' in self.options: self.print_help() return 0 retcode = 0 self.create_tmpdir() cacheinfile = os.path.join(self.tmpdir, 'index.cache.in') self.build_cache_in(cacheinfile) if output is None: output = self.get_option_str('output') if output is None: output = 'index.cache' if path is None: path = self.get_option_list('path') if path is None: path = ':' else: path = ':'.join(path) retcode = subprocess.call(['xsltproc', '--xinclude', '-o', output, '--path', path, XSL_MALCACHE, cacheinfile]) return retcode class XhtmlBuilder (Builder): name = 'xhtml' desc = 'Convert input files to XHTML' blurb = ('Create XHTML output from the input files FILES.\n' + 'FILES can be DocBook files, Mallard page files,\n' + 'or directories containing Mallard page files.') formats = ['docbook4', 'docbook5', 'mallard'] arguments = [ ('help', '-h', None, 'Show this help and exit'), ('cache', '-c', 'CACHE', 'Use the existing Mallard cache CACHE'), ('output', '-o', 'OUT', 'Output files in the directory OUT'), ('xsl', '-x', 'CUSTOM', 'Import the custom XSLT file CUSTOM'), ('path', '-p', 'PATH', 'Extra directories to search for files'), ('ignore', '-i', None, 'Ignore missing media files') ] def __init__(self, yelpbuild, xhtml=True, epub=False): super().__init__(yelpbuild) self.mal2html = None self.db2html = None self.xhtml = xhtml self.epub = epub if self.epub: self.intdatadir = 'yelp' else: self.intdatadir = '' self.cacheinfile = None def build_mallard_all(self, cache=None, output=None, xsl=None, path=None): if self.mal2html is not None: # We build all the pages on the first call, because it's faster return 0 if path is None: path = self.get_option_list('path') self.create_tmpdir() if cache is None: cachefile = self.get_option_str('cache') else: cachefile = cache cachebuilder = CacheBuilder(self.yelpbuild) if cachefile is None: cachefile = os.path.join(self.tmpdir, 'index.cache') retcode = cachebuilder.main(self.fileargs, output=cachefile, path=path) if retcode != 0: return retcode self.cacheinfile = cachefile else: cachefile = os.path.realpath(cachefile) self.cacheinfile = os.path.join(self.tmpdir, 'index.cache.in') cachebuilder.parse_args(self.fileargs) cachebuilder.build_cache_in(self.cacheinfile) self.mal2html = os.path.join(self.tmpdir, 'mal2html.xsl') with open(self.mal2html, 'w') as xslout: if self.xhtml: xslfile = XSL_MAL2XHTML else: xslfile = XSL_MAL2HTML includes = '' if xsl is None: customxsl = self.get_option_str('xsl') else: customxsl = xsl if customxsl is not None: customxsl = urllib.parse.quote(os.path.realpath(customxsl)) includes += '<xsl:include href="file://' + customxsl + '"/>' if self.epub: includes += '''<xsl:param name="mal.if.target" select="'target:epub target:html target:xhtml'"/>''' includes += '''<xsl:template mode="html.header.mode" match="mal:page"/>''' includes += '''<xsl:template mode="html.footer.mode" match="mal:page"/>''' xslout.write(MAL2HTML.format(xslfile=xslfile, cachefile=cachefile, includes=includes, intdatadir=self.intdatadir)) if output is None: output = self.get_option_str('output') if output is None: output = os.getcwd() else: if not os.path.isdir(output): print('Output must be a directory', file=sys.stderr) return 1 if not output.endswith('/'): # xsltproc is picky about this output = output + '/' if path is None: pathstr = ':' else: pathstr = ':'.join(path) retcode = subprocess.call(['xsltproc', '--xinclude', '-o', output, '--path', pathstr, '--stringparam', 'mal.cache.file', cachefile, self.mal2html, self.cacheinfile]) return retcode def build_docbook(self, infile, output=None, xsl=None, path=None): if self.db2html is None: self.create_tmpdir() self.db2html = os.path.join(self.tmpdir, 'db2html.xsl') with open(self.db2html, 'w') as xslout: if self.xhtml: xslfile = XSL_DB2XHTML else: xslfile = XSL_DB2HTML includes = '' if xsl is not None: customxsl = xsl else: customxsl = self.get_option_str('xsl') if customxsl is not None: customxsl = urllib.parse.quote(os.path.realpath(customxsl)) includes += '<xsl:include href="file://' + customxsl + '"/>' xslout.write(DB2HTML.format(xslfile=xslfile, includes=includes, intdatadir=self.intdatadir)) if output is None: output = self.get_option_str('output') if output is None: output = os.getcwd() else: if not os.path.isdir(output): print('Output must be a directory', file=sys.stderr) return 1 if path is None: path = self.get_option_list('path') if path is None: pathstr = ':' else: pathstr = ':'.join(path) retcode = subprocess.call(['xsltproc', '--xinclude', '-o', output, '--path', pathstr, self.db2html, infile.absfile]) return retcode def main(self, args, cache=None, output=None, xsl=None, path=None, ignore=None): if self.parse_args(args) != 0: return 1 if 'help' in self.options: self.print_help() return 0 if path is None: pathopt = self.get_option_list('path') else: pathopt = path path = [] if pathopt is not None: for p in pathopt: path.extend(p.split(':')) if output is None: output = self.get_option_str('output') srcs = {} for infile in self.iter_files(): if infile.filename.endswith('.page') or infile.filename.endswith('.stack'): retcode = self.build_mallard_all(cache=cache, output=output, xsl=xsl, path=path) if retcode != 0: return retcode if output is not None: tree = self.get_xml(infile, path) if tree is None: return 1 for el in tree.xpath('//*[@src]'): src = el.get('src') srcs.setdefault(src, []) orig = os.path.join(os.path.realpath(infile.absdir), src) if orig not in srcs[src]: srcs[src].append(orig) elif infile.filename.endswith('.docbook') or infile.filename.endswith('.xml'): retcode = self.build_docbook(infile, output=output, xsl=xsl, path=path) if retcode != 0: return retcode if output is not None: tree = self.get_xml(infile, path) if tree is None: return 1 for el in tree.xpath('//*[@fileref]'): src = el.get('fileref') srcs.setdefault(src, []) orig = os.path.join(os.path.realpath(infile.absdir), src) if orig not in srcs[src]: srcs[src].append(orig) else: print('Error: No builder for ' + infile.filename) return 1 if ignore is None: ignore = self.get_option_bool('ignore') tocopy = {} for src in srcs: useorig = None for orig in srcs[src]: if os.path.exists(orig): if useorig is None: useorig = orig else: print('Warning: Multiple sources for ' + src + '. Using first.', file=sys.stderr) if useorig is None: for p in path: tryorig = os.path.join(p, src) if os.path.exists(tryorig): useorig = tryorig break if useorig is None: if ignore: print('Warning: No source found for ' + src, file=sys.stderr) else: print('Error: No source found for ' + src, file=sys.stderr) return 1 if useorig is not None: destfile = os.path.join(output, src) destdir = os.path.dirname(destfile) os.makedirs(destdir, exist_ok=True) shutil.copyfile(useorig, destfile) if output is None: shutil.copyfile(os.path.join(YELP_JS_DIR, 'highlight.pack.js'), os.path.join(self.intdatadir, 'highlight.pack.js')) else: shutil.copyfile(os.path.join(YELP_JS_DIR, 'highlight.pack.js'), os.path.join(output, self.intdatadir, 'highlight.pack.js')) return 0 class HtmlBuilder (Builder): name = 'html' desc = 'Convert input files to HTML' blurb = ('Create HTML output from the input files FILES.\n' + 'FILES can be DocBook files, Mallard page files,\n' + 'or directories containing Mallard page files.') formats = ['docbook4', 'docbook5', 'mallard'] arguments = [ ('help', '-h', None, 'Show this help and exit'), ('cache', '-c', 'CACHE', 'Use the existing Mallard cache CACHE'), ('output', '-o', 'OUT', 'Output files in the directory OUT'), ('xsl', '-x', 'CUSTOM', 'Import the custom XSLT file CUSTOM'), ('path', '-p', 'PATH', 'Extra directories to search for files'), ('ignore', '-i', None, 'Ignore missing media files') ] def __init__(self, yelpbuild): super().__init__(yelpbuild) self.xhtmlbuilder = XhtmlBuilder(yelpbuild, xhtml=False) def main(self, args): if self.parse_args(args) != 0: return 1 if 'help' in self.options: self.print_help() return 0 return self.xhtmlbuilder.main(args) class EpubBuilder (Builder): name = 'epub' desc = 'Create an EPUB file for Mallard' blurb = ('Create an EPUB file from the Mallard page files FILES') formats = ['mallard'] arguments = [ ('help', '-h', None, 'Show this help and exit'), ('cache', '-c', 'CACHE', 'Use the existing Mallard cache CACHE'), ('output', '-o', 'OUT', 'Output files in the directory OUT'), ('xsl', '-x', 'CUSTOM', 'Import the custom XSLT file CUSTOM'), ('path', '-p', 'PATH', 'Extra directories to search for files'), ('ignore', '-i', None, 'Ignore missing media files'), ('nozip', None, None, 'Do not zip the output directory') ] def __init__(self, yelpbuild): super().__init__(yelpbuild) def main(self, args): if self.parse_args(args) != 0: return 1 if 'help' in self.options: self.print_help() return 0 output = self.get_option_str('output') nozip = self.get_option_bool('nozip') if nozip: if output is None: output = 'EPUB' if os.path.isfile(output): print('Error: Output must be a directory', file=sys.stderr) sys.exit(1) epubdir = output else: self.create_tmpdir() if output is None: output = 'index.epub' if os.path.isdir(output): print('Error: Output must be a file', file=sys.stderr) sys.exit(1) epubdir = os.path.join(self.tmpdir, 'EPUB') os.makedirs(epubdir, exist_ok=True) os.makedirs(os.path.join(epubdir, 'OPS', 'yelp'), exist_ok=True) xhtmlbuilder = XhtmlBuilder(self.yelpbuild, epub=True) retcode = xhtmlbuilder.main(self.fileargs, cache=self.get_option_str('cache'), output=os.path.join(epubdir, 'OPS'), xsl=self.get_option_str('xsl'), path=self.get_option_list('path'), ignore=self.get_option_bool('ignore')) if retcode != 0: return retcode with open(os.path.join(epubdir, 'mimetype'), 'w') as fd: fd.write('application/epub+zip\n') os.makedirs(os.path.join(epubdir, 'META-INF'), exist_ok=True) with open(os.path.join(epubdir, 'META-INF', 'container.xml'), 'w') as fd: fd.write('<?xml version="1.0" encoding="UTF-8"?>') fd.write('<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">') fd.write('<rootfiles>') fd.write('<rootfile full-path="OPS/opf.opf" media-type="application/oebps-package+xml"/>') fd.write('</rootfiles>') fd.write('</container>\n') path = self.get_option_list('path') if path is None: pathstr = ':' else: pathstr = ':'.join(path) epubid = str(uuid.uuid4()) opfdata = '' for fname in os.listdir(os.path.join(epubdir, 'OPS', 'yelp')): opfdata += ' OPS/yelp/' + urllib.parse.quote(fname) retcode = subprocess.call(['xsltproc', '--xinclude', '-o', os.path.join(epubdir, 'OPS', 'opf.opf'), '--path', pathstr, '--stringparam', 'opf.id', epubid, '--stringparam', 'opf.data', opfdata, XSL_MAL_OPF, xhtmlbuilder.cacheinfile]) if retcode != 0: return retcode retcode = subprocess.call(['xsltproc', '--xinclude', '-o', os.path.join(epubdir, 'OPS', 'ncx.ncx'), '--path', pathstr, '--stringparam', 'ncx.id', epubid, XSL_MAL_NCX, xhtmlbuilder.cacheinfile]) if retcode != 0: return retcode if not nozip: retcode = subprocess.call(['zip', '-q', '-r', os.path.realpath(output), 'mimetype', 'META-INF', 'OPS'], cwd=os.path.realpath(epubdir)) if retcode != 0: return retcode return 0 class YelpBuild: def __init__(self): pass def main(self): if len(sys.argv) < 2: self.print_usage() return 1 builder = None for cls in Builder.__subclasses__(): if sys.argv[1] == cls.name: builder = cls(self) if builder is None: print('Unrecognized command: ' + sys.argv[1], file=sys.stderr) return 1 return builder.main(sys.argv[2:]) def print_usage(self): print('Usage: yelp-builder <COMMAND> [OPTIONS] [FILES]') namelen = 2 builders = [] for cls in sorted(Builder.__subclasses__(), key=(lambda cls: cls.name or '')): namelen = max(namelen, len(cls.name) + 2) builders.append(cls) print('\nCommands:') for cls in builders: print(' ' + cls.name.ljust(namelen) + cls.desc) if __name__ == '__main__': try: sys.exit(YelpBuild().main()) except KeyboardInterrupt: sys.exit(1)
Simpan