Example #1
0
 def test_load_save(self):
     """Verify text formatting is preserved."""
     item = Item(self.ITEM)
     item.load()
     item.save()
     text = common.read_text(self.ITEM)
     self.assertEqual(self.backup, text)
Example #2
0
 def _iter(self, reload=False):
     """Yield the document's items."""
     if self._itered and not reload:
         msg = "iterating document {}'s loaded items...".format(self)
         log.debug(msg)
         yield from list(self._items)
         return
     log.info("loading document {}'s items...".format(self))
     # Reload the document's item
     self._items = []
     for dirpath, dirnames, filenames in os.walk(self.path):
         for dirname in list(dirnames):
             path = os.path.join(dirpath, dirname, Document.CONFIG)
             if os.path.exists(path):
                 path = os.path.dirname(path)
                 dirnames.remove(dirname)
                 log.trace("skipped embedded document: {}".format(path))
         for filename in filenames:
             path = os.path.join(dirpath, filename)
             try:
                 item = Item(path, root=self.root,
                             document=self, tree=self.tree)
             except DoorstopError:
                 pass  # skip non-item files
             else:
                 self._items.append(item)
                 if reload:
                     item.load(reload=reload)
                 if settings.CACHE_ITEMS and self.tree:
                     self.tree._item_cache[item.uid] = item  # pylint: disable=W0212
                     log.trace("cached item: {}".format(item))
     # Set meta attributes
     self._itered = True
     # Yield items
     yield from list(self._items)
Example #3
0
 def test_load_save(self):
     """Verify text formatting is preserved."""
     item = Item(self.ITEM)
     item.load()
     item.save()
     with open(self.ITEM, 'r') as infile:
         text = infile.read()
         self.assertEqual(self.backup, text)
Example #4
0
    def add_item(self, number=None, level=None, reorder=True):
        """Create a new item for the document and return it.

        :param number: desired item number
        :param level: desired item level
        :param reorder: update levels of document items

        :return: added :class:`~doorstop.core.item.Item`

        """
        number = max(number or 0, self.next_number)
        log.debug("next number: {}".format(number))
        try:
            last = self.items[-1]
        except IndexError:
            next_level = level
        else:
            if level:
                next_level = level
            elif last.level.heading:
                next_level = last.level >> 1
                next_level.heading = False
            else:
                next_level = last.level + 1
        log.debug("next level: {}".format(next_level))
        uid = UID(self.prefix, self.sep, number, self.digits)
        item = Item.new(self.tree, self,
                        self.path, self.root, uid,
                        level=next_level)
        if level and reorder:
            self.reorder(keep=item)
        return item
Example #5
0
def add_item(prefix, uid, attrs=None, document=None, request_next_number=None):
    """Create a Doorstop document from existing document information.

    :param prefix: previously imported document's prefix
    :param uid: existing item's UID
    :param attrs: dictionary of Doorstop and custom attributes
    :param document: explicit document to add the item
    :param request_next_number: server method to get a document's next number

    :return: imported Item

    """
    if document:
        # Get an explicit tree
        tree = document.tree
        assert tree  # tree should be set internally
    else:
        # Get an implicit tree and document
        tree = _get_tree(request_next_number=request_next_number)
        document = tree.find_document(prefix)

    # Add an item using the specified UID
    log.info("importing item '{}'...".format(uid))
    item = Item.new(tree,
                    document,
                    document.path,
                    document.root,
                    uid,
                    auto=False)
    for key, value in (attrs or {}).items():
        item.set(key, value)
    item.save()

    log.info("imported: {}".format(item))
    return item
Example #6
0
    def add_item(self, number=None, level=None, reorder=True):
        """Create a new item for the document and return it.

        :param number: desired item number
        :param level: desired item level
        :param reorder: update levels of document items

        :return: added :class:`~doorstop.core.item.Item`

        """
        number = max(number or 0, self.next_number)
        log.debug("next number: {}".format(number))
        try:
            last = self.items[-1]
        except IndexError:
            next_level = level
        else:
            if level:
                next_level = level
            elif last.level.heading:
                next_level = last.level >> 1
                next_level.heading = False
            else:
                next_level = last.level + 1
        log.debug("next level: {}".format(next_level))
        uid = UID(self.prefix, self.sep, number, self.digits)
        item = Item.new(self.tree, self,
                        self.path, self.root, uid,
                        level=next_level)
        if level and reorder:
            self.reorder(keep=item)
        return item
Example #7
0
    def test_tree_skips_subdocuments_when_skipall_file_present(self):
        """Verify items can be found using a convenience function."""

        temp = tempfile.mkdtemp()
        cwd = temp
        root = temp

        # Step 1: Create a new tree with one item in a document in a subfolder.
        tree = build(cwd, root)
        tree.create_document(cwd, 'TST')

        subfolder = os.path.join(temp, 'SUBFOLDER')
        os.makedirs(os.path.join(temp, subfolder))

        sub_document = tree.create_document(subfolder, 'TST_SUB', parent='TST')

        item = Item.new(tree, sub_document, subfolder, cwd, "TST_SUB-001")
        item.save()

        # Step 2: Put a .doorstop.skip-all into the subfolder.
        path = os.path.join(subfolder, '.doorstop.skip-all')
        open(path, 'a').close()

        # Verify that building tree ignores subfolder's document.
        same_tree_again = build(cwd, root)
        self.assertEqual(1, len(same_tree_again.documents))
