diff --git a/docs/_templates/layout.html b/docs/_templates/layout.html
index b9c6754..8435f03 100644
--- a/docs/_templates/layout.html
+++ b/docs/_templates/layout.html
@@ -1,10 +1,10 @@
{% extends "!layout.html" %}
{% block extrahead %}
-
-
-
-
-
-
+
+
+
+
+
+
{{ super() }}
{% endblock %}
\ No newline at end of file
diff --git a/docs/conf.py b/docs/conf.py
index 086680a..2ddee8f 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -4,16 +4,18 @@
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
+from pkg_resources import get_distribution
+
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
-# import os
-# import sys
-# sys.path.insert(0, os.path.abspath('.'))
-from pkg_resources import get_distribution
+import os
+import sys
+
+sys.path.insert(0, os.path.abspath('..'))
# -- Project information -----------------------------------------------------
@@ -29,7 +31,10 @@ version = '.'.join(release.split('.')[:3])
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
- 'myst_parser', 'sphinx_rtd_theme'
+ 'myst_parser',
+ 'sphinx_rtd_theme',
+ 'sphinx.ext.autodoc',
+ 'sphinx.ext.intersphinx',
]
# Add any paths that contain templates here, relative to this directory.
@@ -40,6 +45,8 @@ templates_path = ['_templates']
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+default_role = 'py:obj'
+
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
@@ -54,3 +61,24 @@ html_favicon = 'favicon.ico'
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_css_files = ['css/custom.css']
+
+# -- Options for Autodoc -----------------------------------------------------
+
+add_module_names = False
+autodoc_docstring_signature = True
+autoclass_content = 'both'
+
+autodoc_default_options = {
+ 'member-order': 'bysource',
+ 'undoc-members': True,
+}
+
+# -- Options for Intersphinx -------------------------------------------------
+
+# This config value contains the locations and names of other projects that
+# should be linked to in this documentation.
+
+intersphinx_mapping = {
+ 'python': ('https://docs.python.org/3', None),
+ 'packaging': ('https://packaging.pypa.io/en/latest/', None),
+}
diff --git a/docs/index.rst b/docs/index.rst
index 609940d..edc6a6a 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -11,7 +11,7 @@ Welcome to Yaclog's documentation!
:includehidden:
:caption: Contents:
- Changelog
+ API Reference
.. toctree::
:maxdepth: 1
diff --git a/docs/reference/changelog.rst b/docs/reference/changelog.rst
new file mode 100644
index 0000000..2f66447
--- /dev/null
+++ b/docs/reference/changelog.rst
@@ -0,0 +1,5 @@
+Changelog Module
+================
+
+.. automodule:: yaclog.changelog
+ :members:
\ No newline at end of file
diff --git a/docs/reference/index.rst b/docs/reference/index.rst
new file mode 100644
index 0000000..c74a015
--- /dev/null
+++ b/docs/reference/index.rst
@@ -0,0 +1,8 @@
+API Reference
+=============
+
+.. toctree::
+ :maxdepth: 1
+ :glob:
+
+ *
\ No newline at end of file
diff --git a/docs/reference/markdown.rst b/docs/reference/markdown.rst
new file mode 100644
index 0000000..c8b48d2
--- /dev/null
+++ b/docs/reference/markdown.rst
@@ -0,0 +1,5 @@
+Markdown Module
+==============
+
+.. automodule:: yaclog.markdown
+ :members:
\ No newline at end of file
diff --git a/docs/reference/version.rst b/docs/reference/version.rst
new file mode 100644
index 0000000..c722d3e
--- /dev/null
+++ b/docs/reference/version.rst
@@ -0,0 +1,5 @@
+Version Module
+==============
+
+.. automodule:: yaclog.version
+ :members:
\ No newline at end of file
diff --git a/yaclog/changelog.py b/yaclog/changelog.py
index 56082d6..668aa7f 100644
--- a/yaclog/changelog.py
+++ b/yaclog/changelog.py
@@ -1,3 +1,8 @@
+"""
+Contains the `Changelog` class that represents a parsed changelog file that can be read from and written to
+disk as markdown, as well as the `VersionEntry` class that represents a single version within that changelog
+"""
+
# yaclog: yet another changelog tool
# Copyright (c) 2021. Andrew Cassidy
#
@@ -32,23 +37,21 @@ default_header = '# Changelog\n\nAll notable changes to this project will be doc
class VersionEntry:
"""Holds a single version entry in a :py:class:`Changelog`"""
- header_regex = re.compile( # THE LANGUAGE OF THE GODS
+ _header_regex = re.compile( # THE LANGUAGE OF THE GODS
r"##\s+(?P.*?)(?:\s+-)?(?:\s+(?P\d{4}-\d{2}-\d{2}))?(?P(?:\s+\[[^]]*?])*)\s*$")
- tag_regex = re.compile(r'\[(?P[^]]*?)]')
+ _tag_regex = re.compile(r'\[(?P[^]]*?)]')
def __init__(self, name: str = 'Unreleased',
date: Optional[datetime.date] = None, tags: Optional[List[str]] = None,
link: Optional[str] = None, link_id: Optional[str] = None, line_no: Optional[int] = None):
"""
- Create a new version entry
-
:param str name: The version's name
- :param date: When the version was released
+ :param Optional[datetime.date] date: When the version was released
:param tags: The version's tags
:param link: The version's URL
:param link_id: The version's link ID
- :param line_no What line in the original file the version starts on
+ :param line_no: What line in the original file the version starts on
"""
self.name: str = name
@@ -67,7 +70,7 @@ class VersionEntry:
"""The version's link ID, uses the version name by default when writing"""
self.line_no: Optional[int] = line_no
- """What line the version occurs at in the file, or None if the version was not read from a file.
+ """What line the version occurs at in the file, or `None` if the version was not read from a file.
This is not guaranteed to be correct after the changelog has been modified,
and it has no effect on the written file"""
@@ -86,7 +89,7 @@ class VersionEntry:
"""
version = cls(line_no=line_no)
- match = cls.header_regex.match(header)
+ match = cls._header_regex.match(header)
assert match, f'failed to parse version header: "{header}"'
version.name, version.link, version.link_id = markdown.strip_link(match['name'])
@@ -98,7 +101,7 @@ class VersionEntry:
return cls(name=header.lstrip('#').strip(), line_no=line_no)
if match['tags']:
- version.tags = [m['tag'].upper() for m in cls.tag_regex.finditer(match['tags'])]
+ version.tags = [m['tag'].upper() for m in cls._tag_regex.finditer(match['tags'])]
return version
@@ -200,11 +203,13 @@ class VersionEntry:
return contents
@property
- def released(self):
+ def released(self) -> bool:
+ """Returns true if a PEP440 version number is present in the version name, and has no prerelease segments"""
return yaclog.version.is_release(self.name)
@property
def version(self):
+ """Returns the PEP440 version number from the version name, or `None` if none is found"""
return yaclog.version.extract_version(self.name)[0]
def __str__(self) -> str:
@@ -216,7 +221,7 @@ class Changelog:
def __init__(self, path=None, header: str = default_header):
"""
- Create a new changelog object. Contents will be automatically read from disk if the file exists
+ Contents will be automatically read from disk if the file exists
:param path: The changelog's path on disk
:param header: The header at the top of the changelog to use if the file does not exist
@@ -230,7 +235,7 @@ class Changelog:
self.versions: List[VersionEntry] = []
"""A list of versions in the changelog"""
- self.links = {}
+ self.links: Dict[str, str] = {}
"""Link IDs at the end of the changelog"""
if path and os.path.exists(path):
@@ -339,12 +344,12 @@ class Changelog:
Get the current version from the changelog
:param released: if the returned version should be a released version,
- an unreleased version, or ``None`` to return the most recent
+ an unreleased version, or `None` to return the most recent
:param new_version: if a new version should be created if none exist.
:param new_version_name: The name of the version to create if there
are no matches and ``new_version`` is True.
:return: The current version matching the criteria,
- or None if ``new_version`` is disabled and none are found.
+ or `None` if ``new_version`` is disabled and none are found.
"""
# return the first version that matches `released`
@@ -365,7 +370,7 @@ class Changelog:
"""
Get a version from the changelog
- :param name: The name of the version to get, or ``None`` to return the most recent
+ :param name: The name of the version to get, or `None` to return the most recent
:return: The first version with the selected name
"""
diff --git a/yaclog/markdown.py b/yaclog/markdown.py
index d854490..9a6672f 100644
--- a/yaclog/markdown.py
+++ b/yaclog/markdown.py
@@ -1,3 +1,7 @@
+"""
+Tools for parsing and manipulating markdown, including a very basic tokenizer.
+"""
+
# yaclog: yet another changelog tool
# Copyright (c) 2021. Andrew Cassidy
#
@@ -13,6 +17,7 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
+
import re
from typing import List
@@ -31,23 +36,23 @@ setext_h1_replace_regex = re.compile(r'(?<=\n)(?P[^\n]+?)\n=+[ \t]*(?=\n
setext_h2_replace_regex = re.compile(r'(?<=\n)(?P[^\n]+?)\n-+[ \t]*(?=\n)')
-def strip_link(token):
+def strip_link(text):
"""
- Parses and removes any links from the token
+ Parses and removes any links from the input string
- :param token: An input token which may be a markdown link, either literal or an ID
- :return: A tuple of (name, url, id)
+ :param text: An input string which may be a markdown link, either literal or an ID
+ :return: A tuple of (name, url, id). If the input is not a link, it is returned verbatim as the name.
"""
- if link_lit := link_lit_regex.fullmatch(token):
+ if link_lit := link_lit_regex.fullmatch(text):
# in the form [name](link)
return link_lit['text'], link_lit['link'], None
- if link_def := link_def_regex.fullmatch(token):
+ if link_def := link_def_regex.fullmatch(text):
# in the form [name][id] where id is hopefully linked somewhere else in the document
return link_def['text'], None, link_def['link_id'].lower()
- return token, None, None
+ return text, None, None
def join(segments: List[str]) -> str:
@@ -76,10 +81,17 @@ def join(segments: List[str]) -> str:
class Token:
+ """A single tokenized block of markdown, consisting of one or more lines of text."""
+
def __init__(self, line_no: int, lines: List[str], kind: str):
self.line_no = line_no
+ """Which line this block appears on in the original file"""
+
self.lines = lines
+ """The lines of text making up this block"""
+
self.kind = kind
+ """What kind of token this is. One of ``h[1-6]``, ``p``, ``li`` or ``code``"""
def __str__(self):
return f'{self.kind}: {self.lines}'
@@ -93,7 +105,7 @@ def tokenize(text: str):
(Headers, top-level list items, links, code blocks, paragraphs).
:param text: input text to tokenize
- :return: A list of tokens
+ :return: A list of tokens and a dictionary of links
"""
# convert setext-style headers
diff --git a/yaclog/version.py b/yaclog/version.py
index 0b87bb0..9439995 100644
--- a/yaclog/version.py
+++ b/yaclog/version.py
@@ -1,3 +1,8 @@
+"""
+Various helper functions for analyzing and manipulating PEP440 version numbers,
+meant to augment the `packaging.version` module.
+"""
+
# yaclog: yet another changelog tool
# Copyright (c) 2021. Andrew Cassidy
#