Example #1
0
def main(directory=False):

    if directory:
        os.chdir(directory)

    config = Configuration(os.environ)

    if os.path.exists("_deconst.json"):
        with open("_deconst.json", "r", encoding="utf-8") as cf:
            config.apply_file(cf)

    # Lock source and destination to the same paths as the Makefile.
    srcdir = '.'
    destdir = os.path.join('_build', get_conf_builder(srcdir))

    status = build(srcdir, destdir)
    if status != 0:
        sys.exit(status)

    reasons = config.skip_submit_reasons()
    if reasons:
        print("Not submitting content to the content service because:",
              file=sys.stderr)
        print(file=sys.stderr)
        for reason in reasons:
            print(" * " + reason, file=sys.stderr)
        print(file=sys.stderr)
        return

    submit(destdir,
           config.content_store_url,
           config.content_store_apikey,
           config.content_id_base)
Example #2
0
    def init(self):
        SingleFileHTMLBuilder.init(self)

        self.deconst_config = Configuration(os.environ)

        if os.path.exists("_deconst.json"):
            with open("_deconst.json", "r", encoding="utf-8") as cf:
                self.deconst_config.apply_file(cf)

        self.should_submit = not self.deconst_config.skip_submit_reasons()
Example #3
0
def init_builder(builder):
    """
    Common Builder initialization.
    """

    builder.translator_class = OffsetHTMLTranslator

    builder.deconst_config = Configuration(os.environ)

    if path.exists('_deconst.json'):
        with open('_deconst.json', 'r', encoding='utf-8') as cf:
            builder.deconst_config.apply_file(cf)
Example #4
0
def main():

    config = Configuration(os.environ)

    # Lock source and destination to the same paths as the Makefile.
    srcdir, destdir = '.', '_build/deconst'

    status = build(srcdir, destdir)
    if status != 0:
        sys.exit(status)

    reasons = config.skip_submit_reasons()
    if reasons:
        print("Not submitting content to the content service because:",
              file=sys.stderr)
        print(file=sys.stderr)
        for reason in reasons:
            print(" * " + reason, file=sys.stderr)
        print(file=sys.stderr)
        return

    submit(destdir, config.content_store_url, config.content_id_base)
Example #5
0
    def init(self):
        SingleFileHTMLBuilder.init(self)

        self.deconst_config = Configuration(os.environ)

        if os.path.exists("_deconst.json"):
            with open("_deconst.json", "r", encoding="utf-8") as cf:
                self.deconst_config.apply_file(cf)

        try:
            self.git_root = self.deconst_config.get_git_root(os.getcwd())
        except FileNotFoundError:
            self.git_root = None

        self.should_submit = not self.deconst_config.skip_submit_reasons()
Example #6
0
    def init(self):
        JSONHTMLBuilder.init(self)

        self.deconst_config = Configuration(os.environ)
        self.should_submit = not self.deconst_config.skip_submit_reasons()
Example #7
0
class DeconstJSONBuilder(JSONHTMLBuilder):
    """
    Custom Sphinx builder that generates Deconst-compatible JSON documents.
    """

    implementation = jsonimpl
    name = 'deconst'
    out_suffix = '.json'

    def init(self):
        JSONHTMLBuilder.init(self)

        self.deconst_config = Configuration(os.environ)
        self.should_submit = not self.deconst_config.skip_submit_reasons()

    def finish(self):
        """
        We need to write images and static assets *first*.

        Also, the search indices and so on aren't necessary.
        """

    def dump_context(self, context, filename):
        """
        Override the default serialization code to save a derived metadata
        envelope, instead.
        """

        envelope = {
            "body": context["body"],
            "title": context["title"],
            "layout_key": context["deconst_layout_key"]
        }

        super().dump_context(envelope, filename)

    def handle_page(self, pagename, ctx, *args, **kwargs):
        """
        Override the default serialization code to save a derived metadata
        envelope, instead.
        """

        meta = self.env.metadata[pagename]
        ctx["deconst_layout_key"] = meta.get(
            "deconstlayout", self.config.deconst_default_layout)

        super().handle_page(pagename, ctx, *args, **kwargs)

    def post_process_images(self, doctree):
        """
        Publish images to the content store. Modify the image reference with
        the
        """

        JSONHTMLBuilder.post_process_images(self, doctree)

        if self.should_submit:
            for node in doctree.traverse(nodes.image):
                node['uri'] = self._publish_entry(node['uri'])

    def _publish_entry(self, srcfile):
        # TODO guess the content-type

        url = self.deconst_config.content_store_url + "assets"
        basename = path.basename(srcfile)
        files = {basename: open(srcfile, 'rb')}

        response = requests.post(url, files=files)
        response.raise_for_status()
        return response.json()[basename]
