From fa2d0fa95765d116c79ddfc9b289ef00f6e54927 Mon Sep 17 00:00:00 2001 From: Dmitriy Pleshevskiy Date: Wed, 24 Jul 2019 17:05:09 +0300 Subject: [PATCH] feat: add docs --- Pipfile | 3 + Pipfile.lock | 129 +++++++++++++++++++++++++++++- docs/.nojekyll | 0 docs/Makefile | 23 ++++++ docs/conf.py | 180 ++++++++++++++++++++++++++++++++++++++++++ docs/index.rst | 22 ++++++ docs/make.bat | 35 ++++++++ docs/requirements.txt | 3 + genrss/__init__.py | 68 ++++++++++++++-- setup.py | 2 +- 10 files changed, 454 insertions(+), 11 deletions(-) create mode 100644 docs/.nojekyll create mode 100644 docs/Makefile create mode 100644 docs/conf.py create mode 100644 docs/index.rst create mode 100644 docs/make.bat create mode 100644 docs/requirements.txt diff --git a/Pipfile b/Pipfile index 5829a13..cbe56e7 100644 --- a/Pipfile +++ b/Pipfile @@ -7,6 +7,9 @@ verify_ssl = true pytest = "*" coverage = "*" coveralls = "*" +docutils = "*" +Sphinx = "*" +sphinx-autodoc-typehints = "*" [packages] lxml = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 3c9b0ea..92dc0a2 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "f0911fe39e4694db48dedc159ec16cbf28529b922b8b1ec5fdbdfd67d70cdcf3" + "sha256": "71e76379da4946d895b9c0c272ef7eac1a54a5c13bcd5d9d719285d66ed27940" }, "pipfile-spec": 6, "requires": { @@ -56,6 +56,13 @@ } }, "develop": { + "alabaster": { + "hashes": [ + "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359", + "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02" + ], + "version": "==0.7.12" + }, "asn1crypto": { "hashes": [ "sha256:2f1adbb7546ed199e3c90ef23ec95c5cf3585bac7d11fb7eb562a3fe89c64e87", @@ -77,6 +84,13 @@ ], "version": "==19.1.0" }, + "babel": { + "hashes": [ + "sha256:af92e6106cb7c55286b25b38ad7695f8b4efb36a90ba483d7f7a6628c46158ab", + "sha256:e86135ae101e31e2c8ec20a4e0c5220f4eed12487d5cf3f78be7e98d3a57fc28" + ], + "version": "==2.7.0" + }, "certifi": { "hashes": [ "sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939", @@ -212,6 +226,13 @@ ], "version": "==0.6.2" }, + "docutils": { + "hashes": [ + "sha256:ba4584f9107571ced0d2c7f56a5499c696215ba90797849c92d395979da68521" + ], + "index": "pypi", + "version": "==0.15.post1" + }, "enum34": { "hashes": [ "sha256:2d81cbbe0e73112bdfe6ef8576f2238f2ba27dd0d55752a776c41d38b7da2850", @@ -237,6 +258,13 @@ ], "version": "==2.8" }, + "imagesize": { + "hashes": [ + "sha256:3f349de3eb99145973fefb7dbe38554414e5c30abd0c8e4b970a7c9d09f3a1d8", + "sha256:f3832918bc3c66617f92e35f5d70729187676313caa60c187eb0f28b8fe5e3b5" + ], + "version": "==1.1.0" + }, "importlib-metadata": { "hashes": [ "sha256:6dfd58dfe281e8d240937776065dd3624ad5469c835248219bd16cf2e12dbeb7", @@ -251,6 +279,46 @@ ], "version": "==1.0.22" }, + "jinja2": { + "hashes": [ + "sha256:065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013", + "sha256:14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b" + ], + "version": "==2.10.1" + }, + "markupsafe": { + "hashes": [ + "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", + "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", + "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", + "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", + "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", + "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", + "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", + "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", + "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", + "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", + "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", + "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", + "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", + "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", + "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", + "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", + "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", + "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", + "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", + "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", + "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", + "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", + "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", + "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", + "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", + "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", + "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", + "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7" + ], + "version": "==1.1.1" + }, "more-itertools": { "hashes": [ "sha256:38a936c0a6d98a38bcc2d03fdaaedaba9f412879461dd2ceff8d37564d6522e4", @@ -295,6 +363,13 @@ ], "version": "==2.19" }, + "pygments": { + "hashes": [ + "sha256:71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127", + "sha256:881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297" + ], + "version": "==2.4.2" + }, "pyopenssl": { "hashes": [ "sha256:aeca66338f6de19d1aa46ed634c3b9ae519a64b458f8468aec688e7e3c20f200", @@ -304,10 +379,10 @@ }, "pyparsing": { "hashes": [ - "sha256:530d8bf8cc93a34019d08142593cf4d78a05c890da8cf87ffa3120af53772238", - "sha256:f78e99616b6f1a4745c0580e170251ef1bbafc0d0513e270c4bd281bf29d2800" + "sha256:1873c03321fc118f4e9746baf201ff990ceb915f433f23b395f5580d1840cb2a", + "sha256:9b6323ef4ab914af344ba97510e966d64ba91055d6b9afa6b30799340e89cc03" ], - "version": "==2.4.1" + "version": "==2.4.0" }, "pytest": { "hashes": [ @@ -317,6 +392,14 @@ "index": "pypi", "version": "==4.6.4" }, + "pytz": { + "hashes": [ + "sha256:303879e36b721603cc54604edcac9d20401bdbe31e1e4fdee5b9f98d5d31dfda", + "sha256:d747dd3d23d77ef44c6a3526e274af6efeb0a6f1afd5a69ba4d5be4098c8e141" + ], + "index": "pypi", + "version": "==2019.1" + }, "requests": { "hashes": [ "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", @@ -348,6 +431,44 @@ ], "version": "==1.12.0" }, + "snowballstemmer": { + "hashes": [ + "sha256:9f3b9ffe0809d174f7047e121431acf99c89a7040f0ca84f94ba53a498e6d0c9" + ], + "version": "==1.9.0" + }, + "sphinx": { + "hashes": [ + "sha256:9f3e17c64b34afc653d7c5ec95766e03043cc6d80b0de224f59b6b6e19d37c3c", + "sha256:c7658aab75c920288a8cf6f09f244c6cfdae30d82d803ac1634d9f223a80ca08" + ], + "index": "pypi", + "version": "==1.8.5" + }, + "sphinx-autodoc-typehints": { + "hashes": [ + "sha256:19fe0b426b7c008181f67f816060da7f046bd8a42723f67a685d26d875bcefd7", + "sha256:f9c06acfec80766fe8f542a6d6a042e751fcf6ce2e2711a7dc00d8b6daf8aa36" + ], + "index": "pypi", + "version": "==1.6.0" + }, + "sphinxcontrib-websupport": { + "hashes": [ + "sha256:1501befb0fdf1d1c29a800fdbf4ef5dc5369377300ddbdd16d2cd40e54c6eefc", + "sha256:e02f717baf02d0b6c3dd62cf81232ffca4c9d5c331e03766982e3ff9f1d2bc3f" + ], + "version": "==1.1.2" + }, + "typing": { + "hashes": [ + "sha256:38566c558a0a94d6531012c8e917b1b8518a41e418f7f15f00e129cc80162ad3", + "sha256:53765ec4f83a2b720214727e319607879fec4acde22c4fbb54fa2604e79e44ce", + "sha256:84698954b4e6719e912ef9a42a2431407fe3755590831699debda6fba92aac55" + ], + "markers": "python_version < '3.5'", + "version": "==3.7.4" + }, "urllib3": { "extras": [ "secure" diff --git a/docs/.nojekyll b/docs/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..225fe6c --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,23 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +SOURCEDIR = . +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 + +github: + @make html + @cp -a _build/html/. . + +# 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) \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..a6286a8 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,180 @@ +# -*- coding: utf-8 -*- +# +# Configuration file for the Sphinx documentation builder. +# +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/master/config + +# -- 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('../')) + + +# -- Project information ----------------------------------------------------- + +project = 'GenRSS' +copyright = '2019, Dmitriy Pleshevskiy' +author = 'Dmitriy Pleshevskiy' + +# The short X.Y version +version = '1.0' +# The full version, including alpha/beta/rc tags +release = '1.0.0' + + +# -- General configuration --------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.doctest', + 'sphinx.ext.coverage', + 'sphinx_autodoc_typehints' +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ['_build'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = None + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'alabaster' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# The default sidebars (for documents that don't match any pattern) are +# defined by theme itself. Builtin themes are using these templates by +# default: ``['localtoc.html', 'relations.html', 'sourcelink.html', +# 'searchbox.html']``. +# +# html_sidebars = {} + + +# -- Options for HTMLHelp output --------------------------------------------- + +# Output file base name for HTML help builder. +htmlhelp_basename = 'GenRSSdoc' + + +# -- Options for LaTeX output ------------------------------------------------ + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'GenRSS.tex', 'GenRSS Documentation', + 'Dmitriy Pleshevskiy', 'manual'), +] + + +# -- Options for manual page output ------------------------------------------ + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'genrss', 'GenRSS Documentation', + [author], 1) +] + + +# -- Options for Texinfo output ---------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'GenRSS', 'GenRSS Documentation', + author, 'GenRSS', 'One line description of project.', + 'Miscellaneous'), +] + + +# -- Options for Epub output ------------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = project + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +# +# epub_identifier = '' + +# A unique identification for the text. +# +# epub_uid = '' + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ['search.html'] + + +# -- Extension configuration ------------------------------------------------- diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..5c472f1 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,22 @@ +.. GenRSS documentation master file, created by + sphinx-quickstart on Wed Jul 24 11:05:33 2019. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to GenRSS's documentation! +================================== + +.. automodule:: genrss + :members: + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..4d9eb83 --- /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% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% + +:end +popd diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..dcf1e32 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,3 @@ +docutils==0.15 +Sphinx==1.8.5 +sphinx-autodoc-typehints==1.6.0 diff --git a/genrss/__init__.py b/genrss/__init__.py index fdbb526..d6ea987 100644 --- a/genrss/__init__.py +++ b/genrss/__init__.py @@ -1,18 +1,30 @@ import mimetypes from lxml.etree import Element, CDATA, tostring -from typing import Optional, List, NoReturn +from typing import Optional, List, TypeVar, Dict, Any from datetime import datetime from collections import namedtuple import pytz +__all__ = ('GenRSS', 'Enclosure',) + +ElementT = TypeVar('ElementT') + Enclosure = namedtuple('Enclosure', ('url', 'size', 'type')) Enclosure.__new__.__defaults__ = (None, None, None) +Enclosure.__doc__ = 'Creates information for enclosure tag.' RSS_DEFAULT_GENERATOR = f'Generated by genrss for python' -def create_element(name: str, text=None, children=None, **kwargs) -> Element: +def create_element(name: str, text: Any = None, children: List[ElementT] = None, + **kwargs) -> ElementT: + """Creates xml node with text or children elements. + + :param name: Tag name of node with namespace + :param text: Text of node + :param children: Appends elements as child nodes + """ el = Element(name, **kwargs) if text: if isinstance(text, datetime): @@ -26,6 +38,22 @@ def create_element(name: str, text=None, children=None, **kwargs) -> Element: class GenRSS: + """Generates RSS feed of channel. + + :param title: Title of your site or feed + :param site_url: Absolute url to the site that the feed is for + :param feed_url: Absolute url to the rss feed + :param description: A short description of feed + :param image_url: Image absolute url for channel + :param author: Author of channel + :param pub_date: Datetime in utc when last item was published + :param copyright: Copyright information for this feed + :param language: The language of the content of this feed. + :param editor: Who manages content in this feed + :param webmaster: Who manages feed availability and technical support + :param generator: Feed generator + """ + def __init__(self, title: str, site_url: str, feed_url: str, **kwargs): self.title: str = title self.site_url: str = site_url @@ -42,13 +70,36 @@ class GenRSS: self.categories: List[str] = kwargs.pop('categories', []) self.items: List[Element] = [] - self.generator = kwargs.pop('generator', RSS_DEFAULT_GENERATOR) - self.root_version = '2.0' - self.root_nsmap = { + self.generator: str = kwargs.pop('generator', RSS_DEFAULT_GENERATOR) + self.root_version: str = '2.0' + self.root_nsmap: Dict[str, str] = { 'atom': 'http://www.w3.org/2005/Atom' } - def item(self, title: str, **kwargs) -> NoReturn: + def item(self, title: str, **kwargs): + """Adds item to the feed. + + An item can be used for recipes, blog entries, project update, log + entry, etc. Your RSS feed can have any number of items. + + :param title: Title of this particular item + :param description: Content for the item. Can contain html but + link and image urls must be absolute path including hostname + :param url: Url to the item. This could be a blog entry + :param guid: A unique string feed readers use to know if an item + is new or has already been seen. If you use a guid never change + it. If you don't provide a guid then your item urls must be unique + :param author: If included it is the name of the item's creator. + If not provided the item author will be the same as the feed + author. This is typical except on multi-author blogs + :param categories: If provided, each array item will be added as a + category element + :param enclosure: An enclosure object + :param pub_date: The date and time of when the item was created. + Feed readers use this to determine the sort order. Some readers + will also use it to determine if the content should be presented + as unread + """ description: str = kwargs.pop('description', '') url: Optional[str] = kwargs.pop('url', None) guid: Optional[str] = kwargs.pop('guid', None) @@ -97,6 +148,10 @@ class GenRSS: self.items.append(item) def xml(self, pretty: bool = False) -> str: + """Returns the XML as a string. + + :param pretty: Pretty print xml + """ root = Element('rss', nsmap=self.root_nsmap, version=self.root_version) channel = create_element('channel', children=[ create_element('title', CDATA(self.title)), @@ -136,4 +191,5 @@ class GenRSS: root.append(channel) return '\n' \ + + ('\n' if pretty else '') \ + tostring(root, pretty_print=pretty).decode('utf-8') diff --git a/setup.py b/setup.py index be27639..7b9b722 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ with open('README.md', 'r') as f: if __name__ == '__main__': setup( name='genrss', - version='1.0.0', + version='1.0.1', author='Dmitriy Pleshevskiy', author_email='dmitriy@ideascup.me', description='RSS feed generator for python',