From 055c1d454cb286596e2c8241a8629abf5e33ae05 Mon Sep 17 00:00:00 2001 From: NaboKabbo <101510622+PythonChicken123@users.noreply.github.com> Date: Fri, 1 Dec 2023 22:12:14 +0000 Subject: [PATCH] Add docs/ --- docs/Makefile | 20 + docs/conf.py | 44 ++ docs/ext/boilerplate.py | 364 ++++++++++++ docs/ext/customversion.py | 33 ++ docs/ext/edit_on_github.py | 43 ++ docs/ext/headers.py | 51 ++ docs/ext/indexer.py | 229 ++++++++ docs/ext/utils.py | 105 ++++ docs/index.rst | 19 + docs/make.bat | 35 ++ docs/requirements.txt | 2 + docs/source/api.rst | 7 + docs/source/conf.py | 35 ++ docs/source/index.rst | 22 + docs/source/usage.rst | 34 ++ docs/themes/classic/elements.html | 105 ++++ docs/themes/classic/page.html | 10 + docs/themes/classic/static/pygame.css_t | 730 ++++++++++++++++++++++++ docs/themes/classic/theme.conf | 46 ++ 19 files changed, 1934 insertions(+) create mode 100644 docs/Makefile create mode 100644 docs/conf.py create mode 100644 docs/ext/boilerplate.py create mode 100644 docs/ext/customversion.py create mode 100644 docs/ext/edit_on_github.py create mode 100644 docs/ext/headers.py create mode 100644 docs/ext/indexer.py create mode 100644 docs/ext/utils.py create mode 100644 docs/index.rst create mode 100644 docs/make.bat create mode 100644 docs/requirements.txt create mode 100644 docs/source/api.rst create mode 100644 docs/source/conf.py create mode 100644 docs/source/index.rst create mode 100644 docs/source/usage.rst create mode 100644 docs/themes/classic/elements.html create mode 100644 docs/themes/classic/page.html create mode 100644 docs/themes/classic/static/pygame.css_t create mode 100644 docs/themes/classic/theme.conf diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..26b9422 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..df6cead --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,44 @@ +# Configuration file for the Sphinx documentation builder. + +# -- Project information +import sys +import os +sys.path.insert(0, os.path.abspath('..')) + +project = 'Masterchicken' +copyright = '2023, Masterchicken developers' +author = 'PythonChicken123' + +release = '0.1' +version = '0.0.7' + +# -- General configuration + +extensions = [ + 'sphinx.ext.duration', + 'sphinx.ext.doctest', + 'sphinx.ext.autodoc', + 'sphinx.ext.autosummary', + 'sphinx.ext.intersphinx', + 'sphinx.ext.coverage', + 'ext.headers', + 'ext.boilerplate', + 'ext.customversion', + 'ext.edit_on_github' +] + +intersphinx_mapping = { + 'python': ('https://docs.python.org/3/', None), + 'sphinx': ('https://www.sphinx-doc.org/en/master/', None), +} +intersphinx_disabled_domains = ['std'] +pygments_style = 'sphinx' +modindex_common_prefix = ['masterchicken'] +templates_path = ['_templates'] + +# -- Options for HTML output + +html_theme = "classic" + +# -- Options for EPUB output +epub_show_urls = 'footnote' diff --git a/docs/ext/boilerplate.py b/docs/ext/boilerplate.py new file mode 100644 index 0000000..526033a --- /dev/null +++ b/docs/ext/boilerplate.py @@ -0,0 +1,364 @@ +"""Add the generic fixed and derived content to a Classic Pygame document""" + +from ext.utils import Visitor, get_name, GetError, get_refid, as_refid, as_refuri +from ext.indexer import get_descinfo, get_descinfo_refid + +from sphinx.addnodes import desc, desc_content, desc_classname, desc_name, desc_signature +from sphinx.addnodes import index as section_prelude_end_class +from sphinx.domains.python import PyClasslike + +from docutils.nodes import ( + section, + literal, + reference, + paragraph, + title, + document, + Text, + TextElement, + inline, + table, + tgroup, + colspec, + tbody, + row, + entry, + whitespace_normalize_name, + SkipNode, + line, +) +import os +import re +from collections import deque + + +class PyGameClasslike(PyClasslike): + """ + No signature prefix for classes. + """ + + def get_signature_prefix(self, sig): + return "" if self.objtype == "class" else PyClasslike(self, sig) + + +def setup(app): + # This extension uses indexer collected tables. + app.setup_extension("ext.indexer") + + # Documents to leave untransformed by boilerplate + app.add_config_value("boilerplate_skip_transform", [], "") + + # Custom class directive signature + app.add_directive_to_domain("py", "class", PyGameClasslike) + + # The stages of adding boilerplate markup. + app.connect("doctree-resolved", transform_document) + app.connect("html-page-context", inject_template_globals) + + # Internal nodes. + app.add_node( + TocRef, + html=(visit_toc_ref_html, depart_toc_ref_html), + latex=(visit_toc_ref, depart_toc_ref), + text=(visit_toc_ref, depart_toc_ref), + ) + app.add_node( + TocTable, + html=(visit_toc_table_html, depart_toc_table_html), + latex=(visit_skip, None), + text=(visit_skip, None), + ) + app.add_node( + DocTitle, + html=(visit_doc_title_html, depart_doc_title_html), + latex=(visit_doc_title, depart_doc_title), + ) + + +class TocRef(reference): + pass + + +def visit_toc_ref(self, node): + self.visit_reference(node) + + +def depart_toc_ref(self, node): + self.depart_reference(node) + + +def visit_toc_ref_html(self, node): + refuri = node["refuri"] + refid = as_refid(refuri) + docname = get_descinfo_refid(refid, self.settings.env)["docname"] + link_suffix = self.builder.link_suffix + node["refuri"] = f"{os.path.basename(docname)}{link_suffix}{refuri}" + visit_toc_ref(self, node) + + +class TocTable(table): + pass + + +def visit_toc_table_html(self, node): + self.visit_table(node) + + +def depart_toc_table_html(self, node): + self.depart_table(node) + + +def visit_skip(self, node): + raise SkipNode() + + +depart_toc_ref_html = depart_toc_ref + + +class DocTitle(title): + pass + + +visit_doc_title_html = visit_skip +depart_doc_title_html = None + + +def visit_doc_title(self, node): + self.visit_title(node) + + +def depart_doc_title(self, node): + self.depart_title(node) + + +def transform_document(app, doctree, docname): + if docname in app.config["boilerplate_skip_transform"]: + return + doctree.walkabout(DocumentTransformer(app, doctree)) + + +class DocumentTransformer(Visitor): + _key_re = r"(?P[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]*)*)" + key_pat = re.compile(_key_re) + + def __init__(self, app, document_node): + super().__init__(app, document_node) + self.module_stack = deque() + self.title_stack = deque() + + def visit_section(self, node): + self.title_stack.append(None) + + def depart_section(self, node): + title_node = self.title_stack.pop() + if node["ids"][0].startswith("module-"): + transform_module_section(node, title_node, self.env) + + def visit_desc(self, node): + # Only want to do special processing on pygame's Python api. + # Other stuff, like C code, get no special treatment. + if node["domain"] != "py": + raise self.skip_node + + def depart_desc(self, node): + node["classes"].append("definition") + node[0]["classes"].append("title") + if not node.attributes["noindex"]: + add_toc(node, self.env) + + def visit_title(self, node): + if isinstance(node.parent.parent, document): + # Make title node a DocTitle instance. This works because DocTitle + # simply subclasses title. + node.__class__ = DocTitle + + def visit_reference(self, node): + if "toc" in node["classes"]: + return + try: + child = node[0] + except IndexError: + return + if not isinstance(child, TextElement): + return + name = child.astext() + m = self.key_pat.match(name) + if m is None: + return + key = m.group("key") + try: + summary = get_descinfo_refid(key, self.env)["summary"] + except GetError: + return + if summary: + node["reftitle"] = "" + node["classes"].append("tooltip") + inline_node = inline("", summary, classes=["tooltip-content"]) + node.append(inline_node) + + +def transform_module_section(section_node, title_node, env): + fullmodname = section_node["names"][0] + where = section_node.first_child_matching_class(section_prelude_end_class) + content_children = list(ipop_child(section_node, where + 1)) + if title_node is None: + signature_children = [literal("", fullmodname)] + else: + signature_children = list(ipop_child(title_node)) + signature_node = desc_signature( + "", "", *signature_children, classes=["title", "module"], names=[fullmodname] + ) + content_node = desc_content("", *content_children) + desc_node = desc( + "", + signature_node, + content_node, + desctype="module", + objtype="module", + classes=["definition"], + ) + section_node.append(desc_node) + add_toc(desc_node, env, section_node) + + +def ipop_child(node, start=0): + while len(node) > start: + yield node.pop(start) + + +def get_target_summary(reference_node, env): + try: + return get_descinfo_refid(reference_node["refid"], env)["summary"] + except KeyError: + raise GetError("reference has no refid") + + +# Calls build_toc, which builds the section at the top where it lists the functions +def add_toc(desc_node, env, section_node=None): + """Add a table of contents to a desc node""" + + if section_node is not None: + refid = get_refid(section_node) + else: + refid = get_refid(desc_node) + descinfo = get_descinfo_refid(refid, env) + toc = build_toc(descinfo, env) + if toc is None: + return + content_node = desc_node[-1] + insert_at = 0 + if descinfo["summary"]: # if have a summary + insert_at += 1 + content_node.insert(insert_at, toc) + + +# Builds the section at the top of the module where it lists the functions +def build_toc(descinfo, env): + """Return a desc table of contents node tree""" + + separator = "—" + child_ids = descinfo["children"] + if not child_ids: + return None + max_fullname_len = 0 + max_summary_len = 0 + rows = [] + for fullname, refid, summary in ichild_ids(child_ids, env): + max_fullname_len = max(max_fullname_len, len(fullname)) + max_summary_len = max(max_summary_len, len(summary)) + reference_node = toc_ref(fullname, refid) + ref_entry_node = entry("", line("", "", reference_node)) + sep_entry_node = entry("", Text(separator, "")) + sum_entry_node = entry("", Text(summary, "")) + row_node = row("", ref_entry_node, sep_entry_node, sum_entry_node) + rows.append(row_node) + col0_len = max_fullname_len + 2 # add error margin + col1_len = len(separator) # no padding + col2_len = max_summary_len + 10 # add error margin + tbody_node = tbody("", *rows) + col0_colspec_node = colspec(colwidth=col0_len) + col1_colspec_node = colspec(colwidth=col1_len) + col2_colspec_node = colspec(colwidth=col2_len) + tgroup_node = tgroup( + "", col0_colspec_node, col1_colspec_node, col2_colspec_node, tbody_node, cols=3 + ) + return TocTable("", tgroup_node, classes=["toc"]) + + +def ichild_ids(child_ids, env): + for refid in child_ids: + descinfo = env.pyg_descinfo_tbl[refid] # A KeyError would mean a bug. + yield descinfo["fullname"], descinfo["refid"], descinfo["summary"] + + +def toc_ref(fullname, refid): + name = (whitespace_normalize_name(fullname),) + return TocRef("", fullname, name=name, refuri=as_refuri(refid), classes=["toc"]) + + +def decorate_signatures(desc, classname): + prefix = classname + "." + for child in desc.children: + if isinstance(child, desc_signature) and isinstance(child[0], desc_name): + new_desc_classname = desc_classname("", prefix) + child.insert(0, new_desc_classname) + + +def inject_template_globals(app, pagename, templatename, context, doctree): + def lowercase_name(d): + return get_name(d["fullname"]).lower() + + env = app.builder.env + try: + sections = env.pyg_sections + except AttributeError: + sections = [] + else: + sections = sorted(sections, key=lowercase_name) + + # Sort them in this existing order. + existing_order = [ + "Color", + "cursors", + "display", + "draw", + "event", + "font", + "image", + "joystick", + "key", + "locals", + "mask", + "mixer", + "mouse", + "music", + "pygame", + "Rect", + "Surface", + "sprite", + "time", + "transform", + "BufferProxy", + "freetype", + "gfxdraw", + "midi", + "Overlay", + "PixelArray", + "pixelcopy", + "sndarray", + "surfarray", + ] + existing_order = ["pygame." + x for x in existing_order] + + def sort_by_order(sequence, existing_order): + return existing_order + [x for x in sequence if x not in existing_order] + + full_name_section = {x["fullname"]: x for x in sections} + full_names = [x["fullname"] for x in sections] + sorted_names = sort_by_order(full_names, existing_order) + + sections = [ + full_name_section[name] for name in sorted_names if name in full_name_section + ] + + context["pyg_sections"] = sections diff --git a/docs/ext/customversion.py b/docs/ext/customversion.py new file mode 100644 index 0000000..d951e09 --- /dev/null +++ b/docs/ext/customversion.py @@ -0,0 +1,33 @@ +from sphinx.domains.changeset import versionlabels, VersionChange +from sphinx.locale import _ # just to suppress warnings + +try: + from sphinx.domains.changeset import versionlabel_classes +except ImportError: + # versionlabel_classes doesn't exist in old Sphinx versions. + UPDATE_VERIONLABEL_CLASSES = False +else: + UPDATE_VERIONLABEL_CLASSES = True + + +labels = ("versionadded", "versionchanged", "deprecated", "versionextended") + + +def set_version_formats(app, config): + for label in labels: + versionlabels[label] = _(getattr(config, f"{label}_format")) + + +def setup(app): + app.add_directive("versionextended", VersionChange) + versionlabels["versionextended"] = "Extended in pygame %s" + + if UPDATE_VERIONLABEL_CLASSES: + versionlabel_classes["versionextended"] = "extended" + + for label in ("versionadded", "versionchanged", "deprecated", "versionextended"): + app.add_config_value( + f"{label}_format", str(versionlabels[label]), "env" + ) + + app.connect("config-inited", set_version_formats) diff --git a/docs/ext/edit_on_github.py b/docs/ext/edit_on_github.py new file mode 100644 index 0000000..9dd3c0a --- /dev/null +++ b/docs/ext/edit_on_github.py @@ -0,0 +1,43 @@ +""" +from: https://gist.github.com/mgedmin/6052926 +Sphinx extension to add ReadTheDocs-style "Edit on GitHub" links to the +sidebar. +Loosely based on https://github.com/astropy/astropy/pull/347 +""" + +import os +import warnings + + +__license__ = "BSD (3 clause)" + + +def get_github_url(app, view, path): + return "https://github.com/{project}/{view}/{branch}/docs/reST/{path}".format( + project=app.config.edit_on_github_project, + view=view, + branch=app.config.edit_on_github_branch, + path=path, + ) + + +def html_page_context(app, pagename, templatename, context, doctree): + if templatename != "page.html": + return + + if not app.config.edit_on_github_project: + warnings.warn("edit_on_github_project not specified") + return + + path = os.path.relpath(doctree.get("source"), app.builder.srcdir) + show_url = get_github_url(app, "blob", path) + edit_url = get_github_url(app, "edit", path) + + context["show_on_github_url"] = show_url + context["edit_on_github_url"] = edit_url + + +def setup(app): + app.add_config_value("edit_on_github_project", "", True) + app.add_config_value("edit_on_github_branch", "main", True) + app.connect("html-page-context", html_page_context) diff --git a/docs/ext/headers.py b/docs/ext/headers.py new file mode 100644 index 0000000..02295f0 --- /dev/null +++ b/docs/ext/headers.py @@ -0,0 +1,51 @@ +from ext.utils import get_sectionname, isections +from ext.indexer import tour_descinfo + +import os + + +def setup(app): + # This extension uses indexer collected tables. + app.setup_extension("ext.indexer") + + # The target directory for the header files. + app.add_config_value("headers_dest", ".", "html") + + # Create directory tree if missing? + app.add_config_value("headers_mkdirs", False, "") + + # Suffix to tag onto file name before the '.h' extension + app.add_config_value("headers_filename_sfx", "", "html") + + # Header template to use + app.add_config_value("headers_template", "header.h", "html") + + # Write a header when its corresponding HTML page is written. + app.connect("html-page-context", writer) + + +def writer(app, pagename, templatename, context, doctree): + if doctree is None: + return + + env = app.builder.env + dirpath = os.path.abspath(app.config["headers_dest"]) + if app.config["headers_mkdirs"] and not os.path.lexists(dirpath): + os.makedirs(dirpath) + filename_suffix = app.config["headers_filename_sfx"] + items = [] + for section in isections(doctree): + tour_descinfo(items.append, section, env) + if not items: + return + templates = app.builder.templates + filename = f"{os.path.basename(pagename)}{filename_suffix}.h" + filepath = os.path.join(dirpath, filename) + template = app.config["headers_template"] + header = open(filepath, "w", encoding="utf-8") + context["hdr_items"] = items + try: + header.write(templates.render(template, context)) + finally: + header.close() + del context["hdr_items"] diff --git a/docs/ext/indexer.py b/docs/ext/indexer.py new file mode 100644 index 0000000..9295876 --- /dev/null +++ b/docs/ext/indexer.py @@ -0,0 +1,229 @@ +"""Collect information on document sections and Pygame API objects + +The following persistent Pygame specific environment structures are built: + +pyg_sections: [{'docname': , + 'fullname': , + 'refid': }, + ...] + all Pygame api sections in the documents in order processed. +pyg_descinfo_tbl: {: {'fullname': , + 'desctype': , + 'summary': , + 'signatures': , + 'children': , + 'refid': , + 'docname': }, + ...} + object specific information, including a list of direct children, if any. + +: (str) the simple document name without path or extension. +: (str) a fully qualified object name. It is a unique identifier. +: (str) an id usable as local uri reference. +: (str) unique desc id, the first entry in the ids attribute list. +: (str) an object's type: the desctype attribute. +: (str) a summary line as identified by a :summaryline: role. + This corresponds to the first line of a docstring. +: (list of str) an object's signatures, in document order. +: (list of str) refids of an object's children, in document order. + +""" + +from ext.utils import Visitor, get_fullname, get_refid, as_refid, GetError + +from collections import deque + +import os.path + +MODULE_ID_PREFIX = "module-" + + +def setup(app): + app.connect("env-purge-doc", prep_document_info) + app.connect("doctree-read", collect_document_info) + + +def prep_document_info(app, env, docname): + try: + env.pyg_sections = [e for e in env.pyg_sections if e["docname"] != docname] + except AttributeError: + pass + except KeyError: + pass + try: + descinfo_tbl = env.pyg_descinfo_tbl + except AttributeError: + pass + else: + to_remove = [k for k, v in descinfo_tbl.items() if v["docname"] == docname] + for k in to_remove: + del descinfo_tbl[k] + + +def collect_document_info(app, doctree): + doctree.walkabout(CollectInfo(app, doctree)) + + +class CollectInfo(Visitor): + + """Records the information for a document""" + + desctypes = { + "data", + "function", + "exception", + "class", + "attribute", + "method", + "staticmethod", + "classmethod", + } + + def __init__(self, app, document_node): + super().__init__(app, document_node) + self.docname = self.env.docname + self.summary_stack = deque() + self.sig_stack = deque() + self.desc_stack = deque() + try: + self.env.pyg_sections + except AttributeError: + self.env.pyg_sections = [] + try: + self.env.pyg_descinfo_tbl + except AttributeError: + self.env.pyg_descinfo_tbl = {} + + def visit_document(self, node): + # Only index pygame Python API documents, found in the docs/reST/ref + # subdirectory. Thus the tutorials and the C API documents are skipped. + source = node["source"] + head, file_name = os.path.split(source) + if not file_name: + raise self.skip_node + head, dir_name = os.path.split(head) + if not (dir_name == "ref" or dir_name == "referencias"): + raise self.skip_node + head, dir_name = os.path.split(head) + if not (dir_name == "reST" or dir_name == "es"): + raise self.skip_node + head, dir_name = os.path.split(head) + if dir_name != "docs": + raise self.skip_node + + def visit_section(self, node): + if not node["names"]: + raise self.skip_node + self._push() + + def depart_section(self, node): + """Record section info""" + + summary, sigs, child_descs = self._pop() + if not node.children: + return + if node["ids"][0].startswith(MODULE_ID_PREFIX): + self._add_section(node) + self._add_descinfo(node, summary, sigs, child_descs) + elif child_descs: + # No section level introduction: use the first toplevel directive + # instead. + desc_node = child_descs[0] + summary = get_descinfo(desc_node, self.env).get("summary", "") + self._add_section(desc_node) + self._add_descinfo_entry(node, get_descinfo(desc_node, self.env)) + + def visit_desc(self, node): + """Prepare to collect a summary and toc for this description""" + + if node.get("desctype", "") not in self.desctypes: + raise self.skip_node + self._push() + + def depart_desc(self, node): + """Record descinfo information and add descinfo to parent's toc""" + + self._add_descinfo(node, *self._pop()) + self._add_desc(node) + + def visit_inline(self, node): + """Collect a summary or signature""" + + if "summaryline" in node["classes"]: + self._add_summary(node) + elif "signature" in node["classes"]: + self._add_sig(node) + raise self.skip_departure + + def _add_section(self, node): + entry = { + "docname": self.docname, + "fullname": get_fullname(node), + "refid": get_refid(node), + } + self.env.pyg_sections.append(entry) + + def _add_descinfo(self, node, summary, sigs, child_descs): + entry = { + "fullname": get_fullname(node), + "desctype": node.get("desctype", "module"), + "summary": summary, + "signatures": sigs, + "children": [get_refid(n) for n in child_descs], + "refid": get_refid(node), + "docname": self.docname, + } + self._add_descinfo_entry(node, entry) + + def _add_descinfo_entry(self, node, entry): + key = get_refid(node) + if key.startswith(MODULE_ID_PREFIX): + key = key[len(MODULE_ID_PREFIX) :] + self.env.pyg_descinfo_tbl[key] = entry + + def _push(self): + self.summary_stack.append("") + self.sig_stack.append([]) + self.desc_stack.append([]) + + def _pop(self): + return (self.summary_stack.pop(), self.sig_stack.pop(), self.desc_stack.pop()) + + def _add_desc(self, desc_node): + self.desc_stack[-1].append(desc_node) + + def _add_summary(self, text_element_node): + self.summary_stack[-1] = text_element_node[0].astext() + + def _add_sig(self, text_element_node): + self.sig_stack[-1].append(text_element_node[0].astext()) + + +def tour_descinfo(fn, node, env): + try: + descinfo = get_descinfo(node, env) + except GetError: + return + fn(descinfo) + for refid in descinfo["children"]: + tour_descinfo_refid(fn, refid, env) + + +def tour_descinfo_refid(fn, refid, env): + descinfo = env.pyg_descinfo_tbl[refid] # A KeyError would mean a bug. + fn(descinfo) + for refid in descinfo["children"]: + tour_descinfo_refid(fn, refid, env) + + +def get_descinfo(node, env): + return get_descinfo_refid(get_refid(node), env) + + +def get_descinfo_refid(refid, env): + if refid.startswith(MODULE_ID_PREFIX): + refid = refid[len(MODULE_ID_PREFIX) :] + try: + return env.pyg_descinfo_tbl[refid] + except KeyError: + raise GetError("Not found") diff --git a/docs/ext/utils.py b/docs/ext/utils.py new file mode 100644 index 0000000..bcf387c --- /dev/null +++ b/docs/ext/utils.py @@ -0,0 +1,105 @@ +import docutils.nodes +import sphinx.addnodes + +import sys + + +class GetError(LookupError): + pass + + +def get_fullname(node): + if isinstance(node, docutils.nodes.section): + return get_sectionname(node) + if isinstance(node, sphinx.addnodes.desc): + return get_descname(node) + raise TypeError(f"Unrecognized node type '{node.__class__}'") + + +def get_descname(desc): + try: + sig = desc[0] + except IndexError: + raise GetError("No fullname: missing children in desc") + try: + names = sig["ids"] + except KeyError: + raise GetError("No fullname: missing ids attribute in desc's child") + try: + return names[0] + except IndexError: + raise GetError("No fullname: desc's child has empty names list") + + +def get_sectionname(section): + try: + names = section["names"] + except KeyError: + raise GetError("No fullname: missing names attribute in section") + try: + return names[0] + except IndexError: + raise GetError("No fullname: section has empty names list") + + +def get_refuri(node): + return as_refuri(get_refid(node)) + + +def get_refid(node): + try: + return get_ids(node)[0] + except IndexError: + raise GetError("Node has empty ids list") + + +def as_refid(refuri): + return refuri[1:] + + +def as_refuri(refid): + return "#" + refid + + +def get_ids(node): + if isinstance(node, docutils.nodes.section): + try: + return node["ids"] + except KeyError: + raise GetError("No ids: section missing ids attribute") + if isinstance(node, sphinx.addnodes.desc): + try: + sig = node[0] + except IndexError: + raise GetError("No ids: missing desc children") + try: + return sig["ids"] + except KeyError: + raise GetError("No ids: desc's child missing ids attribute") + raise TypeError(f"Unrecognized node type '{node.__class__}'") + + +def isections(doctree): + for node in doctree: + if isinstance(node, docutils.nodes.section): + yield node + + +def get_name(fullname): + return fullname.split(".")[-1] + + +class Visitor(docutils.nodes.SparseNodeVisitor): + skip_node = docutils.nodes.SkipNode() + skip_departure = docutils.nodes.SkipDeparture() + + def __init__(self, app, document_node): + docutils.nodes.SparseNodeVisitor.__init__(self, document_node) + self.app = app + self.env = app.builder.env + + def unknown_visit(self, node): + return + + def unknown_departure(self, node): + return diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..dd6497e --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,19 @@ +Welcome to Masterchicken's documentation! +=================================== + +**Masterchicken** (/lu'make/) is a Python library to speed up code and imports an optimized csv reader/writer, this CSV reader can read and print a 100,000 lined CSV file in a maxiumum of 7 seconds. + +Check out the :doc:`usage` section for further information, including +how to :ref:`installation` the project. + +.. note:: + + This project is under active development. + +Contents +-------- + +.. toctree:: + + usage + api diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..6247f7e --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..902d623 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,2 @@ +sphinx==7.1.2 +sphinx-rtd-theme==1.3.0rc1 diff --git a/docs/source/api.rst b/docs/source/api.rst new file mode 100644 index 0000000..6e8d80b --- /dev/null +++ b/docs/source/api.rst @@ -0,0 +1,7 @@ +API +=== + +.. autosummary:: + :toctree: generated + + lumache diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 0000000..ff87824 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,35 @@ +# Configuration file for the Sphinx documentation builder. + +# -- Project information + +project = 'Lumache' +copyright = '2021, Graziella' +author = 'Graziella' + +release = '0.1' +version = '0.1.0' + +# -- General configuration + +extensions = [ + 'sphinx.ext.duration', + 'sphinx.ext.doctest', + 'sphinx.ext.autodoc', + 'sphinx.ext.autosummary', + 'sphinx.ext.intersphinx', +] + +intersphinx_mapping = { + 'python': ('https://docs.python.org/3/', None), + 'sphinx': ('https://www.sphinx-doc.org/en/master/', None), +} +intersphinx_disabled_domains = ['std'] + +templates_path = ['_templates'] + +# -- Options for HTML output + +html_theme = 'sphinx_rtd_theme' + +# -- Options for EPUB output +epub_show_urls = 'footnote' diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 0000000..4c0247b --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,22 @@ +Welcome to Lumache's documentation! +=================================== + +**Lumache** (/lu'make/) is a Python library for cooks and food lovers +that creates recipes mixing random ingredients. +It pulls data from the `Open Food Facts database `_ +and offers a *simple* and *intuitive* API. + +Check out the :doc:`usage` section for further information, including +how to :ref:`installation` the project. + +.. note:: + + This project is under active development. + +Contents +-------- + +.. toctree:: + + usage + api diff --git a/docs/source/usage.rst b/docs/source/usage.rst new file mode 100644 index 0000000..342ce29 --- /dev/null +++ b/docs/source/usage.rst @@ -0,0 +1,34 @@ +Usage +===== + +.. _installation: + +Installation +------------ + +To use Lumache, first install it using pip: + +.. code-block:: console + + (.venv) $ pip install lumache + +Creating recipes +---------------- + +To retrieve a list of random ingredients, +you can use the ``lumache.get_random_ingredients()`` function: + +.. autofunction:: lumache.get_random_ingredients + +The ``kind`` parameter should be either ``"meat"``, ``"fish"``, +or ``"veggies"``. Otherwise, :py:func:`lumache.get_random_ingredients` +will raise an exception. + +.. autoexception:: lumache.InvalidKindError + +For example: + +>>> import lumache +>>> lumache.get_random_ingredients() +['shells', 'gorgonzola', 'parsley'] + diff --git a/docs/themes/classic/elements.html b/docs/themes/classic/elements.html new file mode 100644 index 0000000..62fa6b8 --- /dev/null +++ b/docs/themes/classic/elements.html @@ -0,0 +1,105 @@ +{%- extends "!layout.html" %} + +{%- if not theme_relbar %} +{%- block relbar1 %}{# Do not want bar across the top #}{% endblock %} +{%- endif %} + +{#- Classic header with Snakey on the left, page links on the right. + #} +{%- macro header() %} +
+
+ + +
+
+{%- endmacro %} + +{%- block body %} +{%- block section %}{% endblock %} + +