Example #8
0
class DeconstSingleJSONBuilder(SingleFileHTMLBuilder):
    """
    Custom Sphinx builder that generates Deconst-compatible JSON documents.
    """

    name = 'deconst-single'

    def init(self):
        SingleFileHTMLBuilder.init(self)

        self.deconst_config = Configuration(os.environ)

        if os.path.exists("_deconst.json"):
            with open("_deconst.json", "r", encoding="utf-8") as cf:
                self.deconst_config.apply_file(cf)

        try:
            self.git_root = self.deconst_config.get_git_root(os.getcwd())
        except FileNotFoundError:
            self.git_root = None

        self.should_submit = not self.deconst_config.skip_submit_reasons()

    def fix_refuris(self, tree):
        """
        The parent implementation of this includes the base file name, which
        breaks if we serve with a trailing slash. We just want what's between
        the last "#" and the end of the string
        """

        # fix refuris with double anchor
        for refnode in tree.traverse(nodes.reference):
            if 'refuri' not in refnode:
                continue
            refuri = refnode['refuri']
            hashindex = refuri.rfind('#')
            if hashindex < 0:
                continue

            # Leave absolute URLs alone
            if re.match("^https?://", refuri):
                continue

            refnode['refuri'] = refuri[hashindex:]

    def write(self, *ignored):
        docnames = self.env.all_docs

        self.info(bold('preparing documents... '), nonl=True)
        self.prepare_writing(docnames)
        self.info('done')

        self.info(bold('assembling single document... '), nonl=True)
        doctree = self.assemble_doctree()
        doctree.settings = self.docsettings

        self.env.toc_secnumbers = self.assemble_toc_secnumbers()
        self.secnumbers = self.env.toc_secnumbers.get(self.config.master_doc,
                                                      {})
        self.fignumbers = self.env.toc_fignumbers.get(self.config.master_doc,
                                                      {})

        target_uri = self.get_target_uri(self.config.master_doc)
        self.imgpath = relative_uri(target_uri, '_images')
        self.dlpath = relative_uri(target_uri, '_downloads')
        self.current_docname = self.config.master_doc

        if self.should_submit:
            self.post_process_images(doctree)

        # Merge this page's metadata with the repo-wide data.
        meta = self.deconst_config.meta.copy()
        meta.update(self.env.metadata.get(self.config.master_doc))

        title = self.env.longtitles.get(self.config.master_doc)
        toc = self.env.get_toctree_for(self.config.master_doc, self, False)

        self.fix_refuris(toc)

        rendered_title = self.render_partial(title)['title']
        rendered_toc = self.render_partial(toc)['fragment']
        layout_key = meta.get('deconstlayout',
                              self.config.deconst_default_layout)

        unsearchable = meta.get('deconstunsearchable',
                                self.config.deconst_default_unsearchable)
        if unsearchable is not None:
            unsearchable = unsearchable in ("true", True)

        rendered_body = self.write_body(doctree)

        if self.git_root != None and self.deconst_config.github_url != "":
            # current_page_name has no extension, and it _might_ not be .rst
            fileglob = path.join(
                os.getcwd(), self.env.srcdir, self.config.master_doc + ".*"
            )

            edit_segments = [
                self.deconst_config.github_url,
                "edit",
                self.deconst_config.github_branch,
                path.relpath(glob.glob(fileglob)[0], self.git_root)
            ]

            meta["github_edit_url"] = '/'.join(segment.strip('/') for segment in edit_segments)

        envelope = {
            "title": meta.get('deconsttitle', rendered_title),
            "body": rendered_body,
            "toc": rendered_toc,
            "layout_key": layout_key,
            "meta": dict(meta)
        }

        if unsearchable is not None:
            envelope["unsearchable"] = unsearchable

        page_cats = meta.get('deconstcategories')
        global_cats = self.config.deconst_categories
        if page_cats is not None or global_cats is not None:
            cats = set()
            if page_cats is not None:
                cats.update(re.split("\s*,\s*", page_cats))
            cats.update(global_cats or [])
            envelope["categories"] = list(cats)

        outfile = os.path.join(self.outdir, self.config.master_doc + '.json')

        with open(outfile, 'w', encoding="utf-8") as dumpfile:
            json.dump(envelope, dumpfile)

    def write_body(self, doctree):
        destination = StringOutput(encoding='utf-8')
        doctree.settings = self.docsettings

        self.docwriter.write(doctree, destination)
        self.docwriter.assemble_parts()

        return self.docwriter.parts['fragment']

    def finish(self):
        """
        Nothing to see here
        """

    def post_process_images(self, doctree):
        """
        Publish images to the content store. Modify the image reference with
        the
        """

        SingleFileHTMLBuilder.post_process_images(self, doctree)

        if self.should_submit:
            for node in doctree.traverse(nodes.image):
                node['uri'] = self._publish_entry(node['uri'])

    def _publish_entry(self, srcfile):
        (content_type, _) = mimetypes.guess_type(srcfile)

        auth = 'deconst apikey="{}"'.format(
            self.deconst_config.content_store_apikey)
        headers = {"Authorization": auth}
        verify = self.deconst_config.tls_verify

        url = self.deconst_config.content_store_url + "assets"
        basename = path.basename(srcfile)
        if content_type:
            payload = (basename, open(srcfile, 'rb'), content_type)
        else:
            payload = open(srcfile, 'rb')
        files = {basename: payload}

        response = requests.post(url, files=files, headers=headers,
                                 verify=verify)
        response.raise_for_status()
        return response.json()[basename]
