1 # Copyright (c) Twisted Matrix Laboratories.
2 # See LICENSE for details.
6 from zope.interface import Interface, Attribute
8 from twisted.lore import process, indexer, numberer, htmlbook
10 from twisted.python import usage, reflect
11 from twisted import plugin as plugin
13 class IProcessor(Interface):
17 name = Attribute("The user-facing name of this processor")
19 moduleName = Attribute(
20 "The fully qualified Python name of the object defining "
21 "this processor. This object (typically a module) should "
22 "have a C{factory} attribute with C{generate_<output>} methods.")
25 class Options(usage.Options):
27 longdesc = "lore converts documentation formats."
29 optFlags = [["plain", 'p', "Report filenames without progress bar"],
30 ["null", 'n', "Do not report filenames"],
31 ["number", 'N', "Add chapter/section numbers to section headings"],
35 ["input", "i", 'lore'],
36 ["inputext", "e", ".xhtml", "The extension that your Lore input files have"],
37 ["docsdir", "d", None],
39 ["output", "o", 'html'],
40 ["index", "x", None, "The base filename you want to give your index file"],
41 ["book", "b", None, "The book file to generate a book from"],
42 ["prefixurl", None, "", "The prefix to stick on to relative links; only useful when processing directories"],
45 compData = usage.Completions(
46 extraActions=[usage.CompleteFiles(descr="files", repeat=True)])
48 def __init__(self, *args, **kw):
49 usage.Options.__init__(self, *args, **kw)
52 def opt_config(self, s):
54 k, v = s.split('=', 1)
59 def parseArgs(self, *files):
63 def getProcessor(input, output, config):
64 plugins = plugin.getPlugins(IProcessor)
66 if plug.name == input:
67 module = reflect.namedModule(plug.moduleName)
70 # try treating it as a module name
72 module = reflect.namedModule(input)
74 print '%s: no such input: %s' % (sys.argv[0], input)
77 return process.getProcessor(module, output, config)
78 except process.NoProcessorError, e:
79 print "%s: %s" % (sys.argv[0], e)
82 def getWalker(df, opt):
83 klass = process.Walker
85 klass = process.PlainReportingWalker
87 klass = process.NullReportingWalker
88 return klass(df, opt['inputext'], opt['linkrel'])
91 def runGivenOptions(opt):
92 """Do everything but parse the options; useful for testing.
93 Returns a descriptive string if there's an error."""
97 book = htmlbook.Book(opt['book'])
99 df = getProcessor(opt['input'], opt['output'], opt.config)
101 return 'getProcessor() failed'
103 walker = getWalker(df, opt)
106 for filename in opt['files']:
107 walker.walked.append(('', filename))
109 for filename in book.getFiles():
110 walker.walked.append(('', filename))
112 walker.walkdir(opt['docsdir'] or '.', opt['prefixurl'])
115 indexFilename = opt['index']
117 indexFilename = book.getIndexFilename()
122 indexer.setIndexFilename("%s.%s" % (indexFilename, opt['output']))
124 indexer.setIndexFilename(None)
126 ## TODO: get numberSections from book, if any
127 numberer.setNumberSections(opt['number'])
132 for (file, errors) in walker.failures:
134 print "%s:%s" % (file, error)
135 return 'Walker failures'
142 except usage.UsageError, errortext:
143 print '%s: %s' % (sys.argv[0], errortext)
144 print '%s: Try --help for usage details.' % sys.argv[0]
147 result = runGivenOptions(opt)
153 if __name__ == '__main__':