Example #8
0
    def test_tree_finds_subdocuments(self):
        """Verify items can be found using a convenience function."""

        temp = tempfile.mkdtemp()
        cwd = temp
        root = temp

        # Step 1: Create a new tree with one item in a document in a subfolder.
        tree = build(cwd, root)
        document = tree.create_document(cwd, 'TST')

        subfolder = os.path.join(temp, 'SUBFOLDER')
        os.makedirs(os.path.join(temp, subfolder))

        sub_document = tree.create_document(subfolder, 'TST_SUB', parent='TST')

        item = Item.new(tree, sub_document, subfolder, cwd, "TST_SUB-001")
        item.save()

        # Step 2: Read existing tree
        same_tree_again = build(cwd, root)

        # Verify that the tree has:
        # - both root-level and subfolder documents
        # - item in a subdocument
        self.assertEqual(2, len(same_tree_again.documents))
        sub_document = same_tree_again.documents[1]
        self.assertIsNotNone(document)
        self.assertEqual(1, len(sub_document.items))
        item = sub_document.items[0]
        self.assertEqual('TST_SUB-001', item.uid)
Example #9
0
def add_item(prefix, uid, attrs=None, document=None, request_next_number=None):
    """Create a Doorstop document from existing document information.

    :param prefix: previously imported document's prefix
    :param uid: existing item's UID
    :param attrs: dictionary of Doorstop and custom attributes
    :param document: explicit document to add the item
    :param request_next_number: server method to get a document's next number

    :return: imported Item

    """
    if document:
        # Get an explicit tree
        tree = document.tree
        assert tree  # tree should be set internally
    else:
        # Get an implicit tree and document
        tree = _get_tree(request_next_number=request_next_number)
        document = tree.find_document(prefix)

    # Add an item using the specified UID
    log.info("importing item '{}'...".format(uid))
    item = Item.new(tree, document,
                    document.path, document.root, uid,
                    auto=False)
    for key, value in (attrs or {}).items():
        item.set(key, value)
    item.save()

    log.info("imported: {}".format(item))
    return item
Example #10
0
 def test_attributes_with_spec(self, mock_warning):
     """Verify all other `Item` attributes raise an exception."""
     spec = Item(None, os.path.join(FILES, 'REQ001.yml'))
     self.item = UnknownItem(self.item.uid, spec=spec)
     self.assertRaises(AttributeError, getattr, self.item, 'path')
     self.assertRaises(AttributeError, getattr, self.item, 'text')
     self.assertRaises(AttributeError, getattr, self.item, 'delete')
     self.assertRaises(AttributeError, getattr, self.item, 'not_on_item')
     self.assertEqual(3, mock_warning.call_count)
Example #11
0
 def _iter(self, reload=False):
     """Yield the document's items."""
     if self._itered and not reload:
         msg = "iterating document {}'s loaded items...".format(self)
         log.debug(msg)
         yield from list(self._items)
         return
     log.info("loading document {}'s items...".format(self))
     # Reload the document's item
     self._items = []
     for dirpath, dirnames, filenames in os.walk(self.path):
         for dirname in list(dirnames):
             path = os.path.join(dirpath, dirname, Document.CONFIG)
             if os.path.exists(path):
                 path = os.path.dirname(path)
                 dirnames.remove(dirname)
                 log.trace("skipped embedded document: {}".format(path))
         for filename in filenames:
             path = os.path.join(dirpath, filename)
             try:
                 item = Item(path,
                             root=self.root,
                             document=self,
                             tree=self.tree)
             except DoorstopError:
                 pass  # skip non-item files
             else:
                 self._items.append(item)
                 if reload:
                     try:
                         item.load(reload=reload)
                     except Exception:
                         log.error("Unable to load: %s", item)
                         raise
                 if settings.CACHE_ITEMS and self.tree:
                     self.tree._item_cache[item.uid] = item  # pylint: disable=W0212
                     log.trace("cached item: {}".format(item))
     # Set meta attributes
     self._itered = True
     # Yield items
     yield from list(self._items)
Example #12
0
 def test_load_save(self):
     """Verify text formatting is preserved."""
     item = Item(self.ITEM)
     item.load()
     item.save()
     text = common.read_text(self.ITEM)
     self.assertEqual(self.backup, text)