Example #9
0
class DeconstSerialJSONBuilder(JSONHTMLBuilder):
    """
    Custom Sphinx builder that generates Deconst-compatible JSON documents.
    """

    implementation = jsonimpl
    name = 'deconst'
    out_suffix = '.json'

    def init(self):
        JSONHTMLBuilder.init(self)

        self.deconst_config = Configuration(os.environ)

        if os.path.exists("_deconst.json"):
            with open("_deconst.json", "r", encoding="utf-8") as cf:
                self.deconst_config.apply_file(cf)

        try:
            self.git_root = self.deconst_config.get_git_root(os.getcwd())
        except FileNotFoundError:
            self.git_root = None

        self.should_submit = not self.deconst_config.skip_submit_reasons()

    def finish(self):
        """
        We need to write images and static assets *first*.

        Also, the search indices and so on aren't necessary.
        """

    def dump_context(self, context, filename):

        """
        Override the default serialization code to save a derived metadata
        envelope, instead.
        """

        # Merge this page's metadata with the repo-wide data.
        meta = self.deconst_config.meta.copy()
        meta.update(context['meta'])

        if self.git_root != None and self.deconst_config.github_url != "":
            # current_page_name has no extension, and it _might_ not be .rst
            fileglob = path.join(
                os.getcwd(), context["current_page_name"] + ".*"
            )

            edit_segments = [
                self.deconst_config.github_url,
                "edit",
                self.deconst_config.github_branch,
                path.relpath(glob.glob(fileglob)[0], self.git_root)
            ]

            meta["github_edit_url"] = '/'.join(segment.strip('/') for segment in edit_segments)

        envelope = {
            "body": context["body"],
            "title": context["deconst_title"],
            "layout_key": context["deconst_layout_key"],
            "meta": meta
        }

        if context["deconst_unsearchable"] is not None:
            unsearchable = context["deconst_unsearchable"] in ("true", True)
            envelope["unsearchable"] = unsearchable

        page_cats = context["deconst_categories"]
        global_cats = self.config.deconst_categories
        if page_cats is not None or global_cats is not None:
            cats = set()
            if page_cats is not None:
                cats.update(re.split("\s*,\s*", page_cats))
            cats.update(global_cats or [])
            envelope["categories"] = list(cats)

        n = context.get("next")
        p = context.get("prev")

        if n:
            envelope["next"] = {
                "url": n["link"],
                "title": n["title"]
            }
        if p:
            envelope["previous"] = {
                "url": p["link"],
                "title": p["title"]
            }

        if context["display_toc"]:
            envelope["toc"] = context["toc"]

        super().dump_context(envelope, filename)

    def handle_page(self, pagename, ctx, *args, **kwargs):
        """
        Override the default serialization code to save a derived metadata
        envelope, instead.
        """

        meta = self.env.metadata[pagename]
        ctx["deconst_layout_key"] = meta.get(
            "deconstlayout", self.config.deconst_default_layout)
        ctx["deconst_title"] = meta.get("deconsttitle", ctx["title"])
        ctx["deconst_categories"] = meta.get("deconstcategories")
        ctx["deconst_unsearchable"] = meta.get(
            "deconstunsearchable", self.config.deconst_default_unsearchable)

        super().handle_page(pagename, ctx, *args, **kwargs)

    def post_process_images(self, doctree):
        """
        Publish images to the content store. Modify the image reference with
        the
        """

        JSONHTMLBuilder.post_process_images(self, doctree)

        if self.should_submit:
            for node in doctree.traverse(nodes.image):
                node['uri'] = self._publish_entry(node['uri'])

    def _publish_entry(self, srcfile):
        (content_type, _) = mimetypes.guess_type(srcfile)

        auth = 'deconst apikey="{}"'.format(
            self.deconst_config.content_store_apikey)
        headers = {"Authorization": auth}
        verify = self.deconst_config.tls_verify

        url = self.deconst_config.content_store_url + "assets"
        basename = path.basename(srcfile)
        if content_type:
            payload = (basename, open(srcfile, 'rb'), content_type)
        else:
            payload = open(srcfile, 'rb')
        files = {basename: payload}

        response = requests.post(url, files=files, headers=headers,
                                 verify=verify)
        response.raise_for_status()
        return response.json()[basename]
