Example #1
0
    def get_merge(self):
        """
        Return a 3-way merged docstring, or None if no merge is necessary.
        Updates the merge status of the docstring.

        Returns
        -------
        result : {None, str}
            None if no merge is needed, else return the merge result.

        """
        if self.base_doc == self.source_doc:
            # Nothing to merge
            if self.merge_status != MERGE_NONE:
                self.merge_status = MERGE_NONE
                self.save()
            return None

        if self.revisions.count() == 0:
            # No local edits
            self.merge_status = MERGE_NONE
            self.base_doc = self.source_doc
            self.save()
            return None

        if self.text == self.source_doc:
            # Local text agrees with VCS source, no merge needed
            self.merge_status = MERGE_NONE
            self.base_doc = self.source_doc
            self.save()
            return None

        result, conflicts = merge_3way(
            strip_spurious_whitespace(self.text) + "\n",
            strip_spurious_whitespace(self.base_doc) + "\n",
            strip_spurious_whitespace(self.source_doc) + "\n",
        )
        result = strip_spurious_whitespace(result)
        if not conflicts:
            self.merge_status = MERGE_MERGE
        else:
            self.merge_status = MERGE_CONFLICT
        self.save()
        return result
Example #2
0
def _import_docstring_revisions_from_xml(stream):
    tree = etree.parse(stream)
    root = tree.getroot()
    for el in root:
        if el.tag not in ('module', 'class', 'callable', 'object'): continue

        try:
            doc = Docstring.on_site.get(name=el.attrib['id'])
        except Docstring.DoesNotExist:
            print "DOES-NOT-EXIST", el.attrib['id']
            continue

        if el.text:
            doc.edit(strip_spurious_whitespace(el.text.decode('string-escape')),
                     "xml-import",
                     comment="Imported")
Example #3
0
 def edit(self, new_text, author, comment):
     """Create a new revision of the page"""
     new_text = strip_spurious_whitespace(new_text)
     rev = WikiPageRevision(page=self, author=author, text=new_text, comment=comment)
     rev.save()
Example #4
0
    def edit(self, new_text, author, comment):
        """
        Create a new revision of the docstring with given content.

        Also resolves merge conflicts. Does not create a new revision
        if there are no changes in the text.

        Raises
        ------
        Docstring.MergeConflict
            If the new text still contains conflict markers.

        """
        new_text = strip_spurious_whitespace(new_text)

        if self.type_code == "dir":
            raise RuntimeError("'dir' docstrings cannot be edited")

        if "<<<<<<" in new_text or ">>>>>>" in new_text:
            raise RuntimeError("New text still contains merge conflict markers")

        # assume any merge was OK
        self.merge_status = MERGE_NONE
        self.base_doc = self.source_doc
        self.save()

        # Update dirtiness
        self.dirty = self.source_doc != new_text

        # Editing 'file' docstrings can resurrect them from obsoletion,
        # or hide them (ie. remove their connection to their parent 'dir')
        if self.type_code == "file" and new_text and self.is_obsolete:
            # make not obsolete
            self.timestamp = Docstring.get_current_timestamp()
            self.save()
            self._add_to_parent()
        elif self.type_code == "file" and not new_text:
            # hide
            self._remove_aliases()

        # Add a revision (if necessary)
        if new_text != self.text:
            new_review_code = {
                REVIEW_NEEDS_EDITING: REVIEW_BEING_WRITTEN,
                REVIEW_NEEDS_WORK: REVIEW_REVISED,
                REVIEW_NEEDS_PROOF: REVIEW_REVISED,
                REVIEW_PROOFED: REVIEW_REVISED,
            }.get(self.review, self.review)

            if self.revisions.count() == 0:
                # Store the VCS revision the initial edit was based on,
                # for making statistics later on.
                base_rev = DocstringRevision(
                    docstring=self,
                    text=self.source_doc,
                    author="Source",
                    comment="Initial source revision",
                    review_code=self.review,
                    ok_to_apply=False,
                )
                base_rev.timestamp = self.timestamp
                base_rev.save()

            rev = DocstringRevision(
                docstring=self,
                text=new_text,
                author=author,
                comment=comment,
                review_code=new_review_code,
                ok_to_apply=False,
            )
            rev.save()

        # Save
        self.save()

        # Update cross-reference and toctree caches
        LabelCache.cache_docstring(self)
        ToctreeCache.cache_docstring(self)
        self._update_title()
