Add docs, closes #3
This commit is contained in:
parent
5ece50a3e1
commit
5cd5548d46
|
@ -22,11 +22,16 @@ flake8:
|
||||||
script:
|
script:
|
||||||
- flake8
|
- flake8
|
||||||
|
|
||||||
|
doc8:
|
||||||
|
stage: test
|
||||||
|
script:
|
||||||
|
- doc8
|
||||||
|
|
||||||
deploy-pypi:
|
deploy-pypi:
|
||||||
stage: deploy
|
stage: deploy
|
||||||
when: manual
|
when: manual
|
||||||
only:
|
only:
|
||||||
- master@Lucidiot/twtxt-registry-client
|
- tags@Lucidiot/twtxt-registry-client
|
||||||
environment:
|
environment:
|
||||||
name: pypi
|
name: pypi
|
||||||
url: https://pypi.org/project/twtxt-registry-client
|
url: https://pypi.org/project/twtxt-registry-client
|
||||||
|
@ -47,7 +52,7 @@ deploy-testpypi:
|
||||||
stage: deploy
|
stage: deploy
|
||||||
when: manual
|
when: manual
|
||||||
only:
|
only:
|
||||||
- branches@Lucidiot/twtxt-registry-client
|
- tags@Lucidiot/twtxt-registry-client
|
||||||
environment:
|
environment:
|
||||||
name: testpypi
|
name: testpypi
|
||||||
url: https://test.pypi.org/project/twtxt-registry-client
|
url: https://test.pypi.org/project/twtxt-registry-client
|
||||||
|
@ -63,3 +68,17 @@ deploy-testpypi:
|
||||||
- echo "password=$PYPI_DEPLOY_PASSWORD" >> ~/.pypirc
|
- echo "password=$PYPI_DEPLOY_PASSWORD" >> ~/.pypirc
|
||||||
- python setup.py sdist bdist_wheel
|
- python setup.py sdist bdist_wheel
|
||||||
- twine upload dist/* -r testpypi
|
- twine upload dist/* -r testpypi
|
||||||
|
|
||||||
|
pages:
|
||||||
|
stage: deploy
|
||||||
|
when: manual
|
||||||
|
only:
|
||||||
|
- master@Lucidiot/twtxt-registry-client
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- public
|
||||||
|
|
||||||
|
script:
|
||||||
|
- cd docs
|
||||||
|
- make html
|
||||||
|
- mv _build/html ../public
|
||||||
|
|
15
README.rst
15
README.rst
|
@ -2,19 +2,8 @@ twtxt-registry-client
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
A simple API client for servers implementing `twtxt`_'s `registry`_ API.
|
A simple API client for servers implementing `twtxt`_'s `registry`_ API.
|
||||||
|
`Browse documentation`_
|
||||||
|
|
||||||
.. _twtxt: https://github.com/buckket/twtxt
|
.. _twtxt: https://github.com/buckket/twtxt
|
||||||
.. _registry: https://twtxt.readthedocs.io/en/stable/user/registry.html
|
.. _registry: https://twtxt.readthedocs.io/en/stable/user/registry.html
|
||||||
|
.. _Browse documentation: https://lucidiot.gitlab.io/twtxt-registry-client
|
||||||
To-do
|
|
||||||
-----
|
|
||||||
|
|
||||||
* Error handling
|
|
||||||
* Command-line help
|
|
||||||
* Sphinx documentation
|
|
||||||
|
|
||||||
* Doc8 linting
|
|
||||||
* GitLab Pages
|
|
||||||
|
|
||||||
* Parsing, enhanced outputs and porcelain mode, just like with `twtxt`_
|
|
||||||
* Verbose output and logging
|
|
||||||
|
|
|
@ -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 = .
|
||||||
|
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)
|
|
@ -0,0 +1,72 @@
|
||||||
|
from pathlib import Path
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Configuration file for the Sphinx documentation builder.
|
||||||
|
#
|
||||||
|
# This file only contains a selection of the most common options. For a full
|
||||||
|
# list see the documentation:
|
||||||
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||||
|
|
||||||
|
# -- 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.
|
||||||
|
#
|
||||||
|
BASE_DIR = Path(__file__).resolve().parent
|
||||||
|
sys.path.insert(0, BASE_DIR / '..')
|
||||||
|
|
||||||
|
|
||||||
|
# -- Project information -----------------------------------------------------
|
||||||
|
|
||||||
|
project = 'twtxt-registry-client'
|
||||||
|
copyright = '2019, Lucidiot'
|
||||||
|
author = 'Lucidiot'
|
||||||
|
|
||||||
|
# The full version, including alpha/beta/rc tags
|
||||||
|
with (BASE_DIR / '..' / 'VERSION').open() as f:
|
||||||
|
release = f.read().strip()
|
||||||
|
|
||||||
|
|
||||||
|
# -- General configuration ---------------------------------------------------
|
||||||
|
|
||||||
|
# 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.coverage',
|
||||||
|
'sphinx.ext.viewcode',
|
||||||
|
]
|
||||||
|
|
||||||
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
|
templates_path = ['_templates']
|
||||||
|
|
||||||
|
# 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', 'Thumbs.db', '.DS_Store']
|
||||||
|
|
||||||
|
|
||||||
|
# -- 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'
|
||||||
|
html_theme_options = {
|
||||||
|
'description': 'API client and CLI for twtxt registries',
|
||||||
|
'fixed_sidebar': 'true',
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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']
|
||||||
|
|
||||||
|
|
||||||
|
# -- Extension configuration -------------------------------------------------
|
||||||
|
|
||||||
|
# Concatenate the class' and __init__'s docstrings when documenting a class
|
||||||
|
autoclass_content = 'both'
|
|
@ -0,0 +1,55 @@
|
||||||
|
Contributing
|
||||||
|
============
|
||||||
|
|
||||||
|
Contributions to the project are greatly appreciated.
|
||||||
|
|
||||||
|
Bugs and suggestions
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
You may `submit an issue`_ to GitLab to warn of any bugs, ask for new features,
|
||||||
|
or ask any questions that are not answered in this documentation.
|
||||||
|
|
||||||
|
When reporting a bug, do not forget to put in your version of Python and your
|
||||||
|
version of *twtxt-registry-client*. This will greatly help when
|
||||||
|
troubleshooting, as most errors often come from version incompatibilities.
|
||||||
|
|
||||||
|
Development
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Setup
|
||||||
|
^^^^^
|
||||||
|
|
||||||
|
You will need a virtual envionment to work properly. `virtualenvwrapper`_ is
|
||||||
|
recommended::
|
||||||
|
|
||||||
|
git clone https://gitlab.com/Lucidiot/twtxt-registry-client
|
||||||
|
cd twtxt-registry-client
|
||||||
|
mkvirtualenv -a . twtxt-registry-client
|
||||||
|
pip install -e .[dev]
|
||||||
|
|
||||||
|
This will clone the repository, create a virtual environment named
|
||||||
|
``twtxt-registry-client``, then tell pip to let the package be editable
|
||||||
|
(``-e``). The ``[dev]`` suffix adds the extra requirements useful for
|
||||||
|
development.
|
||||||
|
|
||||||
|
Linting
|
||||||
|
^^^^^^^
|
||||||
|
|
||||||
|
The source code follows the PEP 8 code style and performs CI checks using the
|
||||||
|
``flake8`` tool. To perform the same checks locally, run ``flake8`` on the root
|
||||||
|
directory of this repository.
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
-------------
|
||||||
|
|
||||||
|
The documentation you are reading is generated by the `Sphinx`_ tool.
|
||||||
|
The text files that hold the documentation's contents are written in
|
||||||
|
`reStructuredText`_ and are available under the ``/docs`` folder of the
|
||||||
|
`GitLab repository`_.
|
||||||
|
They are also subject to linting using the ``doc8`` tool.
|
||||||
|
|
||||||
|
.. _submit an issue: https://gitlab.com/Lucidiot/twtxt-registry-client/issues/new
|
||||||
|
.. _virtualenvwrapper: https://virtualenvwrapper.readthedocs.io
|
||||||
|
.. _GitLab repository: https://gitlab.com/Lucidiot/twtxt-registry-client
|
||||||
|
.. _Sphinx: http://www.sphinx-doc.org/
|
||||||
|
.. _reStructuredText: http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html
|
|
@ -0,0 +1,211 @@
|
||||||
|
twtxt-registry-client
|
||||||
|
=====================
|
||||||
|
|
||||||
|
:ref:`genindex` - :ref:`modindex` - :ref:`search`
|
||||||
|
|
||||||
|
.. image:: https://img.shields.io/pypi/v/twtxt-registry-client.svg
|
||||||
|
:target: https://pypi.org/project/twtxt-registry-client
|
||||||
|
|
||||||
|
.. image:: https://img.shields.io/pypi/l/twtxt-registry-client.svg
|
||||||
|
:target: https://pypi.org/project/twtxt-registry-client
|
||||||
|
|
||||||
|
.. image:: https://img.shields.io/pypi/format/twtxt-registry-client.svg
|
||||||
|
:target: https://pypi.org/project/twtxt-registry-client
|
||||||
|
|
||||||
|
.. image:: https://img.shields.io/pypi/pyversions/twtxt-registry-client.svg
|
||||||
|
:target: https://pypi.org/project/twtxt-registry-client
|
||||||
|
|
||||||
|
.. image:: https://img.shields.io/pypi/status/twtxt-registry-client.svg
|
||||||
|
:target: https://pypi.org/project/twtxt-registry-client
|
||||||
|
|
||||||
|
.. image:: https://gitlab.com/Lucidiot/twtxt-registry-client/badges/master/pipeline.svg
|
||||||
|
:target: https://gitlab.com/Lucidiot/twtxt-registry-client/pipelines
|
||||||
|
|
||||||
|
.. image:: https://requires.io/github/Lucidiot/twtxt-registry-client/requirements.svg?branch=master
|
||||||
|
:target: https://requires.io/github/Lucidiot/twtxt-registry-client/requirements/?branch=master
|
||||||
|
|
||||||
|
.. image:: https://img.shields.io/github/last-commit/Lucidiot/twtxt-registry-client.svg
|
||||||
|
:target: https://gitlab.com/Lucidiot/twtxt-registry-client/commits
|
||||||
|
|
||||||
|
.. image:: https://img.shields.io/badge/badge%20count-9-brightgreen.svg
|
||||||
|
:target: https://gitlab.com/Lucidiot/twtxt-registry-client
|
||||||
|
|
||||||
|
A Python CLI for the twtxt `registry API`_.
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
This package has a very standard Python setup::
|
||||||
|
|
||||||
|
pip install twtxt_registry_client
|
||||||
|
|
||||||
|
That's it, nothing more.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
Base arguments
|
||||||
|
^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
$ twtxt-registry
|
||||||
|
[--version]
|
||||||
|
[--help]
|
||||||
|
[-k|-insecure]
|
||||||
|
[-f [raw|json|pretty]]
|
||||||
|
REGISTRY_URL
|
||||||
|
COMMAND
|
||||||
|
[subcommand args]
|
||||||
|
|
||||||
|
``--version``
|
||||||
|
Output the CLI's version number and exit.
|
||||||
|
``--help``
|
||||||
|
Output the main help text and exit.
|
||||||
|
``-k`` / ``--insecure``
|
||||||
|
Disable SSL certificate checks; first added for the `twtxt demo registry`_
|
||||||
|
as it is appears to be unmaintained and has an expired certificate.
|
||||||
|
``-f`` / ``--format`` with one of ``raw``, ``json`` or ``pretty``
|
||||||
|
Use a specific :class:`Formatter <twtxt_registry_client.output.Formatter>`
|
||||||
|
class to output the HTTP responses.
|
||||||
|
``REGISTRY_URL``
|
||||||
|
Base URL to a twtxt registry's API.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The recommended base path for registry APIs is at ``http://host/api``,
|
||||||
|
but the `registry API`_ specification does not enforce it. Therefore,
|
||||||
|
on most registries, you will need to append ``/api`` to the hostname.
|
||||||
|
|
||||||
|
``COMMAND [subcommand args]``
|
||||||
|
A client subcommand; see the subcommand-specific sections below.
|
||||||
|
|
||||||
|
Registration
|
||||||
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
$ twtxt-registry
|
||||||
|
[...base arguments...]
|
||||||
|
register
|
||||||
|
[--help]
|
||||||
|
[-n|--nickname [NICK]]
|
||||||
|
[-u|--url [URL]]
|
||||||
|
|
||||||
|
``--help``
|
||||||
|
Output the subcommand help text and exit.
|
||||||
|
``-n [NICK]`` / ``--nickname [NICK]``
|
||||||
|
Set a custom nickname. If omitted, the CLI will try to read it from the
|
||||||
|
``twtxt`` client's standard configuration; this may not work with other
|
||||||
|
twtxt implementations.
|
||||||
|
``-u [URL]`` / ``--url [URL]``
|
||||||
|
Set a custom public URL. If omitted, the CLI will try to read it from the
|
||||||
|
``twtxt`` client's standard configuration; this may not work with other
|
||||||
|
twtxt implementations.
|
||||||
|
|
||||||
|
This subcommand outputs the HTTP response directly, see
|
||||||
|
:meth:`Formatter.format_response()
|
||||||
|
<twtxt_registry_client.output.Formatter.format_response>`.
|
||||||
|
|
||||||
|
List users
|
||||||
|
^^^^^^^^^^
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
$ twtxt-registry
|
||||||
|
[...base arguments...]
|
||||||
|
users
|
||||||
|
[--help]
|
||||||
|
[-q|--query [TEXT]]
|
||||||
|
|
||||||
|
``--help``
|
||||||
|
Output the subcommand help text and exit.
|
||||||
|
``-q [TEXT]`` / ``--query [TEXT]``
|
||||||
|
Optionally filter users by a query.
|
||||||
|
|
||||||
|
This subcommand outputs the users list, see :meth:`Formatter.format_users()
|
||||||
|
<twtxt_registry_client.output.Formatter.format_users>`.
|
||||||
|
If the registry returns an HTTP 4xx or 5xx error code, the response is printed
|
||||||
|
directly. See :meth:`Formatter.format_response()
|
||||||
|
<twtxt_registry_client.output.Formatter.format_response>`.
|
||||||
|
|
||||||
|
List tweets
|
||||||
|
^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
$ twtxt-registry
|
||||||
|
[...base arguments...]
|
||||||
|
tweets
|
||||||
|
[--help]
|
||||||
|
[-q|--query [TEXT]]
|
||||||
|
|
||||||
|
``--help``
|
||||||
|
Output the subcommand help text and exit.
|
||||||
|
``-q [TEXT]`` / ``--query [TEXT]``
|
||||||
|
Optionally filter tweets by a query.
|
||||||
|
|
||||||
|
This subcommand outputs the tweets list, see :meth:`Formatter.format_tweets()
|
||||||
|
<twtxt_registry_client.output.Formatter.format_tweets>`.
|
||||||
|
If the registry returns an HTTP 4xx or 5xx error code, the response is printed
|
||||||
|
directly. See :meth:`Formatter.format_response()
|
||||||
|
<twtxt_registry_client.output.Formatter.format_response>`.
|
||||||
|
|
||||||
|
List tweets by mention
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
$ twtxt-registry
|
||||||
|
[...base arguments...]
|
||||||
|
mentions
|
||||||
|
[--help]
|
||||||
|
[NAME_OR_URL]
|
||||||
|
|
||||||
|
``--help``
|
||||||
|
Output the subcommand help text and exit.
|
||||||
|
``NAME_OR_URL``
|
||||||
|
Name or URL of a user to list mentions to.
|
||||||
|
|
||||||
|
If a name is specified, the CLI will try to deduce its URL from the
|
||||||
|
``twtxt`` client's configuration. If nothing is specified, the CLI will
|
||||||
|
use the configured public URL to default to the local user.
|
||||||
|
This may not work on other twtxt implementations.
|
||||||
|
|
||||||
|
This subcommand outputs the tweets list, see :meth:`Formatter.format_tweets()
|
||||||
|
<twtxt_registry_client.output.Formatter.format_tweets>`.
|
||||||
|
If the registry returns an HTTP 4xx or 5xx error code, the response is printed
|
||||||
|
directly. See :meth:`Formatter.format_response()
|
||||||
|
<twtxt_registry_client.output.Formatter.format_response>`.
|
||||||
|
|
||||||
|
List tweets by tag
|
||||||
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
$ twtxt-registry
|
||||||
|
[...base arguments...]
|
||||||
|
tag
|
||||||
|
[--help]
|
||||||
|
NAME
|
||||||
|
|
||||||
|
``--help``
|
||||||
|
Output the subcommand help text and exit.
|
||||||
|
``NAME``
|
||||||
|
Tag to list tweets for.
|
||||||
|
|
||||||
|
This subcommand outputs the tweets list, see :meth:`Formatter.format_tweets()
|
||||||
|
<twtxt_registry_client.output.Formatter.format_tweets>`.
|
||||||
|
If the registry returns an HTTP 4xx or 5xx error code, the response is printed
|
||||||
|
directly. See :meth:`Formatter.format_response()
|
||||||
|
<twtxt_registry_client.output.Formatter.format_response>`.
|
||||||
|
|
||||||
|
Learn more
|
||||||
|
----------
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
output
|
||||||
|
contributing
|
||||||
|
|
||||||
|
.. _registry API: https://twtxt.readthedocs.io/en/stable/user/registry.html
|
||||||
|
.. _twtxt demo registry: https://registry.twtxt.org
|
|
@ -0,0 +1,35 @@
|
||||||
|
@ECHO OFF
|
||||||
|
|
||||||
|
pushd %~dp0
|
||||||
|
|
||||||
|
REM Command file for Sphinx documentation
|
||||||
|
|
||||||
|
if "%SPHINXBUILD%" == "" (
|
||||||
|
set SPHINXBUILD=sphinx-build
|
||||||
|
)
|
||||||
|
set SOURCEDIR=.
|
||||||
|
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
|
|
@ -0,0 +1,29 @@
|
||||||
|
Formatters
|
||||||
|
==========
|
||||||
|
|
||||||
|
Base attributes
|
||||||
|
---------------
|
||||||
|
|
||||||
|
.. autoclass:: twtxt_registry_client.output.Formatter
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autodata:: twtxt_registry_client.output.registry
|
||||||
|
:annotation:
|
||||||
|
|
||||||
|
.. autoclass:: twtxt_registry_client.output.FormatterMetaclass
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: twtxt_registry_client.output.FormatterRegistry
|
||||||
|
:members:
|
||||||
|
|
||||||
|
Standard formatters
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
.. autoclass:: twtxt_registry_client.output.RawFormatter
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: twtxt_registry_client.output.JSONFormatter
|
||||||
|
:members:
|
||||||
|
|
||||||
|
.. autoclass:: twtxt_registry_client.output.PrettyFormatter
|
||||||
|
:members:
|
|
@ -1 +1,3 @@
|
||||||
flake8>=3.7
|
flake8>=3.7
|
||||||
|
Sphinx>=2.2
|
||||||
|
doc8>=0.8
|
||||||
|
|
|
@ -1,2 +1,5 @@
|
||||||
[flake8]
|
[flake8]
|
||||||
exclude = .git,__pycache__,docs,*.pyc,venv
|
exclude = .git,__pycache__,docs,*.pyc,venv
|
||||||
|
|
||||||
|
[doc8]
|
||||||
|
ignore-path=**/*.txt,*.txt,*.egg-info,docs/_build,venv,.git
|
||||||
|
|
|
@ -52,11 +52,13 @@ def cli(ctx, registry_url, insecure, format):
|
||||||
'-n', '--nickname',
|
'-n', '--nickname',
|
||||||
help='Nickname to register with. '
|
help='Nickname to register with. '
|
||||||
'Defaults to the configured twtxt nickname, if available.',
|
'Defaults to the configured twtxt nickname, if available.',
|
||||||
|
metavar='[NICK]',
|
||||||
)
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
'-u', '--url',
|
'-u', '--url',
|
||||||
help='URL to the twtxt file to register with. '
|
help='URL to the twtxt file to register with. '
|
||||||
'Defaults to the configured twtxt URL, if available.',
|
'Defaults to the configured twtxt URL, if available.',
|
||||||
|
metavar='[URL]',
|
||||||
)
|
)
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
def register(ctx, nickname, url):
|
def register(ctx, nickname, url):
|
||||||
|
|
|
@ -10,34 +10,113 @@ import textwrap
|
||||||
|
|
||||||
|
|
||||||
class FormatterRegistry(ClassRegistry):
|
class FormatterRegistry(ClassRegistry):
|
||||||
|
"""
|
||||||
|
The class that holds registered formatters; allows registering a formatter
|
||||||
|
automatically by merely importing it.
|
||||||
|
|
||||||
|
You should not have to use this class directly; use the already
|
||||||
|
instanciated :data:`registry` in this module instead.
|
||||||
|
"""
|
||||||
|
|
||||||
def check_value(self, value):
|
def check_value(self, value):
|
||||||
|
"""
|
||||||
|
Ensure that a new formatter class subclasses :class:`Formatter`.
|
||||||
|
|
||||||
|
:param Callable value: A formatter subclass.
|
||||||
|
:raises AssertionError:
|
||||||
|
When the formatter subclass does not subclass :class:`Formatter`.
|
||||||
|
"""
|
||||||
assert issubclass(value, Formatter), 'Can only register formatters'
|
assert issubclass(value, Formatter), 'Can only register formatters'
|
||||||
|
|
||||||
|
|
||||||
registry = FormatterRegistry()
|
registry = FormatterRegistry()
|
||||||
|
"""
|
||||||
|
The formatter registry: an enhanced ``dict`` which holds links between
|
||||||
|
formatter names (used in the ``--format`` command-line argument) and formatter
|
||||||
|
classes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class FormatterMetaclass(registry.metaclass, ABCMeta):
|
class FormatterMetaclass(registry.metaclass, ABCMeta):
|
||||||
pass
|
"""
|
||||||
|
The metaclass which allows auto-registration of each formatter.
|
||||||
|
In most cases, you should not have to use this class directly;
|
||||||
|
use the :class:`Formatter` abstract class instead.
|
||||||
|
Registration of classes that do not subclass :class:`Formatter` will fail.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Formatter(metaclass=FormatterMetaclass, register=False):
|
class Formatter(metaclass=FormatterMetaclass, register=False):
|
||||||
|
"""
|
||||||
|
Abstract base class for output formatters.
|
||||||
|
|
||||||
|
When creating a new subclass, you may specify some parameters to pass to
|
||||||
|
the auto-registration system::
|
||||||
|
|
||||||
|
class MyFormatter(key='something'):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class MyInvisibleFormatter(register=False):
|
||||||
|
pass
|
||||||
|
|
||||||
|
In the above example, ``MyFormatter`` can be used in the command line
|
||||||
|
client using ``twtxt-registry -f something``, and MyInvisibleFormatter
|
||||||
|
will not be visible directly (which is useful for abstract classes).
|
||||||
|
|
||||||
|
``register`` defaults to ``True``, and ``key`` defaults to the class name.
|
||||||
|
"""
|
||||||
|
# TODO: Add link to objtools docs here once they are published
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def format_response(self, resp):
|
def format_response(self, resp):
|
||||||
pass
|
"""
|
||||||
|
Generic output for an HTTP response: generally, this would include
|
||||||
|
the HTTP status code and the response body. This is used to output
|
||||||
|
HTTP errors or basic requests which do not have a very meaningful
|
||||||
|
response body, like the registration API.
|
||||||
|
|
||||||
|
:param resp:
|
||||||
|
A requests ``Response`` instance from an API request to a registry.
|
||||||
|
:type resp: requests.Response
|
||||||
|
:returns: A string holding the formatter's output.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def format_tweets(self, resp):
|
def format_tweets(self, resp):
|
||||||
pass
|
"""
|
||||||
|
Output tweets from a successful HTTP response. The tweets can be
|
||||||
|
obtained from ``resp.text`` and parsing of the response text is left
|
||||||
|
to the formatter.
|
||||||
|
|
||||||
|
:param resp:
|
||||||
|
A requests ``Response`` instance from an API request to a registry.
|
||||||
|
:type resp: requests.Response
|
||||||
|
:returns: A string holding the formatter's output.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def format_users(self, resp):
|
def format_users(self, resp):
|
||||||
pass
|
"""
|
||||||
|
Output users from a successful HTTP response. The users can be obtained
|
||||||
|
from ``resp.text`` and parsing of the response text is left to the
|
||||||
|
formatter.
|
||||||
|
|
||||||
|
:param resp:
|
||||||
|
A requests ``Response`` instance from an API request to a registry.
|
||||||
|
:type resp: requests.Response
|
||||||
|
:returns: A string holding the formatter's output.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class RawFormatter(Formatter, key='raw'):
|
class RawFormatter(Formatter, key='raw'):
|
||||||
|
"""
|
||||||
|
A very basic formatter which always outputs the response's body directly.
|
||||||
|
|
||||||
|
Use ``-f raw`` or ``--format raw`` in the CLI to select it.
|
||||||
|
"""
|
||||||
|
|
||||||
def format_response(self, resp):
|
def format_response(self, resp):
|
||||||
return resp.text
|
return resp.text
|
||||||
|
@ -50,8 +129,30 @@ class RawFormatter(Formatter, key='raw'):
|
||||||
|
|
||||||
|
|
||||||
class JSONFormatter(Formatter, key='json'):
|
class JSONFormatter(Formatter, key='json'):
|
||||||
|
"""
|
||||||
|
A formatter which always returns valid JSON documents.
|
||||||
|
|
||||||
|
Use ``-f json`` or ``--format json`` in the CLI to select it.
|
||||||
|
"""
|
||||||
|
|
||||||
def format_response(self, resp):
|
def format_response(self, resp):
|
||||||
|
"""
|
||||||
|
Outputs a simple JSON payload for any HTTP response, including its
|
||||||
|
HTTP status code, its URL and its body.
|
||||||
|
Sample output with a 404 error::
|
||||||
|
|
||||||
|
{
|
||||||
|
"status_code": 404,
|
||||||
|
"url": "http://somewhere/api/not/found",
|
||||||
|
"body": "Page Not Found!"
|
||||||
|
}
|
||||||
|
|
||||||
|
:param resp:
|
||||||
|
A requests ``Response`` instance from an API request to a registry.
|
||||||
|
:type resp: requests.Response
|
||||||
|
:returns: A string holding the JSON output.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
return json.dumps({
|
return json.dumps({
|
||||||
'status_code': resp.status_code,
|
'status_code': resp.status_code,
|
||||||
'url': resp.url,
|
'url': resp.url,
|
||||||
|
@ -59,6 +160,26 @@ class JSONFormatter(Formatter, key='json'):
|
||||||
})
|
})
|
||||||
|
|
||||||
def format_tweets(self, resp):
|
def format_tweets(self, resp):
|
||||||
|
"""
|
||||||
|
Outputs a list of JSON objects for an HTTP response holding tweets,
|
||||||
|
with the users' nickname and URL, the tweet's timestamp, and its
|
||||||
|
content. Sample output::
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"nick": "lucidiot",
|
||||||
|
"url": "https://tilde.town/~lucidiot/twtxt.txt",
|
||||||
|
"timestamp": "2019-02-31T13:37:42.123456Z",
|
||||||
|
"message": "Hello, world!"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
:param resp:
|
||||||
|
A requests ``Response`` instance from an API request to a registry.
|
||||||
|
:type resp: requests.Response
|
||||||
|
:returns: A string holding the JSON output.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
if not resp.ok:
|
if not resp.ok:
|
||||||
return self.format_response(resp)
|
return self.format_response(resp)
|
||||||
output = []
|
output = []
|
||||||
|
@ -73,6 +194,24 @@ class JSONFormatter(Formatter, key='json'):
|
||||||
return json.dumps(output)
|
return json.dumps(output)
|
||||||
|
|
||||||
def format_users(self, resp):
|
def format_users(self, resp):
|
||||||
|
"""
|
||||||
|
Outputs a list of JSON objects for an HTTP response holding users,
|
||||||
|
with their nickname, URL, and last update timestamp. Sample output::
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"nick": "lucidiot",
|
||||||
|
"url": "https://tilde.town/~lucidiot/twtxt.txt",
|
||||||
|
"timestamp": "2019-02-31T13:37:42.123456Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
:param resp:
|
||||||
|
A requests ``Response`` instance from an API request to a registry.
|
||||||
|
:type resp: requests.Response
|
||||||
|
:returns: A string holding the JSON output.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
if not resp.ok:
|
if not resp.ok:
|
||||||
return self.format_response(resp)
|
return self.format_response(resp)
|
||||||
output = []
|
output = []
|
||||||
|
@ -87,6 +226,12 @@ class JSONFormatter(Formatter, key='json'):
|
||||||
|
|
||||||
|
|
||||||
class PrettyFormatter(Formatter, key='pretty'):
|
class PrettyFormatter(Formatter, key='pretty'):
|
||||||
|
"""
|
||||||
|
A formatter with pretty-printing for beautiful command line output.
|
||||||
|
|
||||||
|
This is the default formatter; Use ``-f pretty`` or ``--format pretty``
|
||||||
|
in the CLI to explicitly select it.
|
||||||
|
"""
|
||||||
|
|
||||||
status_colors = {
|
status_colors = {
|
||||||
1: 'white',
|
1: 'white',
|
||||||
|
@ -97,6 +242,24 @@ class PrettyFormatter(Formatter, key='pretty'):
|
||||||
}
|
}
|
||||||
|
|
||||||
def format_response(self, resp):
|
def format_response(self, resp):
|
||||||
|
"""
|
||||||
|
Outputs an HTTP response in a syntax similar to a true HTTP response,
|
||||||
|
with its status code, reason and body:
|
||||||
|
|
||||||
|
HTTP **404 Not Found**
|
||||||
|
|
||||||
|
Page Not Found!
|
||||||
|
|
||||||
|
The HTTP status code may be coloured if the terminal supports it:
|
||||||
|
white for 1xx, green for 2xx, cyan for 3xx, red for 4xx and magenta
|
||||||
|
for 5xx.
|
||||||
|
|
||||||
|
:param resp:
|
||||||
|
A requests ``Response`` instance from an API request to a registry.
|
||||||
|
:type resp: requests.Response
|
||||||
|
:returns: A string holding the human-readable output.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
return 'HTTP {code} {name}\n\n{body}'.format(
|
return 'HTTP {code} {name}\n\n{body}'.format(
|
||||||
code=click.style(
|
code=click.style(
|
||||||
str(resp.status_code),
|
str(resp.status_code),
|
||||||
|
@ -108,6 +271,16 @@ class PrettyFormatter(Formatter, key='pretty'):
|
||||||
)
|
)
|
||||||
|
|
||||||
def format_tweets(self, resp):
|
def format_tweets(self, resp):
|
||||||
|
"""
|
||||||
|
Outputs an HTTP response as a list of tweets, in a format similar to
|
||||||
|
the output of the original ``twtxt`` CLI.
|
||||||
|
|
||||||
|
:param resp:
|
||||||
|
A requests ``Response`` instance from an API request to a registry.
|
||||||
|
:type resp: requests.Response
|
||||||
|
:returns: A string holding the human-readable output.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
if not resp.ok:
|
if not resp.ok:
|
||||||
return self.format_response(resp)
|
return self.format_response(resp)
|
||||||
|
|
||||||
|
@ -152,6 +325,16 @@ class PrettyFormatter(Formatter, key='pretty'):
|
||||||
return '\n\n'.join(output)
|
return '\n\n'.join(output)
|
||||||
|
|
||||||
def format_users(self, resp):
|
def format_users(self, resp):
|
||||||
|
"""
|
||||||
|
Outputs an HTTP response as a list of users, in a format similar to
|
||||||
|
the output of the original ``twtxt`` CLI.
|
||||||
|
|
||||||
|
:param resp:
|
||||||
|
A requests ``Response`` instance from an API request to a registry.
|
||||||
|
:type resp: requests.Response
|
||||||
|
:returns: A string holding the human-readable output.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
if not resp.ok:
|
if not resp.ok:
|
||||||
return self.format_response(resp)
|
return self.format_response(resp)
|
||||||
output = []
|
output = []
|
||||||
|
|
Reference in New Issue