+
+{{ _('Edit on GitHub') }} +{%- endblock %} + + +{%- block relbaritems %} + +{% endblock %} diff --git a/docs/themes/classic/page.html b/docs/themes/classic/page.html new file mode 100644 index 0000000..677c128 --- /dev/null +++ b/docs/themes/classic/page.html @@ -0,0 +1,10 @@ +{%- extends "elements.html" %} + +{%- block document %} +{{ header() }} +{{ super() }} +{%- endblock %} + +{%- block section %} +{{ body }} +{%- endblock %} diff --git a/docs/themes/classic/static/pygame.css_t b/docs/themes/classic/static/pygame.css_t new file mode 100644 index 0000000..0b3683f --- /dev/null +++ b/docs/themes/classic/static/pygame.css_t @@ -0,0 +1,730 @@ +/** + * Pygame stylesheet -- original theme + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +@import url("reset.css"); +@import url("tooltip.css"); +@import url("basic.css"); + +/* -- index page ------------------------------------------------------------ */ + +#pygame-front-page h2 { + margin-top: 2em; +} + +#pygame-front-page dt { + padding-top: 1em; +} + +#pygame-front-page dl { + padding-left: 1em; +} + +/* -- tutorial page --------------------------------------------------------- */ + +div.body table.docinfo { + font-size: 0.9em; + margin-left: 3em; +} + +div.body table.docinfo th { + text-align: left; + font-weight: bolder; +} + +div.body table.docinfo td { + padding-left: 1em; +} + +div.body table.matrix th, +div.body table.matrix td { + border-right: 0.1em solid black; +} + +div.body table.matrix th:last-child, +div.body table.matrix td:last-child { + border-right-style: none; +} + +div.body table.matrix th { + font-weight: bolder; +} + +div.body table.matrix th.head { + text-align: center; + border-bottom: 0.2em double black; +} + +div.body table.matrix td { + text-align: center; +} + +div.body table.matrix th.stub { + text-align: left; +} + +/* Let a float element be beside a code block */ +div.highlight-python.notranslate { + display: inline-block; +} + +/* -- page layout ----------------------------------------------------------- */ + +body { + font-family: {{ theme_bodyfont }}; + color: {{ theme_textcolor }}; + font-style: normal; + text-decoration: none; + text-align: left; + border-style: none; +} + +div.header { + padding: 0.5em; + line-height: 1.2em; +} + +div.header > div { + border: {{ theme_headerborder }}; + border-collapse: collapse; + background-color: {{ theme_headerbgcolor }}; +} + +div.header .logo { + background-color: {{ theme_logobgcolor }}; + padding: 0.3em; + border-right: 3px solid black; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} + +div.header .logo img { + min-width: 200px; + min-height: 60px; + border-style: none; +} + +div.header .pagelinks { + padding: 0.3em; + text-align: center; + vertical-align: middle; + flex-grow: 1; +} + +div.header p.top { + margin-top: 0; + margin-bottom: 1em; +} + +div.header p.bottom { + margin-bottom: 0; + text-align: left; +} + +div.header .pagelinks a { + text-decoration: underline; +} + +div.header .pagelinks a:hover { + text-decoration: none; +} + +div.document { + background-color: {{ theme_bgcolor }}; +} + +.flex-container { + display: flex; + flex-direction: row; +} + +@media only screen and (max-width: 680px) { + .flex-container { + flex-direction: column; + } + + div.header .logo { + border-right: none; + border-bottom: 3px solid black; + } +} + +/* on wide screens center text, and max width for readable area. */ +@media only screen and (min-width: 680px) { + div.documentwrapper { + float: initial; + width: 100%; + max-width: 700px; + margin: 0 auto; + } +} + +.toc td { + display:block; + width: min(200%, 100vw - 132px); +} + +.toc td:last-child { + padding-bottom: 20px; +} + +table.toc td:nth-child(2) { + display: none; +} + +div.bodywrapper { + margin: 0 0 0 230px; +} + +div.body { + min-width: auto; + padding: 0.5em; +} + +div.heading { + padding: 0 0 0 0.5em; +} + +{% if theme_rightsidebar|tobool %} +div.bodywrapper { + margin: 0 230px 0 0; +} + +{% endif -%} + +div.footer { +{%- if theme_nofooter %} + display: none; +{%- endif %} + color: {{ theme_footertextcolor }}; + width: 100%; + padding: 9px 0; + text-align: center; + font-size: 75%; +} + +div.footer a { + background-color: {{ theme_footerbgcolor }}; + color: {{ theme_footertextcolor }}; + text-decoration: underline; +} + +div.related { + background-color: {{ theme_relbarbgcolor }}; + line-height: 30px; + color: {{ theme_relbartextcolor }}; + border-top: 3px solid black; + border-bottom: 3px solid black; +} + +div.related a { + color: {{ theme_relbarlinkcolor }}; +} + +div.sphinxsidebar { + {%- if theme_stickysidebar|tobool %} + top: 30px; + bottom: 0; + margin: 0; + position: fixed; + overflow: auto; + height: auto; + {%- endif %} + {%- if theme_rightsidebar|tobool %} + float: right; + {%- if theme_stickysidebar|tobool %} + right: 0; + {%- endif %} + {%- endif %} +} + +div.sphinxsidebar h3 { + font-family: {{ theme_headfont }}; + color: {{ theme_sidebartextcolor }}; + font-size: 1.4em; + font-weight: normal; + margin: 0; + padding: 0; +} + +div.sphinxsidebar h3 a { + color: {{ theme_sidebartextcolor }}; +} + +div.sphinxsidebar h4 { + font-family: {{ theme_headfont }}; + color: {{ theme_sidebartextcolor }}; + font-size: 1.3em; + font-weight: normal; + margin: 5px 0 0 0; + padding: 0; +} + +div.sphinxsidebar p { + color: {{ theme_sidebartextcolor }}; +} + +div.sphinxsidebar p.topless { + margin: 5px 10px 10px 10px; +} + +div.sphinxsidebar ul { + margin: 10px; + padding: 0; + color: {{ theme_sidebartextcolor }}; +} + +div.sphinxsidebar a { + color: {{ theme_sidebarlinkcolor }}; +} + +div.sphinxsidebar input { + border: 1px solid {{ theme_sidebarlinkcolor }}; + font-family: sans-serif; + font-size: 1em; +} + +/* -- body styles ----------------------------------------------------------- */ + +a { + color: {{ theme_linkcolor }}; + background-color: transparent; + text-decoration: underline; +} + +a:hover { + color: {{ theme_linkcolor }}; + background-color: transparent; + text-decoration: none; +} + +p { + margin: 0.5em 0; +} + +strong { + font-weight: bold; +} + +em { + font-style: italic; +} + +dl.definition { + padding-top: 0.7em; + padding-bottom: 0.7em; +} + +dt.title { + font-family: monospace; +} + +dt tt { + font-weight: bold; + font-size: 1.1em; +} + +dt.module { + margin-bottom: 1em; +} + +table.toc { + margin-left: 2em; + margin-bottom: 0.5em; +} + +table.toc td { + border-style: none; + padding-right: 10px; +} + +span.summaryline { + font-style: italic; +} + +span.signature { + font-family: {{ theme_sigfont }}; + margin-bottom: 0.5em; +} + +span.pre { + font-family: monospace; +} + +code.download span.pre { + font-family: inherit; + font-weight: normal; +} + +table.docutils td.toc { + border-style: none; +} + +div.body p, div.body dd, div.body li { + text-align: left; + line-height: 130%; +} + +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + font-family: {{ theme_headfont }}; + font-weight: bold; + margin: 1em 0 0.5em; + text-align: left; +} + +div.body h6 { + font-weight: normal; + font-style: italic; + margin: 0; + padding: 0; + line-height: 130%; +} + +div.body h1 { font-size: 1.5em; } +div.body h2 { font-size: 1.4em; } +div.body h3 { font-size: 1.3em; } +div.body h4 { font-size: 1.2em; } +div.body h5 { font-size: 1.1em; } +div.body h6 { font-size: 1.0em; } + +a.headerlink { + color: {{ theme_headlinkcolor }}; + font-size: 0.8em; + padding: 0 4px 0 4px; + text-decoration: none; +} + +a.headerlink:hover { + background-color: {{ theme_headlinkcolor }}; + color: white; +} + +blockquote { + margin-left: 2em; +} + +div.admonition p.admonition-title + p { + display: inline; +} + +div.admonition p, +div.admonition pre, +div.admonition ul, +div.admonition ol { + margin-bottom: 5px; +} + +p.admonition-title { + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +dl.definition div.note, dl.definition div.seealso { + background: none; + border: none; +} + +dl.definition div.admonition { + margin-bottom: 0.5em; +} + +dl.definition .admonition-title { + font-weight: bold; +} + +div.note { + background-color: {{ theme_notebgcolor }}; + border: 1px solid #ccc; +} + +.note tt { + background: #d6d6d6; +} + +div.seealso { + background-color: #ffc; + border: 1px solid #ff6; +} + +div.topic { + background-color: #eee; +} + +div.caution { + background-color: {{ theme_cautionbgcolor }}; + border: 1px solid #aabb88; +} + +div.warning { + background-color: #ffe4e4; + border: 1px solid #f66; +} + +.warning tt { + background: #efc2c2; +} + +p.linklist { + text-align: center; +} + +.section:target > h2, +.section:target > h3, +.section:target > h4, +dt:target, +span.highlighted { + background-color: {{ theme_highlightbgcolor }}; +} + +pre { + background-color: {{ theme_codebgcolor }}; + border: 1px solid #ac9; + border-left: none; + border-right: none; + color: #333333; + display: block; + font-family: monospace; + line-height: 120%; + margin-bottom: 1em; + padding: 5px 5px 5px 15px; + text-align: justify; +} + +div.highlight pre { + border: none; +} + +ul.simple { + list-style-type: circle; + margin-bottom: 1em; +} + +code.descclassname, code.descname { + font-size: 1.3em; + font-weight: bold; +} + +/* + Top level section title format + section tag has been introduced in docutils 0.17 as a replacement for div.section + Both rule variations are kept to support old versions of docutils + */ +div.body > section > dl > dt.title, div.body > div.section > dl > dt.title { + font-size: 120%; + font-weight: bold; + margin-bottom: 1em; +} + +/* Visually separate an element from previous content in a section */ +div.body .leading { + margin-top: 0.5em; +} + +/* Visually separate an element from following content in a section */ +div.body .trailing { + margin-bottom: 0.75em; +} + +/* An element that spans the width of its section */ +div.body div.fullwidth div, +div.body div.fullwidth img { + display: block; + margin-left: auto; + margin-right: auto; +} + +div.body div.fullwidth p { + text-align: center; +} + +/* A two column table with link, description pairs. */ +table.more-to-explore, +table.more-to-explore th, +table.more-to-explore td { + border: 0.2em double black; + border-collapse: collapse; + background-color: inherit; +} + +table.more-to-explore caption { + text-align: left; + font-size: larger; + background-color: inherit; +} + +table.more-to-explore th, +table.more-to-explore td { + padding: 0.2em 2em 0.3em 0.5em; +} + +div.body p.small-heading { + margin-bottom: 0.2em; + font-size: small; +} + +/* Inlined element float right */ +div.body div.inlined, +div.body img.inlined-right { + float: right; + margin: 1em 0; +} + +div.body .inset { + margin-left: 2em; +} + +/* Code line reference text style */ +div.body span.codelineref { + text-decoration: underline; +} + +/* Highlight Code types and functions. */ +.py-class .pre, +.reference.internal em { + font-weight: bold; + background-color: {{ theme_keywordbgcolor }}; +} + +span.linenos { + margin-right: 15px; +} + +/* Examples section: contains one or more example subsections */ +div.examples > div.example { + clear: both; + margin-top: 0.2em; + border: 0.1em solid teal; + padding: 0.2em; +} + +div.example img { + float: left; + padding-right: 0.3em; + padding-bottom: 0.1em; +} + +div.example p, +div.example pre { + margin-top: 0; + margin-bottom: 0.4em; +} + +div.example div.break { + clear: both; + height: 0; +} + +/* -- tooltip style --------------------------------------------------------- */ + +a.tooltip { + text-decoration: underline; +} + +a.tooltip:hover { + text-decoration: none; +} + +.tooltip-content { + width: 250px; + background-color: {{ theme_tooltipbgcolor }}; + border: 1px solid {{ theme_tooltipbdrcolor }}; + padding: 3px; + border-radius: 5px 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.1); + -webkit-box-shadow: 5px 5px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 5px 5px rgba(0, 0, 0, 0.1); +} + +/* -- comments style --------------------------------------------------------- */ + +form.addcomment { + display:inline; +} + +.addcomment input, +a.commentButton { + background-color: {{ theme_examplebgcolor }}; + border: 1px solid #000000; + font-family: Arial,Helvetica,sans-serif; + font-size: 12px; + font-weight: bold; + padding: 6px; + margin-right: 4px; +} + +article.commentsArticle { + background: none repeat scroll 0 0 #FEF790; + border: 1px solid black; + margin-bottom: 12px; + margin-top: 12px; + padding: 4px; + +} + +article.hidden { + display:none; +} + +header.commentHeading { + background: none repeat scroll 0 0 #FDE42D; + text-align: center; +} + +pre.commentContent { + overflow: auto; + max-width: 800px; + margin-left:0; + border: 0; + white-space: pre-wrap; +} + +/* -- logos page ---------------------------------------------------------------- */ + +.fullwidth .line-block { + text-align: center; +} + +/* -- responsive design --------------------------------------------------------- */ + +@media only screen and (max-width: 680px) { + /* Make tables be responsive-ish. */ + table, thead, tbody, th, td, tr { + display: block; + } + + div.body img.inlined-right { + float: none; + display: block; + margin: auto + } + + span.linenos { + margin-right: 10px; + } + + .addcomment input, a.commentButton { + font-size: 10px; + } + + pre { + white-space: pre-wrap; + word-wrap: break-word; + } + + .toc td { + width: 100%; + } +} diff --git a/docs/themes/classic/theme.conf b/docs/themes/classic/theme.conf new file mode 100644 index 0000000..e68d25b --- /dev/null +++ b/docs/themes/classic/theme.conf @@ -0,0 +1,46 @@ +[theme] +inherit = basic +stylesheet = pygame.css +pygments_style = sphinx + +[options] +home_uri = '' +embedded = true +nosidebar = true +nofooter = true +relbar = false +noheader = false +noheading = false +notableofcontents = false + +footerbgcolor = #11303d +footertextcolor = #000000 +sidebarbgcolor = #1c4e63 +sidebartextcolor = #ffffff +sidebarlinkcolor = #98dbcc +relbarbgcolor = #6aee28 +relbartextcolor = #000000 +relbarlinkcolor = #000000 +bgcolor = #aaeebb +textcolor = #000000 +headbgcolor = #f2f2f2 +headtextcolor = #20435c +headlinkcolor = #68698b +linkcolor = #000000 +codebgcolor = #eeffcc +codetextcolor = #333333 +cautionbgcolor = #eeffcc +notebgcolor = #eeeeee +highlightbgcolor = #c7c695 +headerbgcolor = #6aee28 +logobgcolor = #c2fc20 +keywordbgcolor = #90ee90 +examplebgcolor = #6aee28 +tooltipbgcolor = #c2fc20 +tooltipbdrcolor = #ace01C + +headerborder = 3px solid black + +bodyfont = sans-serif +headfont = sans-serif +sigfont = monospace