Example #5
0
def _update_docstrings_from_xml(site, stream):
    tree = etree.parse(stream)
    root = tree.getroot()

    timestamp = datetime.datetime.now()

    known_entries = {}
    for el in root:
        if el.tag not in ('module', 'class', 'callable', 'object', 'dir',
                          'file'):
            continue
        known_entries[el.attrib['id']] = True

    for el in root:
        if el.tag not in ('module', 'class', 'callable', 'object', 'dir',
                          'file'):
            continue

        bases = []
        for b in el.findall('base'):
            bases.append(b.attrib['ref'])
        bases = " ".join(bases)
        if not bases:
            bases = None

        if el.text:
            docstring = strip_spurious_whitespace(el.text.decode('string-escape'))
        else:
            docstring = u""

        if not isinstance(docstring, unicode):
            try:
                docstring = docstring.decode('utf-8')
            except UnicodeError:
                docstring = docstring.decode('iso-8859-1')

        try:
            line = int(el.get('line'))
        except (ValueError, TypeError):
            line = None

        doc, created = Docstring.on_site.get_or_create(name=el.attrib['id'],
                                                       site=site)
        doc.type_code = el.tag
        doc.type_name = el.get('type')
        doc.argspec = el.get('argspec')
        doc.objclass = el.get('objclass')
        doc.bases = bases
        doc.file_name = el.get('file')
        doc.line_number = line
        doc.timestamp = timestamp

        if created:
            # New docstring
            doc.merge_status = MERGE_NONE
            doc.base_doc = doc.source_doc
            doc.source_doc = docstring
            doc.dirty = False
        elif docstring != doc.base_doc:
            # Source has changed, try to merge from base
            doc.source_doc = docstring
            doc.save()
            doc.get_merge() # update merge status

        doc.dirty = (doc.source_doc != doc.text)
        doc.contents.all().delete()
        doc.save()

        # -- Contents

        for ref in el.findall('ref'):
            alias = DocstringAlias()
            alias.target = ref.attrib['ref']
            alias.parent = doc
            alias.alias = ref.attrib['name']
            alias.save()

    # -- Handle obsoletion of 'file' pages missing in SVN

    for doc in Docstring.on_site.filter(type_code='file',
                                        timestamp__lt=timestamp).all():
        doc.source_doc = ""
        if doc.text != doc.base_doc and doc.text != "":
            # Non-empty docstrings won't become obsolete, but may cause
            # a merge conflict
            doc.timestamp = timestamp
            doc.dirty = True
            doc.save()
            if doc.base_doc != doc.source_doc:
                doc.get_merge()

    # -- Handle obsoletion of 'dir' pages missing in SVN

    for doc in Docstring.on_site.filter(type_code='dir',
                                        timestamp__lt=timestamp).all():
        
        # Only 'dir' pages with no remaining children become obsolete
        children = Docstring.get_non_obsolete().filter(
            name__startswith=doc.name + '/').all()
        if not children:
            continue
        else:
            # For others, insert deduced children
            doc.timestamp = timestamp
            for child in children:
                alias = DocstringAlias()
                alias.target = child.name
                alias.parent = doc
                alias.alias = child.name.split('/')[-1]
                alias.save()
            doc.save()

    # -- Update label cache

    LabelCache.clear(site=site)
    
    from django.db import connection, transaction
    cursor = connection.cursor()

    # -- Insert docstring names at once using raw SQL (fast!)

    # direct names
    cursor.execute("""
    INSERT INTO docweb_labelcache (label, target, title, site_id)
    SELECT d.name, d.name, d.name, %s
    FROM docweb_docstring AS d
    WHERE d.site_id = %s AND d.timestamp = %s
    """, [site.id, site.id, timestamp])

    # 1st dereference level (normal docstrings)
    cursor.execute(port_sql("""
    INSERT INTO docweb_labelcache (label, target, title, site_id)
    SELECT d.name || '.' || a.alias, a.target, a.alias, %s
    FROM docweb_docstring AS d
    LEFT JOIN docweb_docstringalias AS a
    ON d.name = a.parent_id
    WHERE d.name || '.' || a.alias != a.target AND d.type_ != 'dir'
          AND d.site_id = %s AND d.timestamp = %s
    """), [site.id, site.id, timestamp])
    
    # 1st dereference level (for .rst pages; they can have only 1 level)
    cursor.execute(port_sql("""
    INSERT INTO docweb_labelcache (label, target, title, site_id)
    SELECT d.name || '/' || a.alias, a.target, a.alias, %s
    FROM docweb_docstring AS d
    LEFT JOIN docweb_docstringalias AS a
    ON d.name = a.parent_id
    WHERE d.name || '/' || a.alias != a.target AND d.type_ = 'dir'
          AND d.site_id = %s AND d.timestamp = %s
    """), [site.id, site.id, timestamp])

    # -- Raw SQL needs a manual flush
    transaction.commit_unless_managed()

    # -- Do the part of the work that's not possible using SQL only
    for doc in Docstring.get_non_obsolete().filter(type_code='file').all():
        LabelCache.cache_docstring_labels(doc)
        ToctreeCache.cache_docstring(doc)
        doc._update_title()