Example #10
0
class DeconstSingleJSONBuilder(SingleFileHTMLBuilder):
    """
    Custom Sphinx builder that generates Deconst-compatible JSON documents.
    """

    name = 'deconst-single'

    def init(self):
        SingleFileHTMLBuilder.init(self)

        self.deconst_config = Configuration(os.environ)

        if os.path.exists("_deconst.json"):
            with open("_deconst.json", "r", encoding="utf-8") as cf:
                self.deconst_config.apply_file(cf)

        self.should_submit = not self.deconst_config.skip_submit_reasons()

    def fix_refuris(self, tree):
        """
        The parent implementation of this includes the base file name, which
        breaks if we serve with a trailing slash. We just want what's between
        the last "#" and the end of the string
        """

        # fix refuris with double anchor
        for refnode in tree.traverse(nodes.reference):
            if 'refuri' not in refnode:
                continue
            refuri = refnode['refuri']
            hashindex = refuri.rfind('#')
            if hashindex < 0:
                continue

            # Leave absolute URLs alone
            if re.match("^https?://", refuri):
                continue

            refnode['refuri'] = refuri[hashindex:]

    def write(self, *ignored):
        docnames = self.env.all_docs

        self.info(bold('preparing documents... '), nonl=True)
        self.prepare_writing(docnames)
        self.info('done')

        self.info(bold('assembling single document... '), nonl=True)
        doctree = self.assemble_doctree()
        doctree.settings = self.docsettings

        self.env.toc_secnumbers = self.assemble_toc_secnumbers()
        self.secnumbers = self.env.toc_secnumbers.get(self.config.master_doc,
                                                      {})
        self.fignumbers = self.env.toc_fignumbers.get(self.config.master_doc,
                                                      {})

        target_uri = self.get_target_uri(self.config.master_doc)
        self.imgpath = relative_uri(target_uri, '_images')
        self.dlpath = relative_uri(target_uri, '_downloads')
        self.current_docname = self.config.master_doc

        if self.should_submit:
            self.post_process_images(doctree)

        meta = self.env.metadata.get(self.config.master_doc)

        title = self.env.longtitles.get(self.config.master_doc)
        toc = self.env.get_toctree_for(self.config.master_doc, self, False)

        self.fix_refuris(toc)

        rendered_title = self.render_partial(title)['title']
        rendered_toc = self.render_partial(toc)['fragment']
        layout_key = meta.get('deconstlayout',
                              self.config.deconst_default_layout)

        rendered_body = self.write_body(doctree)

        envelope = {
            "title": meta.get('deconsttitle', rendered_title),
            "body": rendered_body,
            "toc": rendered_toc,
            "layout_key": layout_key,
            "meta": dict(meta)
        }

        outfile = os.path.join(self.outdir, self.config.master_doc + '.json')

        with open(outfile, 'w', encoding="utf-8") as dumpfile:
            json.dump(envelope, dumpfile)

    def write_body(self, doctree):
        destination = StringOutput(encoding='utf-8')
        doctree.settings = self.docsettings

        self.docwriter.write(doctree, destination)
        self.docwriter.assemble_parts()

        return self.docwriter.parts['fragment']

    def finish(self):
        """
        Nothing to see here
        """

    def post_process_images(self, doctree):
        """
        Publish images to the content store. Modify the image reference with
        the
        """

        SingleFileHTMLBuilder.post_process_images(self, doctree)

        if self.should_submit:
            for node in doctree.traverse(nodes.image):
                node['uri'] = self._publish_entry(node['uri'])

    def _publish_entry(self, srcfile):
        (content_type, _) = mimetypes.guess_type(srcfile)

        auth = 'deconst apikey="{}"'.format(
            self.deconst_config.content_store_apikey)
        headers = {"Authorization": auth}

        url = self.deconst_config.content_store_url + "assets"
        basename = path.basename(srcfile)
        if content_type:
            payload = (basename, open(srcfile, 'rb'), content_type)
        else:
            payload = open(srcfile, 'rb')
        files = {basename: payload}

        response = requests.post(url, files=files, headers=headers)
        response.raise_for_status()
        return response.json()[basename]
