def run_reorder(args, cwd, error, catch=True, _tree=None): """Process arguments and run the `doorstop reorder` subcommand. :param args: Namespace of CLI arguments :param cwd: current working directory :param error: function to call for CLI errors :param catch: catch and log :class:`~doorstop.common.DoorstopError` """ reordered = False with utilities.capture(catch=catch) as success: # get the document tree = _tree or _get_tree(args, cwd) document = tree.find_document(args.prefix) if not success: return False with utilities.capture(catch=catch) as success: # automatically order if args.auto: msg = "reordering document {}...".format(document) utilities.show(msg, flush=True) document.reorder(manual=False) reordered = True # or, reorder from a previously updated index elif document.index: relpath = os.path.relpath(document.index, cwd) if utilities.ask("reorder from '{}'?".format(relpath)): msg = "reordering document {}...".format(document) utilities.show(msg, flush=True) document.reorder(automatic=not args.manual) reordered = True else: del document.index # or, create a new index to update else: document.index = True # create index relpath = os.path.relpath(document.index, cwd) editor.edit(relpath, tool=args.tool) get('reorder')(args, cwd, error, catch=False, _tree=tree) if not success: msg = "after fixing the error: doorstop reorder {}".format(document) utilities.show(msg) return False if reordered: utilities.show("reordered document: {}".format(document)) return True
def run_clear(args, cwd, error, catch=True): """Process arguments and run the `doorstop clear` subcommand. :param args: Namespace of CLI arguments :param cwd: current working directory :param error: function to call for CLI errors :param catch: catch and log :class:`~doorstop.common.DoorstopError` """ with utilities.capture(catch=catch) as success: tree = _get_tree(args, cwd) if args.parents: # Check that the parent item UIDs exist for pid in args.parents: tree.find_item(pid) pids = " to " + ", ".join(args.parents) else: pids = "" for item in _iter_items(args, tree, error): msg = "clearing item {}'s suspect links{}...".format( item.uid, pids) utilities.show(msg) item.clear(parents=args.parents) if not success: return False return True
def run_add(args, cwd, _, catch=True): """Process arguments and run the `doorstop add` subcommand. :param args: Namespace of CLI arguments :param cwd: current working directory :param error: function to call for CLI errors :param catch: catch and log :class:`~doorstop.common.DoorstopError` """ with utilities.capture(catch=catch) as success: # get the document request_next_number = _request_next_number(args) tree = _get_tree(args, cwd, request_next_number=request_next_number) document = tree.find_document(args.prefix) # add items to it for _ in range(args.count): item = document.add_item(level=args.level) utilities.show("added item: {} ({})".format( item.uid, item.relpath)) if not success: return False return True
def run(args, cwd, error, catch=True): # pylint: disable=W0613 """Process arguments and run the `doorstop` subcommand. :param args: Namespace of CLI arguments :param cwd: current working directory :param error: function to call for CLI errors :param catch: catch and log :class:`~doorstop.common.DoorstopError` """ with utilities.capture(catch=catch) as success: # get the tree tree = _get_tree(args, cwd, load=True) # validate it utilities.show("validating items...", flush=True) valid = tree.validate(skip=args.skip) if not success: return False if len(tree) > 1 and valid: utilities.show('\n' + tree.draw() + '\n') return valid
def run_add(args, cwd, _, catch=True): """Process arguments and run the `doorstop add` subcommand. :param args: Namespace of CLI arguments :param cwd: current working directory :param error: function to call for CLI errors :param catch: catch and log :class:`~doorstop.common.DoorstopError` """ with utilities.capture(catch=catch) as success: tree = build(cwd=cwd, root=args.project) document = tree.find_document(args.prefix) if args.force: log.warn("creating items without the server...") else: server.check() for _ in range(args.count): number = 0 while number is not None and number < document.next: if number: log.warn("server is behind, requesting next number...") if args.force: number = None else: number = server.get_next_number(args.prefix) item = document.add_item(number=number, level=args.level) show("added item: {} ({})".format(item.uid, item.relpath)) if not success: return False return True
def run_delete(args, cwd, _, catch=True): """Process arguments and run the `doorstop delete` subcommand. :param args: Namespace of CLI arguments :param cwd: current working directory :param error: function to call for CLI errors :param catch: catch and log :class:`~doorstop.common.DoorstopError` """ with utilities.capture(catch=catch) as success: # get the document tree = _get_tree(args, cwd) document = tree.find_document(args.prefix) # delete it prefix, relpath = document.prefix, document.relpath document.delete() if not success: return False utilities.show("deleted document: {} ({})".format(prefix, relpath)) return True
def run_create(args, cwd, _, catch=True): """Process arguments and run the `doorstop create` subcommand. :param args: Namespace of CLI arguments :param cwd: current working directory :param error: function to call for CLI errors :param catch: catch and log :class:`~doorstop.common.DoorstopError` """ with utilities.capture(catch=catch) as success: # get the tree tree = _get_tree(args, cwd) # create a new document document = tree.create_document(args.path, args.prefix, parent=args.parent, digits=args.digits) if not success: return False utilities.show("created document: {} ({})".format(document.prefix, document.relpath)) return True
def run_add(args, cwd, _, catch=True): """Process arguments and run the `doorstop add` subcommand. :param args: Namespace of CLI arguments :param cwd: current working directory :param error: function to call for CLI errors :param catch: catch and log :class:`~doorstop.common.DoorstopError` """ with utilities.capture(catch=catch) as success: # get the document request_next_number = _request_next_number(args) tree = _get_tree(args, cwd, request_next_number=request_next_number) document = tree.find_document(args.prefix) # add items to it for _ in range(args.count): item = document.add_item(level=args.level) utilities.show("added item: {} ({})".format(item.uid, item.relpath)) if not success: return False return True
def run(args, cwd, error, catch=True): # pylint: disable=W0613 """Process arguments and run the `doorstop` subcommand. :param args: Namespace of CLI arguments :param cwd: current working directory :param error: function to call for CLI errors :param catch: catch and log :class:`~doorstop.common.DoorstopError` """ with utilities.capture(catch=catch) as success: # get the tree tree = _get_tree(args, cwd, load=True) # validate it utilities.show("validating items...", flush=True) valid = tree.validate() if not success: return False if len(tree) > 1 and valid: utilities.show('\n' + tree.draw() + '\n') return valid
def run_unlink(args, cwd, _, catch=True): """Process arguments and run the `doorstop unlink` subcommand. :param args: Namespace of CLI arguments :param cwd: current working directory :param error: function to call for CLI errors :param catch: catch and log :class:`~doorstop.common.DoorstopError` """ with utilities.capture(catch=catch) as success: # get the tree tree = _get_tree(args, cwd) # unlink items child, parent = tree.unlink_items(args.child, args.parent) if not success: return False msg = "unlinked items: {} ({}) -> {} ({})".format(child.uid, child.relpath, parent.uid, parent.relpath) utilities.show(msg) return True
def run_edit(args, cwd, error, catch=True): """Process arguments and run the `doorstop edit` subcommand. :param args: Namespace of CLI arguments :param cwd: current working directory :param error: function to call for CLI errors :param catch: catch and log :class:`~doorstop.common.DoorstopError` """ item = document = None ext = utilities.get_ext(args, '.yml', '.yml', whole_tree=False, error=error) with utilities.capture(catch=catch) as success: tree = build(cwd=cwd, root=args.project) # find item or document if not args.document: try: item = tree.find_item(args.label) except common.DoorstopError as exc: if args.item: raise exc from None if not item: document = tree.find_document(args.label) # edit item or document if item: item.edit(tool=args.tool) else: _export_import(args, cwd, error, document, ext) if not success: return False if item: show("opened item: {} ({})".format(item.uid, item.relpath)) return True
def run_import(args, cwd, error, catch=True, _tree=None): """Process arguments and run the `doorstop import` subcommand. :param args: Namespace of CLI arguments :param cwd: current working directory :param error: function to call for CLI errors :param catch: catch and log :class:`~doorstop.common.DoorstopError` """ document = item = None attrs = utilities.literal_eval(args.attrs, error) mapping = utilities.literal_eval(args.map, error) if args.path: if not args.prefix: error("when [path] specified, [prefix] is also required") elif args.document: error("'--document' cannot be used with [path] [prefix]") elif args.item: error("'--item' cannot be used with [path] [prefix]") ext = utilities.get_ext(args, error, None, None) elif not (args.document or args.item): error("specify [path], '--document', or '--item' to import") with utilities.capture(catch=catch) as success: if args.path: # get the document request_next_number = _request_next_number(args) tree = _tree or _get_tree(args, cwd, request_next_number=request_next_number) document = tree.find_document(args.prefix) # import items into it msg = "importing '{}' into document {}...".format(args.path, document) utilities.show(msg, flush=True) importer.import_file(args.path, document, ext, mapping=mapping) elif args.document: prefix, path = args.document document = importer.create_document(prefix, path, parent=args.parent) elif args.item: prefix, uid = args.item request_next_number = _request_next_number(args) item = importer.add_item(prefix, uid, attrs=attrs, request_next_number=request_next_number) if not success: return False if document: utilities.show("imported document: {} ({})".format(document.prefix, document.relpath)) else: assert item utilities.show("imported item: {} ({})".format(item.uid, item.relpath)) return True
def run_publish(args, cwd, error, catch=True): """Process arguments and run the `doorstop publish` subcommand. :param args: Namespace of CLI arguments :param cwd: current working directory :param error: function to call for CLI errors :param catch: catch and log :class:`~doorstop.common.DoorstopError` """ whole_tree = args.prefix == 'all' ext = utilities.get_ext(args, error, '.txt', '.html', whole_tree) # Get the tree or document with utilities.capture(catch=catch) as success: publisher.check(ext) tree = _get_tree(args, cwd, load=whole_tree) if not whole_tree: document = tree.find_document(args.prefix) if not success: return False # Set publishing arguments kwargs = {} if args.width: kwargs['width'] = args.width # Write to output file(s) if args.path: path = os.path.abspath(os.path.join(cwd, args.path)) if whole_tree: msg = "publishing tree to '{}'...".format(path) utilities.show(msg, flush=True) published_path = publisher.publish(tree, path, ext, template=args.template, **kwargs) else: msg = "publishing document {} to '{}'...".format(document, path) utilities.show(msg, flush=True) published_path = publisher.publish(document, path, ext, template=args.template, **kwargs) if published_path: utilities.show("published: {}".format(published_path)) # Or, display to standard output else: if whole_tree: error("only single documents can be displayed") for line in publisher.publish_lines(document, ext, **kwargs): utilities.show(line) return True
def test_failure_uncaught(self): """Verify a failure can be left uncaught.""" try: with utilities.capture(catch=False) as success: raise common.DoorstopError except common.DoorstopError: self.assertFalse(success) else: self.fail("DoorstopError not raised")
def run_publish(args, cwd, error, catch=True): """Process arguments and run the `doorstop publish` subcommand. :param args: Namespace of CLI arguments :param cwd: current working directory :param error: function to call for CLI errors :param catch: catch and log :class:`~doorstop.common.DoorstopError` """ # Parse arguments whole_tree = args.prefix == 'all' ext = utilities.get_ext(args, '.txt', '.html', whole_tree, error) # Publish documents with utilities.capture(catch=catch) as success: publisher.check(ext) tree = build(cwd=cwd, root=args.project) if not whole_tree: document = tree.find_document(args.prefix) if not success: return False # Set publishing arguments kwargs = {} if args.width: kwargs['width'] = args.width # Write to output file(s) if args.path: if whole_tree: show("publishing tree to '{}'...".format(args.path), flush=True) path = publisher.publish(tree, args.path, ext, **kwargs) else: msg = "publishing document {} to '{}'...".format(document, args.path) show(msg, flush=True) path = publisher.publish(document, args.path, ext, **kwargs) if path: show("published: {}".format(path)) # Display to standard output else: if whole_tree: error("only single documents can be displayed") for line in publisher.publish_lines(document, ext, **kwargs): show(line) return True
def run_export(args, cwd, error, catch=True, auto=False, _tree=None): """Process arguments and run the `doorstop export` subcommand. :param args: Namespace of CLI arguments :param cwd: current working directory :param error: function to call for CLI errors :param catch: catch and log :class:`~doorstop.common.DoorstopError` :param auto: include placeholders for new items on import """ whole_tree = args.prefix == 'all' ext = utilities.get_ext(args, error, '.yml', '.csv', whole_tree=whole_tree) # Get the tree or document with utilities.capture(catch=catch) as success: exporter.check(ext) tree = _tree or _get_tree(args, cwd, load=whole_tree) if not whole_tree: document = tree.find_document(args.prefix) if not success: return False # Write to output file(s) if args.path: if whole_tree: msg = "exporting tree to '{}'...".format(args.path) utilities.show(msg, flush=True) path = exporter.export(tree, args.path, ext, auto=auto) else: msg = "exporting document {} to '{}'...".format(document, args.path) utilities.show(msg, flush=True) path = exporter.export(document, args.path, ext, auto=auto) if path: utilities.show("exported: {}".format(path)) # Or, display to standard output else: if whole_tree: error("only single documents can be displayed") for line in exporter.export_lines(document, ext): utilities.show(line) return True
def run_export(args, cwd, error, catch=True, auto=False, _tree=None): """Process arguments and run the `doorstop export` subcommand. :param args: Namespace of CLI arguments :param cwd: current working directory :param error: function to call for CLI errors :param catch: catch and log :class:`~doorstop.common.DoorstopError` :param auto: include placeholders for new items on import """ whole_tree = args.prefix == 'all' ext = utilities.get_ext(args, error, '.yml', '.csv', whole_tree=whole_tree) # Get the tree or document with utilities.capture(catch=catch) as success: exporter.check(ext) tree = _tree or _get_tree(args, cwd, load=whole_tree) if not whole_tree: document = tree.find_document(args.prefix) if not success: return False # Write to output file(s) if args.path: if whole_tree: msg = "exporting tree to '{}'...".format(args.path) utilities.show(msg, flush=True) path = exporter.export(tree, args.path, ext, auto=auto) else: msg = "exporting document {} to '{}'...".format( document, args.path) utilities.show(msg, flush=True) path = exporter.export(document, args.path, ext, auto=auto) if path: utilities.show("exported: {}".format(path)) # Or, display to standard output else: if whole_tree: error("only single documents can be displayed") for line in exporter.export_lines(document, ext): utilities.show(line) return True
def run_review(args, cwd, error, catch=True): """Process arguments and run the `doorstop review` subcommand. :param args: Namespace of CLI arguments :param cwd: current working directory :param error: function to call for CLI errors :param catch: catch and log :class:`~doorstop.common.DoorstopError` """ with utilities.capture(catch=catch) as success: for item in _iter_items(args, cwd, error): show("marking item {} as reviewed...".format(item.uid)) item.review() if not success: return False return True
def run_create(args, cwd, _, catch=True): """Process arguments and run the `doorstop create` subcommand. :param args: Namespace of CLI arguments :param cwd: current working directory :param error: function to call for CLI errors :param catch: catch and log :class:`~doorstop.common.DoorstopError` """ with utilities.capture(catch=catch) as success: tree = build(cwd=cwd, root=args.project) document = tree.create_document(args.path, args.prefix, parent=args.parent, digits=args.digits) if not success: return False show("created document: {} ({})".format(document.prefix, document.relpath)) return True
def run_link(args, cwd, _, catch=True): """Process arguments and run the `doorstop link` subcommand. :param args: Namespace of CLI arguments :param cwd: current working directory :param error: function to call for CLI errors :param catch: catch and log :class:`~doorstop.common.DoorstopError` """ with utilities.capture(catch=catch) as success: tree = build(cwd=cwd, root=args.project) child, parent = tree.link_items(args.child, args.parent) if not success: return False msg = "linked items: {} ({}) -> {} ({})" show(msg.format(child.uid, child.relpath, parent.uid, parent.relpath)) return True
def run_remove(args, cwd, _, catch=True): """Process arguments and run the `doorstop remove` subcommand. :param args: Namespace of CLI arguments :param cwd: current working directory :param error: function to call for CLI errors :param catch: catch and log :class:`~doorstop.common.DoorstopError` """ with utilities.capture(catch=catch) as success: tree = build(cwd=cwd, root=args.project) item = tree.find_item(args.uid) item.delete() if not success: return False show("removed item: {} ({})".format(item.uid, item.relpath)) return True
def run_review(args, cwd, error, catch=True): """Process arguments and run the `doorstop review` subcommand. :param args: Namespace of CLI arguments :param cwd: current working directory :param error: function to call for CLI errors :param catch: catch and log :class:`~doorstop.common.DoorstopError` """ with utilities.capture(catch=catch) as success: for item in _iter_items(args, cwd, error): utilities.show("marking item {} as reviewed...".format(item.uid)) item.review() if not success: return False return True
def run_clear(args, cwd, error, catch=True): """Process arguments and run the `doorstop clear` subcommand. :param args: Namespace of CLI arguments :param cwd: current working directory :param error: function to call for CLI errors :param catch: catch and log :class:`~doorstop.common.DoorstopError` """ with utilities.capture(catch=catch) as success: for item in _iter_items(args, cwd, error): msg = "clearing item {}'s suspect links...".format(item.uid) utilities.show(msg) item.clear() if not success: return False return True
def run_edit(args, cwd, error, catch=True): """Process arguments and run the `doorstop edit` subcommand. :param args: Namespace of CLI arguments :param cwd: current working directory :param error: function to call for CLI errors :param catch: catch and log :class:`~doorstop.common.DoorstopError` """ item = document = None ext = utilities.get_ext(args, error, '.yml', '.yml', whole_tree=False) with utilities.capture(catch=catch) as success: # get the item or document request_next_number = _request_next_number(args) tree = _get_tree(args, cwd, request_next_number=request_next_number) if not args.document: try: item = tree.find_item(args.label) except common.DoorstopError as exc: if args.item: raise exc from None # pylint: disable=raising-bad-type if not item: document = tree.find_document(args.label) # edit it if item: item.edit(tool=args.tool) else: _export_import(args, cwd, error, document, ext) if not success: return False if item: utilities.show("opened item: {} ({})".format(item.uid, item.relpath)) return True
def run_edit(args, cwd, error, catch=True): """Process arguments and run the `doorstop edit` subcommand. :param args: Namespace of CLI arguments :param cwd: current working directory :param error: function to call for CLI errors :param catch: catch and log :class:`~doorstop.common.DoorstopError` """ item = document = None ext = utilities.get_ext(args, error, '.yml', '.yml', whole_tree=False) with utilities.capture(catch=catch) as success: # get the item or document request_next_number = _request_next_number(args) tree = _get_tree(args, cwd, request_next_number=request_next_number) if not args.document: try: item = tree.find_item(args.label) except common.DoorstopError as exc: if args.item: raise exc from None if not item: document = tree.find_document(args.label) # edit it if item: item.edit(tool=args.tool) else: _export_import(args, cwd, error, document, ext) if not success: return False if item: utilities.show("opened item: {} ({})".format(item.uid, item.relpath)) return True
def run_remove(args, cwd, _, catch=True): """Process arguments and run the `doorstop remove` subcommand. :param args: Namespace of CLI arguments :param cwd: current working directory :param error: function to call for CLI errors :param catch: catch and log :class:`~doorstop.common.DoorstopError` """ with utilities.capture(catch=catch) as success: # get the item tree = _get_tree(args, cwd) item = tree.find_item(args.uid) # delete it item.delete() if not success: return False utilities.show("removed item: {} ({})".format(item.uid, item.relpath)) return True
def test_success(self): """Verify a success can be captured.""" with utilities.capture() as success: pass # no exception raised self.assertTrue(success)
def run_import(args, cwd, error, catch=True, _tree=None): """Process arguments and run the `doorstop import` subcommand. :param args: Namespace of CLI arguments :param cwd: current working directory :param error: function to call for CLI errors :param catch: catch and log :class:`~doorstop.common.DoorstopError` """ document = item = None attrs = utilities.literal_eval(args.attrs, error) mapping = utilities.literal_eval(args.map, error) if args.path: if not args.prefix: error("when [path] specified, [prefix] is also required") elif args.document: error("'--document' cannot be used with [path] [prefix]") elif args.item: error("'--item' cannot be used with [path] [prefix]") ext = utilities.get_ext(args, error, None, None) elif not (args.document or args.item): error("specify [path], '--document', or '--item' to import") with utilities.capture(catch=catch) as success: if args.path: # get the document request_next_number = _request_next_number(args) tree = _tree or _get_tree( args, cwd, request_next_number=request_next_number) document = tree.find_document(args.prefix) # import items into it msg = "importing '{}' into document {}...".format( args.path, document) utilities.show(msg, flush=True) importer.import_file(args.path, document, ext, mapping=mapping) elif args.document: prefix, path = args.document document = importer.create_document(prefix, path, parent=args.parent) elif args.item: prefix, uid = args.item request_next_number = _request_next_number(args) item = importer.add_item(prefix, uid, attrs=attrs, request_next_number=request_next_number) if not success: return False if document: utilities.show("imported document: {} ({})".format( document.prefix, document.relpath)) else: assert item utilities.show("imported item: {} ({})".format(item.uid, item.relpath)) return True
def test_failure(self): """Verify a failure can be captured.""" with utilities.capture() as success: raise common.DoorstopError self.assertFalse(success)