Example #13
0
 def add(self):
     """Create a new item for the document and return it."""
     number = self.next
     logging.debug("next number: {}".format(number))
     try:
         last = self.items[-1]
     except IndexError:
         level = None
     else:
         level = last.level[:-1] + (last.level[-1] + 1,)
     logging.debug("next level: {}".format(level))
     item = Item.new(self.path, self.root,
                     self.prefix, self.sep, self.digits,
                     number, level=level)
     self._items.append(item)
     return item
Example #14
0
    def add_item(self, number=None, level=None, reorder=True, defaults=None):
        """Create a new item for the document and return it.

        :param number: desired item number
        :param level: desired item level
        :param reorder: update levels of document items

        :return: added :class:`~doorstop.core.item.Item`

        """
        number = max(number or 0, self.next_number)
        log.debug("next number: {}".format(number))
        try:
            last = self.items[-1]
        except IndexError:
            next_level = level
        else:
            if level:
                next_level = level
            elif last.level.heading:
                next_level = last.level >> 1
                next_level.heading = False
            else:
                next_level = last.level + 1
        log.debug("next level: {}".format(next_level))

        # Load more defaults before the item is created to avoid partially
        # constructed items in case the loading fails.
        more_defaults = self._load_with_include(defaults) if defaults else None

        uid = UID(self.prefix, self.sep, number, self.digits)
        item = Item.new(self.tree, self, self.path, self.root, uid, level=next_level)
        if self._attribute_defaults:
            item.set_attributes(self._attribute_defaults)
        if more_defaults:
            item.set_attributes(more_defaults)
        if level and reorder:
            self.reorder(keep=item)
        return item
Example #15
0
    def test_tree_finds_documents(self):
        """Verify items can be found using a convenience function."""

        temp = tempfile.mkdtemp()
        cwd = temp
        root = temp

        # Step 1: Create a new tree with one item.
        tree = build(cwd, root)
        document = tree.create_document(cwd, 'TST')
        item = Item.new(tree, document, cwd, cwd, "TST-001")
        item.save()

        # Step 2: Find a newly created tree
        same_tree_again = build(cwd, root)

        # Verify that that the tree, document and its item can be found.
        self.assertEqual(1, len(same_tree_again.documents))
        document = same_tree_again.document
        self.assertIsNotNone(document)
        self.assertEqual(1, len(document.items))
        item = document.items[0]
        self.assertEqual('TST-001', item.uid)
Example #16
0
    def test_tree_does_not_find_documents_when_skipall_file_present(self):
        """Verify items can be found using a convenience function."""

        temp = tempfile.mkdtemp()
        cwd = temp
        root = temp

        # Step 1: Create a new tree with one item.
        tree = build(cwd, root)
        document = tree.create_document(cwd, 'TST')
        item = Item.new(tree, document, cwd, cwd, "TST-001")
        item.save()

        # Step 2: Put a .doorstop.skip-all to the root of the tree.
        path = os.path.join(temp, '.doorstop.skip-all')
        open(path, 'a').close()

        # Step 3: Find a newly created tree
        same_tree_again = build(cwd, root)

        # Verify that the tree does not have a document because it was ignored.
        document = same_tree_again.document
        self.assertIsNone(document)
Example #17
0
    def add_item(self,
                 number=None,
                 level=None,
                 reorder=True,
                 defaults=None,
                 name=None):
        """Create a new item for the document and return it.

        :param number: desired item number
        :param level: desired item level
        :param reorder: update levels of document items

        :return: added :class:`~doorstop.core.item.Item`

        """
        uid = None
        if name is None:
            number = max(number or 0, self.next_number)
            log.debug("next number: {}".format(number))
            uid = UID(self.prefix, self.sep, number, self.digits)
        else:
            try:
                uid = UID(self.prefix, self.sep, int(name), self.digits)
            except ValueError:
                if not self.sep:
                    msg = "cannot add item with name '{}' to document '{}' without a separator".format(
                        name, self.prefix)
                    raise DoorstopError(msg)
                if self.sep not in settings.SEP_CHARS:
                    msg = "cannot add item with name '{}' to document '{}' with an invalid separator '{}'".format(
                        name, self.prefix, self.sep)
                    raise DoorstopError(msg)
                uid = UID(self.prefix, self.sep, name)
                if uid.prefix != self.prefix or uid.name != name:
                    msg = "invalid item name '{}'".format(name)
                    raise DoorstopError(msg)

        try:
            last = self.items[-1]
        except IndexError:
            next_level = level
        else:
            if level:
                next_level = level
            elif last.level.heading:
                next_level = last.level >> 1
                next_level.heading = False
            else:
                next_level = last.level + 1
        log.debug("next level: {}".format(next_level))

        # Load more defaults before the item is created to avoid partially
        # constructed items in case the loading fails.
        more_defaults = self._load_with_include(defaults) if defaults else None

        item = Item.new(self.tree,
                        self,
                        self.path,
                        self.root,
                        uid,
                        level=next_level)
        if self._attribute_defaults:
            item.set_attributes(self._attribute_defaults)
        if more_defaults:
            item.set_attributes(more_defaults)
        if level and reorder:
            self.reorder(keep=item)
        return item