Example #11
0
class DeconstSerialJSONBuilder(JSONHTMLBuilder):
    """
    Custom Sphinx builder that generates Deconst-compatible JSON documents.
    """

    implementation = jsonimpl
    name = "deconst"
    out_suffix = ".json"

    def init(self):
        JSONHTMLBuilder.init(self)

        self.deconst_config = Configuration(os.environ)

        if os.path.exists("_deconst.json"):
            with open("_deconst.json", "r", encoding="utf-8") as cf:
                self.deconst_config.apply_file(cf)

        self.should_submit = not self.deconst_config.skip_submit_reasons()

    def finish(self):
        """
        We need to write images and static assets *first*.

        Also, the search indices and so on aren't necessary.
        """

    def dump_context(self, context, filename):

        """
        Override the default serialization code to save a derived metadata
        envelope, instead.
        """

        envelope = {
            "body": context["body"],
            "title": context["deconst_title"],
            "layout_key": context["deconst_layout_key"],
        }

        n = context.get("next")
        p = context.get("prev")

        if n:
            envelope["next"] = {"url": n["link"], "title": n["title"]}
        if p:
            envelope["previous"] = {"url": p["link"], "title": p["title"]}

        if context["display_toc"]:
            envelope["toc"] = context["toc"]

        super().dump_context(envelope, filename)

    def handle_page(self, pagename, ctx, *args, **kwargs):
        """
        Override the default serialization code to save a derived metadata
        envelope, instead.
        """

        meta = self.env.metadata[pagename]
        ctx["deconst_layout_key"] = meta.get("deconstlayout", self.config.deconst_default_layout)
        ctx["deconst_title"] = meta.get("deconsttitle", ctx["title"])

        super().handle_page(pagename, ctx, *args, **kwargs)

    def post_process_images(self, doctree):
        """
        Publish images to the content store. Modify the image reference with
        the
        """

        JSONHTMLBuilder.post_process_images(self, doctree)

        if self.should_submit:
            for node in doctree.traverse(nodes.image):
                node["uri"] = self._publish_entry(node["uri"])

    def _publish_entry(self, srcfile):
        (content_type, _) = mimetypes.guess_type(srcfile)

        auth = 'deconst apikey="{}"'.format(self.deconst_config.content_store_apikey)
        headers = {"Authorization": auth}

        url = self.deconst_config.content_store_url + "assets"
        basename = path.basename(srcfile)
        if content_type:
            payload = (basename, open(srcfile, "rb"), content_type)
        else:
            payload = open(srcfile, "rb")
        files = {basename: payload}

        response = requests.post(url, files=files, headers=headers)
        response.raise_for_status()
        return response.json()[basename]