restructuredtext-lint ===================== .. image:: https://travis-ci.org/twolfson/restructuredtext-lint.png?branch=master :target: https://travis-ci.org/twolfson/restructuredtext-lint :alt: Build Status `reStructuredText`_ `linter`_ This was created out of frustration with `PyPI`_; it sucks finding out your `reST`_ is invalid **after** uploading it. It is being developed in junction with a `Sublime Text`_ linter. .. _`reStructuredText`: http://docutils.sourceforge.net/rst.html .. _`linter`: http://en.wikipedia.org/wiki/Lint_%28software%29 .. _`reST`: `reStructuredText`_ .. _`PyPI`: http://pypi.python.org/ .. _`Sublime Text`: http://sublimetext.com/ Getting Started --------------- Install the module with: ``pip install restructuredtext_lint`` .. code:: python import restructuredtext_lint errors = restructuredtext_lint.lint(""" Hello World ======= """) # `errors` will be list of system messages # [<system_message: <paragraph...><literal_block...>>] errors[0].message # Title underline too short. CLI Utility ^^^^^^^^^^^ For your convenience, we present a CLI utility ``rst-lint`` (also available as ``restructuredtext-lint``). .. code:: console $ rst-lint --help usage: rst-lint [-h] [--version] [--format {text,json}] [--encoding ENCODING] [--level {debug,info,warning,error,severe}] [--rst-prolog RST_PROLOG] path [path ...] Lint reStructuredText files. Returns 0 if all files pass linting, 1 for an internal error, and 2 if linting failed. positional arguments: path File/folder to lint optional arguments: -h, --help show this help message and exit --version show program's version number and exit --format {text,json} Format of the output (default: "text") --encoding ENCODING Encoding of the input file (e.g. "utf-8") --level {debug,info,warning,error,severe} Minimum error level to report (default: "warning") --rst-prolog RST_PROLOG reStructuredText content to prepend to all files (useful for substitutions) $ rst-lint README.rst WARNING README.rst:2 Title underline too short. PyPI issues ^^^^^^^^^^^ While a document may lint cleanly locally, there can be issues when submitted it to `PyPI`_. Here are some common problems: - Usage of non-builtin lexers (e.g. ``bibtex``) will pass locally but not be recognized/parsable on `PyPI`_ - This is due to `PyPI`_ not having a non-builtin lexer installed - Please avoid non-builtin lexers to avoid complications - For more information, see `#27`_ - Relative hyperlinks will not work (e.g. ``./UNLICENSE``) - According to Stack Overflow, hyperlinks must use a scheme (e.g. ``http``, ``https``) and that scheme must be whitelisted - http://stackoverflow.com/a/16594755 - Please use absolute hyperlinks (e.g. ``https://github.com/twolfson/restructuredtext-lint/blob/master/UNLICENSE``) .. _`#27`: https://github.com/twolfson/restructuredtext-lint/issues/27 Documentation ------------- ``restructuredtext-lint`` exposes a ``lint`` and ``lint_file`` function ``restructuredtext_lint.lint(content, filepath=None, rst_prolog=None)`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Lint `reStructuredText`_ and return errors - content ``String`` - `reStructuredText`_ to be linted - filepath ``String`` - Optional path to file, this will be returned as the source - rst_prolog ``String`` - Optional content to prepend to content, line numbers will be offset to ignore this Returns: - errors ``List`` - List of errors - Each error is a class from `docutils`_ with the following attrs - line ``Integer|None`` - Line where the error occurred - On rare occasions, this will be ``None`` (e.g. anonymous link mismatch) - source ``String`` - ``filepath`` provided in parameters - level ``Integer`` - Level of the warning - Levels represent 'info': 1, 'warning': 2, 'error': 3, 'severe': 4 - type ``String`` - Noun describing the error level - Levels can be 'INFO', 'WARNING', 'ERROR', or 'SEVERE' - message ``String`` - Error message - full_message ``String`` - Error message and source lines where the error occurred - It should be noted that ``level``, ``type``, ``message``, and ``full_message`` are custom attrs added onto the original ``system_message`` .. _`docutils`: http://docutils.sourceforge.net/ ``restructuredtext_lint.lint_file(filepath, encoding=None, *args, **kwargs)`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Lint a `reStructuredText`_ file and return errors - filepath ``String`` - Path to file for linting - encoding ``String`` - Encoding to read file in as - When ``None`` is provided, it will use OS default as provided by `locale.getpreferredencoding`_ - The list of supported encodings can be found at http://docs.python.org/2/library/codecs.html#standard-encodings - ``*args`` - Additional arguments to be passed to ``lint`` - ``**kwargs`` - Additional keyword arguments to be passed to ``lint`` .. _`locale.getpreferredencoding`: http://docs.python.org/2/library/locale.html#locale.getpreferredencoding Returns: Same structure as ``restructuredtext_lint.lint`` Extension --------- Under the hood, we leverage `docutils`_ for parsing reStructuredText documents. `docutils`_ supports adding new directives and roles via ``register_directive`` and ``register_role``. Sphinx ^^^^^^ Unfortunately due to customizations in `Sphinx's parser`_ we cannot include all of its directives/roles (see `#29`_). However, we can include some of them as one-offs. Here is an example of adding a directive from `Sphinx`_. .. _`Sphinx`: http://sphinx-doc.org/ .. _`Sphinx's parser`: Sphinx_ .. _`#29`: https://github.com/twolfson/restructuredtext-lint/issues/29#issuecomment-243456787 https://github.com/sphinx-doc/sphinx/blob/1.3/sphinx/directives/code.py **sphinx.rst** .. code:: rst Hello ===== World .. highlight:: python Hello World! **sphinx.py** .. code:: python # Load in our dependencies from docutils.parsers.rst.directives import register_directive from sphinx.directives.code import Highlight import restructuredtext_lint # Load our new directive register_directive('highlight', Highlight) # Lint our README errors = restructuredtext_lint.lint_file('docs/sphinx/README.rst') print errors[0].message # Error in "highlight" directive: no content permitted. Examples -------- Here is an example of all invalid properties .. code:: python rst = """ Some content. Hello World ======= Some more content! """ errors = restructuredtext_lint.lint(rst, 'myfile.py') errors[0].line # 5 errors[0].source # myfile.py errors[0].level # 2 errors[0].type # WARNING errors[0].message # Title underline too short. errors[0].full_message # Title underline too short. # # Hello World # ======= Contributing ------------ In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Test via ``nosetests``. Donating -------- Support this project and `others by twolfson`_ via `donations`_. http://twolfson.com/support-me .. _`others by twolfson`: http://twolfson.com/projects .. _donations: http://twolfson.com/support-me Unlicense --------- As of Nov 22 2013, Todd Wolfson has released this repository and its contents to the public domain. It has been released under the `UNLICENSE`_. .. _UNLICENSE: https://github.com/twolfson/restructuredtext-lint/blob/master/UNLICENSE