mirror of https://github.com/tLDP/python-tldp
logic overhaul; abstract inventory creation
each function was creating its own inventory; this is not necessary also, if a user wants only to build a single document, it is not necessary to scan the entire source and output directory (small, but perceptible speedup to the end user) better checking of the various different possible argument types, since an argument can be a filename, a status_class or a stem
This commit is contained in:
parent
16885508da
commit
dfc9855991
226
tldp/driver.py
226
tldp/driver.py
|
@ -9,93 +9,67 @@ import logging
|
||||||
from argparse import Namespace
|
from argparse import Namespace
|
||||||
|
|
||||||
import tldp
|
import tldp
|
||||||
|
|
||||||
|
from tldp.inventory import status_classes, status_types
|
||||||
from tldp.utils import arg_isloglevel
|
from tldp.utils import arg_isloglevel
|
||||||
|
from tldp.sources import arg_issourcedoc
|
||||||
|
|
||||||
logformat = '%(levelname)-9s %(name)s %(filename)s#%(lineno)s %(funcName)s %(message)s'
|
logformat = '%(levelname)-9s %(name)s %(filename)s#%(lineno)s %(funcName)s %(message)s'
|
||||||
logging.basicConfig(stream=sys.stderr, format=logformat, level=logging.ERROR)
|
logging.basicConfig(stream=sys.stderr, format=logformat, level=logging.ERROR)
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def detail(config, args):
|
def summary(config, inv=None):
|
||||||
i = tldp.inventory.Inventory(config.pubdir, config.sourcedir)
|
if inv is None:
|
||||||
|
inv = tldp.inventory.Inventory(config.pubdir, config.sourcedir)
|
||||||
width = Namespace()
|
width = Namespace()
|
||||||
width.status = max([len(x) for x in tldp.inventory.status_types])
|
width.status = max([len(x) for x in status_types])
|
||||||
width.stem = max([len(x) for x in i.source.keys()])
|
width.count = len(str(len(inv.source.keys())))
|
||||||
# -- if user just said "list" with no args, then give the user something
|
for status in status_types:
|
||||||
# sane, "all"; it would make sense for this to be "work", too, but
|
|
||||||
# "all" seems to be less surprising
|
|
||||||
#
|
|
||||||
if not args:
|
|
||||||
args.append('all')
|
|
||||||
for arg in args:
|
|
||||||
status_class = tldp.inventory.status_classes[arg]
|
|
||||||
for status in status_class:
|
|
||||||
s = getattr(i, status, None)
|
|
||||||
assert s is not None
|
|
||||||
for stem, doc in s.items():
|
|
||||||
# -- a 'stale' or 'broken' document is implicitly a 'published'
|
|
||||||
# document as well, but we only want to list each document
|
|
||||||
# once
|
|
||||||
#
|
|
||||||
if doc.status == status:
|
|
||||||
doc.detail(width, config.verbose, file=sys.stdout)
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
def status(config, args):
|
|
||||||
i = tldp.inventory.Inventory(config.pubdir, config.sourcedir)
|
|
||||||
width = Namespace()
|
|
||||||
width.status = max([len(x) for x in tldp.inventory.status_types])
|
|
||||||
width.count = len(str(len(i.source.keys())))
|
|
||||||
for status in tldp.inventory.status_types:
|
|
||||||
if status == 'all':
|
if status == 'all':
|
||||||
continue
|
continue
|
||||||
count = len(getattr(i, status, 0))
|
count = len(getattr(inv, status, 0))
|
||||||
s = '{0:{w.status}} {1:{w.count}} '.format(status, count, w=width)
|
s = '{0:{w.status}} {1:{w.count}} '.format(status, count, w=width)
|
||||||
print(s, end="")
|
print(s, end="")
|
||||||
if config.verbose:
|
if config.verbose:
|
||||||
print(', '.join(getattr(i, status).keys()))
|
print(', '.join(getattr(inv, status).keys()))
|
||||||
else:
|
else:
|
||||||
abbrev = getattr(i, status).keys()
|
abbrev = getattr(inv, status).keys()
|
||||||
s = ''
|
s = ''
|
||||||
if abbrev:
|
if abbrev:
|
||||||
s = s + abbrev.pop(0)
|
s = s + abbrev.pop(0)
|
||||||
while abbrev and len(s) < 50:
|
while abbrev and len(s) < 50:
|
||||||
s = s + ', ' + abbrev.pop()
|
s = s + ', ' + abbrev.pop(0)
|
||||||
if abbrev:
|
if abbrev:
|
||||||
s = s + ', and %d more ...' % (len(abbrev))
|
s = s + ', and %d more ...' % (len(abbrev))
|
||||||
print(s)
|
print(s)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def build(config, args):
|
def detail(config, docs, inv, **kwargs):
|
||||||
targets = list()
|
if inv is None:
|
||||||
stems = list()
|
inv = tldp.inventory.Inventory(config.pubdir, config.sourcedir)
|
||||||
args = set(args)
|
if not docs:
|
||||||
if args:
|
docs = inv.work.values()
|
||||||
for arg in args:
|
width = Namespace()
|
||||||
if os.path.isfile(arg) or os.path.isdir(arg):
|
width.status = max([len(x) for x in status_types])
|
||||||
source = tldp.sources.SourceDocument(arg)
|
width.stem = max([len(x) for x in inv.source.keys()])
|
||||||
targets.append(source)
|
# -- if user just said "list" with no args, then give the user something
|
||||||
else:
|
# sane, "all"; it would make sense for this to be "work", too, but
|
||||||
stems.append(arg)
|
# "all" seems to be less surprising
|
||||||
if stems or not args:
|
#
|
||||||
i = tldp.inventory.Inventory(config.pubdir, config.sourcedir)
|
for doc in docs:
|
||||||
if stems:
|
stdout = kwargs.get('file', sys.stdout)
|
||||||
for source in i.source.values():
|
doc.detail(width, config.verbose, file=stdout)
|
||||||
if source.stem in stems:
|
return 0
|
||||||
targets.append(source)
|
|
||||||
else:
|
|
||||||
targets.extend(i.new.values())
|
def build(config, docs, inv):
|
||||||
targets.extend(i.stale.values())
|
if inv is None:
|
||||||
targets.extend(i.broken.values())
|
inv = tldp.inventory.Inventory(config.pubdir, config.sourcedir)
|
||||||
if len(targets) != len(args):
|
if not docs:
|
||||||
targets = [x.stem for x in targets]
|
docs = inv.work.values()
|
||||||
missing = args.difference(set(targets))
|
for source in docs:
|
||||||
logger.error("Could not find matching file or stem for args: %s",
|
|
||||||
', '.join(missing))
|
|
||||||
return 1
|
|
||||||
for source in targets:
|
|
||||||
if source.stem in config.skip:
|
if source.stem in config.skip:
|
||||||
logger.info("%s skipping build per request", source.stem)
|
logger.info("%s skipping build per request", source.stem)
|
||||||
continue
|
continue
|
||||||
|
@ -103,7 +77,7 @@ def build(config, args):
|
||||||
dirname = os.path.join(config.pubdir, source.stem)
|
dirname = os.path.join(config.pubdir, source.stem)
|
||||||
source.output = tldp.outputs.OutputDirectory(dirname)
|
source.output = tldp.outputs.OutputDirectory(dirname)
|
||||||
if not source.doctype:
|
if not source.doctype:
|
||||||
logger.warning("%s skipping document of unknown doctype",
|
logger.warning("%s skipping document of unknown doctype",
|
||||||
source.stem)
|
source.stem)
|
||||||
continue
|
continue
|
||||||
output = source.output
|
output = source.output
|
||||||
|
@ -112,46 +86,122 @@ def build(config, args):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def run():
|
def script(config, docs, inv):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def getDocumentNames(args):
|
||||||
|
sought = set()
|
||||||
|
for arg in args:
|
||||||
|
doc = arg_issourcedoc(arg)
|
||||||
|
if doc is not None:
|
||||||
|
sought.add(doc)
|
||||||
|
remainder = set(args).difference(sought)
|
||||||
|
return sought, remainder
|
||||||
|
|
||||||
|
|
||||||
|
def getStatusNames(args):
|
||||||
|
found = set()
|
||||||
|
sought = set()
|
||||||
|
for arg in args:
|
||||||
|
stati = status_classes.get(arg, None)
|
||||||
|
if stati:
|
||||||
|
sought.update(stati)
|
||||||
|
found.add(arg)
|
||||||
|
remainder = set(args).difference(found)
|
||||||
|
return sought, remainder
|
||||||
|
|
||||||
|
|
||||||
|
def getStemNames(config, stati, args, inv=None):
|
||||||
|
if inv is None:
|
||||||
|
inv = tldp.inventory.Inventory(config.pubdir, config.sourcedir)
|
||||||
|
sought = set()
|
||||||
|
for stem, doc in inv.all.items():
|
||||||
|
if stem in args:
|
||||||
|
sought.add(doc)
|
||||||
|
if doc.status in stati:
|
||||||
|
sought.add(doc)
|
||||||
|
soughtstems = [x.stem for x in sought]
|
||||||
|
remainder = set(args).difference(soughtstems)
|
||||||
|
return sought, remainder, inv
|
||||||
|
|
||||||
|
|
||||||
|
def run(argv):
|
||||||
# -- may want to see option parsing, so set --loglevel as
|
# -- may want to see option parsing, so set --loglevel as
|
||||||
# soon as possible
|
# soon as possible
|
||||||
if '--loglevel' in sys.argv:
|
if '--loglevel' in argv:
|
||||||
levelarg = 1 + sys.argv.index('--loglevel')
|
levelarg = 1 + argv.index('--loglevel')
|
||||||
level = arg_isloglevel(sys.argv[levelarg])
|
level = arg_isloglevel(argv[levelarg])
|
||||||
# -- set the root logger's level
|
# -- set the root logger's level
|
||||||
logging.getLogger().setLevel(level)
|
logging.getLogger().setLevel(level)
|
||||||
|
|
||||||
# -- produce a configuration from CLI, ENV and CFG
|
# -- produce a configuration from CLI, ENV and CFG
|
||||||
#
|
#
|
||||||
tag = 'ldptool'
|
tag = 'ldptool'
|
||||||
argv = sys.argv[1:]
|
|
||||||
config, args = tldp.config.collectconfiguration(tag, argv)
|
config, args = tldp.config.collectconfiguration(tag, argv)
|
||||||
|
|
||||||
# -- check to see if the user wishes to --list things
|
# -- summary does not require any args
|
||||||
# this function and friends is called 'detail', because
|
if config.summary:
|
||||||
# Python reserves a special (fundamental) meaning for the word
|
|
||||||
# list; but for the end-user they are synonyms
|
|
||||||
#
|
|
||||||
if config.detail:
|
|
||||||
sys.exit(detail(config, args))
|
|
||||||
|
|
||||||
# -- check to see if the user wants --status output
|
if args:
|
||||||
#
|
return "Unknown args received for --summary: " + ' '.join(args)
|
||||||
if config.status:
|
if not config.pubdir:
|
||||||
if config.pubdir is None:
|
return "Option --pubdir (and --sourcedir) required for --summary."
|
||||||
sys.exit("Option --pubdir required for --status.")
|
|
||||||
if not config.sourcedir:
|
if not config.sourcedir:
|
||||||
sys.exit("Option --sourcedir required for --status.")
|
return "Option --sourcedir (and --pubdir) required for --summary."
|
||||||
sys.exit(status(config, args))
|
|
||||||
|
|
||||||
# -- our primary action is to try to build
|
return summary(config)
|
||||||
if config.build is None:
|
|
||||||
config.all = True
|
|
||||||
sys.exit(build(config, args))
|
|
||||||
|
|
||||||
|
# -- args can be a mix of full paths to documents (file or directory)
|
||||||
|
# stem names (for operating on inventory) and status_type names
|
||||||
|
#
|
||||||
|
# -- sort them out into each of the different types
|
||||||
|
#
|
||||||
|
docs = list()
|
||||||
|
inv = None
|
||||||
|
if args:
|
||||||
|
rawdocs, remainder = getDocumentNames(args)
|
||||||
|
logger.debug("args included %d documents in filesystem: %r",
|
||||||
|
len(rawdocs), rawdocs)
|
||||||
|
if rawdocs:
|
||||||
|
for doc in rawdocs:
|
||||||
|
docs.append(tldp.sources.SourceDocument(doc))
|
||||||
|
|
||||||
|
if remainder:
|
||||||
|
stati, remainder = getStatusNames(remainder)
|
||||||
|
logger.debug("args included %d status type args: %r",
|
||||||
|
len(stati), stati)
|
||||||
|
|
||||||
|
if remainder or stati:
|
||||||
|
logger.debug("Checking inventory (%d stems, %d status_classes).",
|
||||||
|
len(remainder), len(stati))
|
||||||
|
if not config.pubdir:
|
||||||
|
return " --pubdir (and --sourcedir) required for inventory."
|
||||||
|
if not config.sourcedir:
|
||||||
|
return " --sourcedir (and --pubdir) required for inventory."
|
||||||
|
|
||||||
|
stems, remainder, inv = getStemNames(config, stati, remainder)
|
||||||
|
if stems:
|
||||||
|
for doc in stems:
|
||||||
|
docs.append(doc)
|
||||||
|
|
||||||
|
if remainder:
|
||||||
|
return "Unknown argument (not stem, file nor status_class): " \
|
||||||
|
+ ' '.join(remainder)
|
||||||
|
|
||||||
|
if config.detail:
|
||||||
|
return detail(config, docs, inv)
|
||||||
|
|
||||||
|
if config.script:
|
||||||
|
return script(config, docs, inv)
|
||||||
|
|
||||||
|
if not config.build:
|
||||||
|
logger.info("Assuming --build, since no other action was specified...")
|
||||||
|
|
||||||
|
return build(config, docs, inv)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
run()
|
sys.exit(run(sys.argv[1:]))
|
||||||
|
|
||||||
#
|
#
|
||||||
# -- end of file
|
# -- end of file
|
||||||
|
|
Loading…
Reference in New Issue