././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1673898568.943623 Markups-4.0.0/0000755000175000017500000000000014361325111012574 5ustar00dmitrydmitry././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1673897482.0 Markups-4.0.0/LICENSE0000644000175000017500000000273614361323012013607 0ustar00dmitrydmitryCopyright 2012-2023 Dmitry Shachnev . All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1648472338.0 Markups-4.0.0/MANIFEST.in0000644000175000017500000000021614220330422014324 0ustar00dmitrydmitryinclude LICENSE include README.rst include changelog include markup2html.py recursive-include docs *.rst conf.py recursive-include tests *.py ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1673898568.943623 Markups-4.0.0/Markups.egg-info/0000755000175000017500000000000014361325111015710 5ustar00dmitrydmitry././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1673898568.0 Markups-4.0.0/Markups.egg-info/PKG-INFO0000644000175000017500000000653114361325110017011 0ustar00dmitrydmitryMetadata-Version: 2.1 Name: Markups Version: 4.0.0 Summary: A wrapper around various text markups Author-email: Dmitry Shachnev License: BSD 3-Clause License Project-URL: Homepage, https://github.com/retext-project/pymarkups Project-URL: Documentation, https://pymarkups.readthedocs.io/en/latest/ Project-URL: Issue Tracker, https://github.com/retext-project/pymarkups/issues/ Classifier: Development Status :: 5 - Production/Stable Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Topic :: Text Processing :: Markup Classifier: Topic :: Text Processing :: General Classifier: Topic :: Software Development :: Libraries :: Python Modules Requires-Python: >=3.9 Description-Content-Type: text/x-rst Provides-Extra: markdown Provides-Extra: restructuredtext Provides-Extra: textile Provides-Extra: highlighting Provides-Extra: asciidoc License-File: LICENSE .. image:: https://github.com/retext-project/pymarkups/workflows/tests/badge.svg :target: https://github.com/retext-project/pymarkups/actions :alt: GitHub Actions status .. image:: https://codecov.io/gh/retext-project/pymarkups/branch/master/graph/badge.svg :target: https://codecov.io/gh/retext-project/pymarkups :alt: Coverage status .. image:: https://readthedocs.org/projects/pymarkups/badge/?version=latest :target: https://pymarkups.readthedocs.io/en/latest/ :alt: ReadTheDocs status This module provides a wrapper around various text markup languages. Available by default are Markdown_, reStructuredText_, Textile_ and AsciiDoc_, but you can easily add your own markups. Usage example: .. code:: python >>> import markups >>> markup = markups.get_markup_for_file_name("myfile.rst") >>> markup.name 'reStructuredText' >>> markup.attributes[markups.common.SYNTAX_DOCUMENTATION] 'https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html' >>> text = """ ... Hello, world! ... ============= ... ... This is an example **reStructuredText** document. ... """ >>> result = markup.convert(text) >>> result.get_document_title() 'Hello, world!' >>> print(result.get_document_body()) # doctest: +NORMALIZE_WHITESPACE

Hello, world!

This is an example reStructuredText document.

.. _Markdown: https://daringfireball.net/projects/markdown/ .. _reStructuredText: https://docutils.sourceforge.io/rst.html .. _Textile: https://en.wikipedia.org/wiki/Textile_(markup_language) .. _AsciiDoc: https://asciidoc.org The release version can be downloaded from PyPI_ or installed using:: pip install Markups .. _PyPI: https://pypi.org/project/Markups/ The source code is hosted on GitHub_. .. _GitHub: https://github.com/retext-project/pymarkups The documentation is available online_ or can be generated from source by installing Sphinx_ and running:: python3 -m sphinx docs build/sphinx/html .. _online: https://pymarkups.readthedocs.io/en/latest/ .. _Sphinx: https://www.sphinx-doc.org/en/master/ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1673898568.0 Markups-4.0.0/Markups.egg-info/SOURCES.txt0000644000175000017500000000127014361325110017573 0ustar00dmitrydmitryLICENSE MANIFEST.in README.rst changelog markup2html.py pyproject.toml Markups.egg-info/PKG-INFO Markups.egg-info/SOURCES.txt Markups.egg-info/dependency_links.txt Markups.egg-info/entry_points.txt Markups.egg-info/requires.txt Markups.egg-info/top_level.txt docs/changelog.rst docs/conf.py docs/custom_markups.rst docs/index.rst docs/interface.rst docs/overview.rst docs/standard_markups.rst markups/__init__.py markups/abstract.py markups/asciidoc.py markups/common.py markups/markdown.py markups/py.typed markups/restructuredtext.py markups/textile.py tests/__init__.py tests/test_asciidoc.py tests/test_markdown.py tests/test_public_api.py tests/test_restructuredtext.py tests/test_textile.py././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1673898568.0 Markups-4.0.0/Markups.egg-info/dependency_links.txt0000644000175000017500000000000114361325110021755 0ustar00dmitrydmitry ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1673898568.0 Markups-4.0.0/Markups.egg-info/entry_points.txt0000644000175000017500000000031514361325110021204 0ustar00dmitrydmitry[pymarkups] asciidoc = markups.asciidoc:AsciiDocMarkup markdown = markups.markdown:MarkdownMarkup restructuredtext = markups.restructuredtext:ReStructuredTextMarkup textile = markups.textile:TextileMarkup ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1673898568.0 Markups-4.0.0/Markups.egg-info/requires.txt0000644000175000017500000000022714361325110020310 0ustar00dmitrydmitrypython-markdown-math [asciidoc] asciidoc lxml [highlighting] Pygments [markdown] Markdown>=3 PyYAML [restructuredtext] docutils [textile] textile ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1673898568.0 Markups-4.0.0/Markups.egg-info/top_level.txt0000644000175000017500000000001014361325110020430 0ustar00dmitrydmitrymarkups ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1673898568.943623 Markups-4.0.0/PKG-INFO0000644000175000017500000000653114361325111013676 0ustar00dmitrydmitryMetadata-Version: 2.1 Name: Markups Version: 4.0.0 Summary: A wrapper around various text markups Author-email: Dmitry Shachnev License: BSD 3-Clause License Project-URL: Homepage, https://github.com/retext-project/pymarkups Project-URL: Documentation, https://pymarkups.readthedocs.io/en/latest/ Project-URL: Issue Tracker, https://github.com/retext-project/pymarkups/issues/ Classifier: Development Status :: 5 - Production/Stable Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Topic :: Text Processing :: Markup Classifier: Topic :: Text Processing :: General Classifier: Topic :: Software Development :: Libraries :: Python Modules Requires-Python: >=3.9 Description-Content-Type: text/x-rst Provides-Extra: markdown Provides-Extra: restructuredtext Provides-Extra: textile Provides-Extra: highlighting Provides-Extra: asciidoc License-File: LICENSE .. image:: https://github.com/retext-project/pymarkups/workflows/tests/badge.svg :target: https://github.com/retext-project/pymarkups/actions :alt: GitHub Actions status .. image:: https://codecov.io/gh/retext-project/pymarkups/branch/master/graph/badge.svg :target: https://codecov.io/gh/retext-project/pymarkups :alt: Coverage status .. image:: https://readthedocs.org/projects/pymarkups/badge/?version=latest :target: https://pymarkups.readthedocs.io/en/latest/ :alt: ReadTheDocs status This module provides a wrapper around various text markup languages. Available by default are Markdown_, reStructuredText_, Textile_ and AsciiDoc_, but you can easily add your own markups. Usage example: .. code:: python >>> import markups >>> markup = markups.get_markup_for_file_name("myfile.rst") >>> markup.name 'reStructuredText' >>> markup.attributes[markups.common.SYNTAX_DOCUMENTATION] 'https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html' >>> text = """ ... Hello, world! ... ============= ... ... This is an example **reStructuredText** document. ... """ >>> result = markup.convert(text) >>> result.get_document_title() 'Hello, world!' >>> print(result.get_document_body()) # doctest: +NORMALIZE_WHITESPACE

Hello, world!

This is an example reStructuredText document.

.. _Markdown: https://daringfireball.net/projects/markdown/ .. _reStructuredText: https://docutils.sourceforge.io/rst.html .. _Textile: https://en.wikipedia.org/wiki/Textile_(markup_language) .. _AsciiDoc: https://asciidoc.org The release version can be downloaded from PyPI_ or installed using:: pip install Markups .. _PyPI: https://pypi.org/project/Markups/ The source code is hosted on GitHub_. .. _GitHub: https://github.com/retext-project/pymarkups The documentation is available online_ or can be generated from source by installing Sphinx_ and running:: python3 -m sphinx docs build/sphinx/html .. _online: https://pymarkups.readthedocs.io/en/latest/ .. _Sphinx: https://www.sphinx-doc.org/en/master/ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1673895706.0 Markups-4.0.0/README.rst0000644000175000017500000000420414361317432014272 0ustar00dmitrydmitry.. image:: https://github.com/retext-project/pymarkups/workflows/tests/badge.svg :target: https://github.com/retext-project/pymarkups/actions :alt: GitHub Actions status .. image:: https://codecov.io/gh/retext-project/pymarkups/branch/master/graph/badge.svg :target: https://codecov.io/gh/retext-project/pymarkups :alt: Coverage status .. image:: https://readthedocs.org/projects/pymarkups/badge/?version=latest :target: https://pymarkups.readthedocs.io/en/latest/ :alt: ReadTheDocs status This module provides a wrapper around various text markup languages. Available by default are Markdown_, reStructuredText_, Textile_ and AsciiDoc_, but you can easily add your own markups. Usage example: .. code:: python >>> import markups >>> markup = markups.get_markup_for_file_name("myfile.rst") >>> markup.name 'reStructuredText' >>> markup.attributes[markups.common.SYNTAX_DOCUMENTATION] 'https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html' >>> text = """ ... Hello, world! ... ============= ... ... This is an example **reStructuredText** document. ... """ >>> result = markup.convert(text) >>> result.get_document_title() 'Hello, world!' >>> print(result.get_document_body()) # doctest: +NORMALIZE_WHITESPACE

Hello, world!

This is an example reStructuredText document.

.. _Markdown: https://daringfireball.net/projects/markdown/ .. _reStructuredText: https://docutils.sourceforge.io/rst.html .. _Textile: https://en.wikipedia.org/wiki/Textile_(markup_language) .. _AsciiDoc: https://asciidoc.org The release version can be downloaded from PyPI_ or installed using:: pip install Markups .. _PyPI: https://pypi.org/project/Markups/ The source code is hosted on GitHub_. .. _GitHub: https://github.com/retext-project/pymarkups The documentation is available online_ or can be generated from source by installing Sphinx_ and running:: python3 -m sphinx docs build/sphinx/html .. _online: https://pymarkups.readthedocs.io/en/latest/ .. _Sphinx: https://www.sphinx-doc.org/en/master/ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1673898394.0 Markups-4.0.0/changelog0000644000175000017500000001500714361324632014460 0ustar00dmitrydmitryVersion 4.0.0, 2023-01-16 ========================= Incompatible changes: * Python versions older than 3.9 are no longer supported. * Python-Markdown versions older than 3.0 are no longer supported. * Setuptools 61.2 or higher is required to build the project. * ``setup.py`` has been removed. Use ``pip``, ``build`` or other :PEP:`517` compatible tool. Other changes: * Added AsciiDocMarkup (contributed by Dave Kuhlman in #17). * Made the tests pass with Pygments ≥ 2.11. * Made the tests pass when PyYAML is not installed (#18). * Reformatted code in accordance with :PEP:`8` standard. * Fixed mypy errors and added a :PEP:`561` ``py.typed`` file. Version 3.1.3, 2021-11-21 ========================= * Fixed logic to load extensions file when PyYAML module is not available (issue #16, thanks foxB612 for the bug report). * Made the tests pass with docutils 0.18. Version 3.1.2, 2021-09-06 ========================= * Incompatible change: Python 3.6 is no longer supported. * Fixed replacing Markdown extensions in document. * Fixed crash when using TOC backrefs in reStructuredText (issue #14, thanks Hrissimir for the patch). Version 3.1.1, 2021-03-05 ========================= * The reStructuredText markup now includes line numbers information in ``data-posmap`` attributes. * The reStructuredText markup now uses only ``minimal.css`` stylesheet (not ``plain.css`` anymore). * Added support for the upcoming docutils 0.17 release to the tests. Version 3.1.0, 2021-01-31 ========================= Incompatible changes: * Python versions older than 3.6 are no longer supported. Other changes: * Instead of ``pkg_resources``, ``importlib.metadata`` is now used. * For Markdown markup, ``markdown-extensions.yaml`` files are now supported in addition to ``markdown-extensions.txt`` files. * Type annotations were added for public API. * The reStructuredText markup no longer raises exceptions for invalid markup. * MathJax v3 is now supported in addition to v2. Also, the Arch Linux mathjax packages are now supported (issue #4). * Added Pygments CSS support for the ``pymdownx.highlight`` Markdown extension. Version 3.0.0, 2018-05-03 ========================= Incompatible changes: * The deprecated AbstractMarkup API has been removed. * Python 3.2 is no longer supported. * The output now uses HTML5 instead of HTML4. * The custom markups are now registered with entry points. * The ``get_custom_markups()`` method has been removed. * New required dependency: python-markdown-math_. Other changes: * The upcoming Python-Markdown 3.x release is now supported. .. _python-markdown-math: https://pypi.org/project/python-markdown-math/ Version 2.0.1, 2017-06-24 ========================= * The new MathJax CDN is used, the old one will be shut down soon. * When using MathJax with Markdown, the AMSmath and AMSsymbols extensions are now enabled. Version 2.0.0, 2016-05-09 ========================= Incompatible changes: * Changed the API of pymarkups to clearly separate the conversion step from access to the various elements of the result. The old API is deprecated and will be removed in a future release. Please see the documentation for details on using the new API. * The reStructuredText markup now includes document title and subtitle in the HTML body. Other changes: * Added a ``markup2html.py`` reference script to show API usage. * Improved support for specifying Markdown extensions in the document. Version 1.0.1, 2015-12-22 ========================= * The Textile markup now uses the recommended python-textile API. * Fixed warnings during installation. * Python-Markdown Math extension updated to the latest version. Version 1.0, 2015-12-13 ======================= * Web module removed, as ReText no longer needs it. * Textile markup updated to work with the latest version of Python-Textile module. * The setup script now uses setuptools when it is available. * Testsuite and documentation improvements. Version 0.6.3, 2015-06-16 ========================= * No-change re-upload with fixed tarball and changelog. Version 0.6.2, 2015-06-09 ========================= * Markdown markup: fixed detection of codehilite extension with options. * Added a warning about deprecation of the markups.web module. Version 0.6.1, 2015-04-19 ========================= * PyMarkups now uses warnings system instead of printing messages to stderr. * Improvements to Markdown markup: + Fixed parsing math that contains nested environments (thanks to Gautam Iyer for the patch). + Fixed crash on extensions names starting with dot. * Miscellaneous fixes. Version 0.6, 2015-01-25 ======================= Incompatible changes: * Custom markups are now normal Python modules. * Web module no longer supports Python 2.x. Other changes: * Refactor the code related to Markdown extensions to make it work with upcoming Python-Markdown releases. * MathJax extension is now in a separate module. Version 0.5.2, 2014-11-05 ========================= * Fixed loading of Markdown extensions with options. Version 0.5.1, 2014-09-16 ========================= * Fixed Markdown markup crash on empty files. * Include documentation in the tarballs. * Testsuite improvements. Version 0.5, 2014-07-25 ======================= * Improvements to Markdown markup: + All math delimiters except ``$...$`` are now enabled by default. + ``remove_extra`` extension now disables formulas support. + It is now possible to specify required extensions in the first line of the file. * Add Sphinx documentation. Version 0.4, 2013-11-30 ======================= * Add Textile markup. * reStructuredText markup now supports file names and settings overrides. * Web module now raises WebUpdateError when updating fails. Version 0.3, 2013-07-25 ======================= * MathJax support in Markdown has been improved and no longer relies on tex2jax extension. * It is now possible to pass extensions list to MarkdownMarkup constructor. * Pygments style is now configurable. * Testsuite improvements. Version 0.2.3, 2012-11-02 ========================= * Fix support for custom working directory in web module. * Bug fixes in Markdown module and tests. Version 0.2.2, 2012-10-02 ========================= * Re-written math support for Markdown. * Add tests to the tarball. * Add example template for web module. * Bug fixes in Markdown and web modules. Version 0.2.1, 2012-09-09 ========================= * Add caching support, to speed up get_document_body function. * Add testsuite. * Fix some bugs in markdown module. Version 0.2, 2012-09-04 ======================= * Initial release. ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1673898568.943623 Markups-4.0.0/docs/0000755000175000017500000000000014361325111013524 5ustar00dmitrydmitry././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1648472338.0 Markups-4.0.0/docs/changelog.rst0000644000175000017500000000050014220330422016173 0ustar00dmitrydmitry======================== Python-Markups changelog ======================== This changelog only lists the most important changes that happened in Python-Markups. Please see the `Git log`_ for the full list of changes. .. _`Git log`: https://github.com/retext-project/pymarkups/commits/master .. include:: ../changelog ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1673897441.0 Markups-4.0.0/docs/conf.py0000644000175000017500000000306114361322741015031 0ustar00dmitrydmitry#!/usr/bin/env python3 import os import sys from markups import __version_tuple__ # 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. sys.path.insert(0, os.path.abspath('..')) # -- 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', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = 'Python-Markups' copyright = '2023, Dmitry Shachnev' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '%d.%d' % __version_tuple__[:2] # The full version, including alpha/beta/rc tags. release = '%d.%d.%d' % __version_tuple__ # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # -- 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 = 'nature' ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1648472338.0 Markups-4.0.0/docs/custom_markups.rst0000644000175000017500000000465514220330422017337 0ustar00dmitrydmitry============== Custom Markups ============== Registering the markup module ============================= A third-party markup is a Python module that can be installed the usual way. To register your markup class with PyMarkups, make it inherit from :class:`~markups.abstract.AbstractMarkup`, and add that class to your module's ``entry_points``, in the “pymarkups” entry point group. For example: .. code-block:: python setup( ... entry_points={ 'pymarkups': [ 'mymarkup = mymodule:MyMarkupClass', ], }, ... ) Or using the declarative syntax in ``setup.cfg``: .. code-block:: ini [options.entry_points] pymarkups = mymarkup = mymodule:MyMarkupClass See the `setuptools documentation`_ on entry points for details. To check if the module was found by Python-Markups, one can check if the module is present in return value of :func:`~markups.get_all_markups` function. .. versionchanged:: 3.0 The custom markups should be registered using the entry points mechanism, the ``pymarkups.txt`` file is no longer supported. .. _`setuptools documentation`: https://setuptools.pypa.io/en/latest/userguide/entry_point.html Importing third-party modules ============================= A markup must not directly import any third party Python module it uses at file level. Instead, it should check the module availability in :meth:`~markups.abstract.AbstractMarkup.available` static method. That method can try to import the needed modules, and return ``True`` in case of success, and ``False`` in case of failure. Implementing methods ==================== Any markup must inherit from :class:`~markups.abstract.AbstractMarkup`. Third-party markups must implement :class:`~markups.abstract.AbstractMarkup`'s :meth:`~markups.abstract.AbstractMarkup.convert` method, which must perform the time-consuming part of markup conversion and return a newly constructed instance of (a subclass of) :class:`~markups.abstract.ConvertedMarkup`. :class:`~markups.abstract.ConvertedMarkup` encapsulates the title, body, stylesheet and javascript of a converted document. Of these only the body is required during construction, the others default to an empty string. If additional markup-specific state is required to implement :class:`~markups.abstract.ConvertedMarkup`, a subclass can be defined and an instance of it returned from :meth:`~markups.abstract.AbstractMarkup.convert` instead. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1671910683.0 Markups-4.0.0/docs/index.rst0000644000175000017500000000200414351652433015372 0ustar00dmitrydmitry=================================== Python-Markups module documentation =================================== Introduction to Python-Markups ============================== Python-Markups is a module that provides unified interface for using various markup languages, such as Markdown, reStructuredText, Textile and AsciiDoc. It is also possible for clients to create and register their own markup languages. The output language Python-Markups works with is HTML. Stylesheets and JavaScript sections are supported. The abstract interface that any markup implements is :class:`~markups.abstract.AbstractMarkup`. Contents ======== .. toctree:: overview interface standard_markups custom_markups changelog Links ===== * Python-Markups source code is hosted on GitHub_. * You can get the source tarball from PyPI_. * It is also packaged in Debian_. .. _GitHub: https://github.com/retext-project/pymarkups .. _PyPI: https://pypi.org/project/Markups/ .. _Debian: https://packages.debian.org/sid/source/pymarkups ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1648472338.0 Markups-4.0.0/docs/interface.rst0000644000175000017500000000122614220330422016212 0ustar00dmitrydmitry================ Markup interface ================ The main class for interacting with markups is :class:`~markups.abstract.AbstractMarkup`. However, you shouldn't create direct instances of that class. Instead, use one of the :doc:`standard markup classes `. .. autoclass:: markups.abstract.AbstractMarkup :members: When :class:`~markups.abstract.AbstractMarkup`'s :meth:`~markups.abstract.AbstractMarkup.convert` method is called it will return an instance of :class:`~markups.abstract.ConvertedMarkup` or a subclass thereof that provides access to the conversion results. .. autoclass:: markups.abstract.ConvertedMarkup :members: ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1648472338.0 Markups-4.0.0/docs/overview.rst0000644000175000017500000000236714220330422016127 0ustar00dmitrydmitry============ API overview ============ For the basic usage of Python-Markups, one should import some markup class from :mod:`markups`, create an instance of that class, and use the :meth:`~markups.abstract.AbstractMarkup.convert` method: >>> import markups >>> markup = markups.ReStructuredTextMarkup() >>> markup.convert('*reStructuredText* test').get_document_body() '
\n

reStructuredText test

\n
\n' For advanced usage (like dynamically choosing the markup class), one may use one of the functions documented below. Getting lists of available markups ================================== .. autofunction:: markups.get_all_markups .. autofunction:: markups.get_available_markups Getting a specific markup ========================= .. autofunction:: markups.get_markup_for_file_name .. autofunction:: markups.find_markup_class_by_name .. _configuration-directory: Configuration directory ======================= Some markups can provide configuration files that the user may use to change the behavior. These files are stored in a single configuration directory. If :envvar:`XDG_CONFIG_HOME` is defined, then the configuration directory is it. Otherwise, it is :file:`.config` subdirectory in the user's home directory. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1671910968.0 Markups-4.0.0/docs/standard_markups.rst0000644000175000017500000001032014351653070017623 0ustar00dmitrydmitry================ Built-in markups ================ These markups are available by default: Markdown markup =============== Markdown_ markup uses Python-Markdown_ as a backend (version 2.6 or later is required). There are several ways to enable `Python-Markdown extensions`_. * List extensions in a file named :file:`markdown-extensions.yaml` or :file:`markdown-extensions.txt` in the :ref:`configuration directory `. The extensions will be automatically applied to all documents. * If :file:`markdown-extensions.yaml` or :file:`markdown-extensions.txt` is placed into working directory, all documents in that directory will get extensions that are listed in that file. * If first line of a document contains ":samp:`Required extensions: {ext1 ext2 ...}`", that list will be applied to a document. * Finally, one can programmatically pass list of extension names to :class:`markups.MarkdownMarkup` constructor. The YAML file should be a list of extensions, possibly with configuration options, for example: .. code-block:: yaml - smarty: substitutions: left-single-quote: "‚" right-single-quote: "‘" smart_dashes: False - toc: permalink: True separator: "_" toc_depth: 3 - sane_lists Or using a JSON-like syntax: .. code-block:: yaml ["smarty", "sane_lists"] YAML support works only when the PyYAML_ module is installed. The txt file is a simple list of extensions, separated by newlines. Lines starting with ``#`` are treated as comments and ignored. It is possible to specify string options in brackets, for example:: toc(title=Contents) sane_lists The same syntax to specify options works in the ``Required extensions`` line. You can put it into a comment to make it invisible in the output:: The `Math Markdown extension`_ is enabled by default. This extension supports a syntax for LaTeX-style math formulas (powered by MathJax_). The delimiters are: ================ =============== Inline math Standalone math ================ =============== ``$...$`` [#f1]_ ``$$...$$`` ``\(...\)`` ``\[...\]`` ================ =============== .. [#f1] To enable single-dollar-sign delimiter, one should add ``mdx_math(enable_dollar_delimiter=1)`` to the extensions list. The `Python-Markdown Extra`_ set of extensions is enabled by default. To disable it, one can enable virtual ``remove_extra`` extension (which also completely disables LaTeX formulas support). The default file extension associated with Markdown markup is ``.mkd``, though many other extensions (including ``.md`` and ``.markdown``) are supported as well. .. _Markdown: https://daringfireball.net/projects/markdown/ .. _Python-Markdown: https://python-markdown.github.io/ .. _MathJax: https://www.mathjax.org/ .. _`Python-Markdown extensions`: https://python-markdown.github.io/extensions/ .. _PyYAML: https://pypi.org/project/PyYAML/ .. _`Math Markdown extension`: https://github.com/mitya57/python-markdown-math .. _`Python-Markdown Extra`: https://python-markdown.github.io/extensions/extra/ .. autoclass:: markups.MarkdownMarkup reStructuredText markup ======================== This markup provides support for reStructuredText_ language (the language this documentation is written in). It uses Docutils_ Python module. The file extension associated with reStructuredText markup is ``.rst``. .. _reStructuredText: https://docutils.sourceforge.io/rst.html .. _Docutils: https://docutils.sourceforge.io/ .. autoclass:: markups.ReStructuredTextMarkup Textile markup ============== This markup provides support for Textile_ language. It uses python-textile_ module. The file extension associated with Textile markup is ``.textile``. .. _Textile: https://en.wikipedia.org/wiki/Textile_(markup_language) .. _python-textile: https://github.com/textile/python-textile .. autoclass:: markups.TextileMarkup AsciiDoc markup =============== This markup provides support for AsciiDoc_ language. It uses asciidoc-py_ module. The file extension associated with AsciiDoc markup is ``.adoc``. .. _AsciiDoc: https://asciidoc.org .. _asciidoc-py: https://asciidoc-py.github.io .. autoclass:: markups.AsciiDocMarkup ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1671915413.0 Markups-4.0.0/markup2html.py0000755000175000017500000000236314351663625015441 0ustar00dmitrydmitry#!/usr/bin/env python3 import argparse import sys import markups def export_file(args: argparse.Namespace) -> None: markup = markups.get_markup_for_file_name(args.input_file) with open(args.input_file) as input: text = input.read() if not markup: sys.exit('Markup not available.') converted = markup.convert(text) html = converted.get_whole_html(include_stylesheet=args.include_stylesheet, fallback_title=args.fallback_title, webenv=args.web_environment) with open(args.output_file, 'w') as output: output.write(html) if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('--web-environment', help='export for web environment', action='store_true') parser.add_argument('--include-stylesheet', help='embed the stylesheet into html', action='store_true') parser.add_argument('--fallback-title', help='fallback title of the HTML document', metavar='TITLE') parser.add_argument('input_file', help='input file') parser.add_argument('output_file', help='output file') args = parser.parse_args() export_file(args) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1673898568.943623 Markups-4.0.0/markups/0000755000175000017500000000000014361325111014256 5ustar00dmitrydmitry././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1673898394.0 Markups-4.0.0/markups/__init__.py0000644000175000017500000000707414361324632016406 0ustar00dmitrydmitry# This file is part of python-markups module # License: 3-clause BSD, see LICENSE file # Copyright: (C) Dmitry Shachnev, 2012-2022 from importlib.metadata import entry_points from typing import Literal, Optional, Union, overload from markups.abstract import AbstractMarkup from markups.asciidoc import AsciiDocMarkup from markups.markdown import MarkdownMarkup from markups.restructuredtext import ReStructuredTextMarkup from markups.textile import TextileMarkup __version_tuple__ = (4, 0, 0) __version__ = '.'.join(map(str, __version_tuple__)) __all__ = [ "AbstractMarkup", "AsciiDocMarkup", "MarkdownMarkup", "ReStructuredTextMarkup", "TextileMarkup", "find_markup_class_by_name", "get_all_markups", "get_available_markups", "get_markup_for_file_name", ] builtin_markups = [ MarkdownMarkup, ReStructuredTextMarkup, TextileMarkup, AsciiDocMarkup, ] # Public API def get_all_markups() -> list[type[AbstractMarkup]]: """ :returns: list of all markups (both standard and custom ones) """ try: # Python 3.10+ entrypoints = entry_points(group="pymarkups") except TypeError: # Older versions entrypoints = entry_points()["pymarkups"] return [entry_point.load() for entry_point in entrypoints] def get_available_markups() -> list[type[AbstractMarkup]]: """ :returns: list of all available markups (markups whose :meth:`~markups.abstract.AbstractMarkup.available` method returns True) """ available_markups = [] for markup in get_all_markups(): if markup.available(): available_markups.append(markup) return available_markups @overload def get_markup_for_file_name( filename: str, return_class: Literal[False] = False ) -> Optional[AbstractMarkup]: ... @overload def get_markup_for_file_name( filename: str, return_class: Literal[True] ) -> Optional[type[AbstractMarkup]]: ... def get_markup_for_file_name( filename: str, return_class: bool = False ) -> Optional[Union[AbstractMarkup, type[AbstractMarkup]]]: """ :param filename: name of the file :param return_class: if true, this function will return a class rather than an instance :returns: a markup with :attr:`~markups.abstract.AbstractMarkup.file_extensions` attribute containing extension of `filename`, if found, otherwise ``None`` >>> import markups >>> markup = markups.get_markup_for_file_name('foo.mkd') >>> markup.convert('**Test**').get_document_body() '

Test

\\n' >>> markups.get_markup_for_file_name('bar.rst', return_class=True) """ markup_class = None for markup in get_all_markups(): for extension in markup.file_extensions: if filename.endswith(extension): markup_class = markup if return_class: return markup_class if markup_class and markup_class.available(): return markup_class(filename=filename) return None def find_markup_class_by_name(name: str) -> Optional[type[AbstractMarkup]]: """ :returns: a markup with :attr:`~markups.abstract.AbstractMarkup.name` attribute matching `name`, if found, otherwise ``None`` >>> import markups >>> markups.find_markup_class_by_name('textile') """ for markup in get_all_markups(): if markup.name.lower() == name.lower(): return markup return None ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1673897809.0 Markups-4.0.0/markups/abstract.py0000644000175000017500000001026414361323521016441 0ustar00dmitrydmitry# This file is part of python-markups module # License: 3-clause BSD, see LICENSE file # Copyright: (C) Dmitry Shachnev, 2012-2023 from __future__ import annotations from typing import Any, Optional whole_html_template = """ {custom_headers}{title} {stylesheet}{javascript} {body} """ class AbstractMarkup: """Abstract class for markup languages. :param filename: optional name of the file """ #: name of the markup visible to user name: str #: various attributes, like links to website and syntax documentation attributes: dict[int, Any] #: indicates which file extensions are associated with the markup file_extensions: tuple[str, ...] #: the default file extension default_extension: str def __init__(self, filename: Optional[str] = None): self.filename = filename @staticmethod def available() -> bool: """ :returns: whether the markup is ready for use (for example, whether the required third-party modules are importable) """ return True def convert(self, text: str) -> ConvertedMarkup: """ :returns: a ConvertedMarkup instance (or a subclass thereof) containing the markup converted to HTML """ raise NotImplementedError class ConvertedMarkup: """This class encapsulates the title, body, stylesheet and javascript of a converted document. Instances of this class are created by :meth:`.AbstractMarkup.convert` method, usually it should not be instantiated directly. """ def __init__(self, body: str, title: str = '', stylesheet: str = '', javascript: str = ''): self.title = title self.stylesheet = stylesheet self.javascript = javascript self.body = body def get_document_title(self) -> str: """ :returns: the document title """ return self.title def get_document_body(self) -> str: """ :returns: the contents of the ```` HTML tag """ return self.body def get_stylesheet(self) -> str: """ :returns: the contents of ``\n' if include_stylesheet else '') context = { "body": self.get_document_body(), "title": self.get_document_title() or fallback_title, "javascript": self.get_javascript(webenv), "stylesheet": stylesheet, "custom_headers": custom_headers, } return whole_html_template.format(**context) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1673897756.0 Markups-4.0.0/markups/asciidoc.py0000644000175000017500000000540414361323434016417 0ustar00dmitrydmitry# This file is part of python-markups module # License: 3-clause BSD, see LICENSE file # Copyright: (C) Dave Kuhlman, 2022 import importlib import warnings from io import StringIO import markups.common as common from markups.abstract import AbstractMarkup, ConvertedMarkup class AsciiDocMarkup(AbstractMarkup): """Markup class for AsciiDoc language. Inherits :class:`~markups.abstract.AbstractMarkup`. """ name = "asciidoc" attributes = { common.LANGUAGE_HOME_PAGE: "https://asciidoc.org", common.MODULE_HOME_PAGE: "https://asciidoc-py.github.io", common.SYNTAX_DOCUMENTATION: "https://asciidoc-py.github.io/userguide.html", } file_extensions = (".adoc", ".asciidoc") default_extension = ".adoc" @staticmethod def available() -> bool: try: importlib.import_module("asciidoc") importlib.import_module("lxml") except ImportError: return False return True def convert(self, text: str) -> ConvertedMarkup: import asciidoc from lxml import etree outfile = StringIO() infile = StringIO(text) opts = [ ("--backend", "html5"), ("--attribute", r"newline=\n"), ("--attribute", "footer-style=none"), ("--out-file", outfile), ] try: asciidoc.execute(None, opts, [infile]) except SystemExit as ex: warnings.warn(str(ex.__context__), SyntaxWarning) pass result = outfile.getvalue() parser = etree.HTMLParser() root_element = etree.fromstring(result, parser) head_element = root_element.xpath("./head")[0] title_element = root_element.xpath("./head/title")[0] style_elements = root_element.xpath("./head/style") javascript_elements = root_element.xpath("./head/script") body_element = root_element.xpath("./body")[0] head = "" for child in head_element.getchildren(): head += etree.tostring( child, encoding="unicode", method="html", ) body = "" for child in body_element.getchildren(): body += etree.tostring( child, encoding="unicode", method="html", ) title = title_element.text stylesheet = "" for style_element in style_elements: stylesheet += style_element.text javascript = "" for javascript_element in javascript_elements: javascript += etree.tostring( javascript_element, encoding="unicode", method="html", ) return ConvertedMarkup(body, title, stylesheet, javascript) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1673897857.0 Markups-4.0.0/markups/common.py0000644000175000017500000000305114361323601016121 0ustar00dmitrydmitry# This file is part of python-markups module # License: 3-clause BSD, see LICENSE file # Copyright: (C) Dmitry Shachnev, 2012-2022 import os.path from typing import Optional # Some common constants and functions (LANGUAGE_HOME_PAGE, MODULE_HOME_PAGE, SYNTAX_DOCUMENTATION) = range(3) CONFIGURATION_DIR = (os.getenv('XDG_CONFIG_HOME') or os.getenv('APPDATA') or os.path.expanduser('~/.config')) MATHJAX2_LOCAL_URLS = ( 'file:///usr/share/javascript/mathjax/MathJax.js', # Debian libjs-mathjax 'file:///usr/share/mathjax2/MathJax.js', # Arch Linux mathjax2 ) MATHJAX3_LOCAL_URLS = ( 'file:///usr/share/mathjax/tex-chtml.js', # Arch Linux mathjax ) MATHJAX_WEB_URL = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js' PYGMENTS_STYLE = 'default' def get_pygments_stylesheet( selector: Optional[str], style: Optional[str] = None ) -> str: if style is None: style = PYGMENTS_STYLE if style == '': return '' try: from pygments.formatters import HtmlFormatter except ImportError: return '' else: defs = HtmlFormatter(style=style).get_style_defs(selector) assert isinstance(defs, str) return defs + "\n" def get_mathjax_url_and_version(webenv: bool) -> tuple[str, int]: if not webenv: for url in MATHJAX3_LOCAL_URLS: if os.path.exists(url[7:]): # strip file:// return url, 3 for url in MATHJAX2_LOCAL_URLS: if os.path.exists(url[7:]): return url, 2 return MATHJAX_WEB_URL, 3 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1673897811.0 Markups-4.0.0/markups/markdown.py0000644000175000017500000002437714361323523016474 0ustar00dmitrydmitry# This file is part of python-markups module # License: 3-clause BSD, see LICENSE file # Copyright: (C) Dmitry Shachnev, 2012-2023 from __future__ import annotations import importlib import os import re import warnings from typing import Any, Iterable, Iterator, Optional import markups.common as common from markups.abstract import AbstractMarkup, ConvertedMarkup try: import yaml HAVE_YAML = True except ImportError: HAVE_YAML = False MATHJAX2_CONFIG = \ ''' ''' # Taken from: # https://docs.mathjax.org/en/latest/upgrading/v2.html?highlight=upgrading#changes-in-the-mathjax-api MATHJAX3_CONFIG = \ ''' ''' # noqa: E501 extensions_re = re.compile(r'required.extensions: (.+)', flags=re.IGNORECASE) extension_name_re = re.compile( r'[a-z0-9_.]+(?:\([^)]+\))?', flags=re.IGNORECASE) _canonicalized_ext_names: dict[str, str] = {} _name_and_config = tuple[str, dict[str, Any]] class MarkdownMarkup(AbstractMarkup): """Markup class for Markdown language. Inherits :class:`~markups.abstract.AbstractMarkup`. :param extensions: list of extension names :type extensions: list """ name = 'Markdown' attributes = { common.LANGUAGE_HOME_PAGE: 'https://daringfireball.net/projects/markdown/', common.MODULE_HOME_PAGE: 'https://github.com/Python-Markdown/markdown', common.SYNTAX_DOCUMENTATION: 'https://daringfireball.net/projects/markdown/syntax' } file_extensions = ('.md', '.mkd', '.mkdn', '.mdwn', '.mdown', '.markdown') default_extension = '.mkd' @staticmethod def available() -> bool: try: import markdown except ImportError: return False return getattr(markdown, '__version_info__', (2,)) >= (3,) def _load_extensions_list_from_txt_file( self, filename: str ) -> Iterator[_name_and_config]: with open(filename) as extensions_file: for line in extensions_file: if not line.startswith('#'): yield self._split_extension_config(line.rstrip()) def _load_extensions_list_from_yaml_file( self, filename: str ) -> Iterator[_name_and_config]: with open(filename) as extensions_file: try: data = yaml.safe_load(extensions_file) except yaml.YAMLError as ex: warnings.warn( f'Failed parsing {filename}: {ex}', SyntaxWarning) raise IOError from ex if isinstance(data, list): for item in data: if isinstance(item, dict): yield from item.items() elif isinstance(item, str): yield item, {} def _get_global_extensions( self, filename: Optional[str] ) -> Iterator[_name_and_config]: local_directory = os.path.dirname(filename) if filename else '' choices = [ os.path.join(local_directory, 'markdown-extensions.yaml'), os.path.join(local_directory, 'markdown-extensions.txt'), os.path.join(common.CONFIGURATION_DIR, 'markdown-extensions.yaml'), os.path.join(common.CONFIGURATION_DIR, 'markdown-extensions.txt'), ] for choice in choices: if choice.endswith('.yaml') and not HAVE_YAML: continue try: if choice.endswith('.txt'): yield from self._load_extensions_list_from_txt_file(choice) else: yield from self._load_extensions_list_from_yaml_file(choice) except IOError: continue # Cannot open file, move to the next choice else: break # File loaded successfully, skip the remaining choices def _get_document_extensions(self, text: str) -> Iterator[_name_and_config]: lines = text.splitlines() match = extensions_re.search(lines[0]) if lines else None if match: extensions = extension_name_re.findall(match.group(1)) yield from self._split_extensions_configs(extensions) def _canonicalize_extension_name(self, extension_name: str) -> Optional[str]: prefixes = ('markdown.extensions.', '', 'mdx_') for prefix in prefixes: try: module = importlib.import_module(prefix + extension_name) if not hasattr(module, 'makeExtension'): continue except (ImportError, ValueError, TypeError): pass else: return prefix + extension_name return None def _split_extension_config(self, extension_name: str) -> _name_and_config: """Splits the configuration options from the extension name.""" lb = extension_name.find('(') if lb == -1: return extension_name, {} extension_name, parameters = extension_name[:lb], extension_name[lb + 1:-1] pairs = [x.split("=") for x in parameters.split(",")] return extension_name, {x.strip(): y.strip() for (x, y) in pairs} def _split_extensions_configs( self, extensions: Iterable[str] ) -> Iterator[_name_and_config]: """Splits the configuration options from a list of strings. :returns: a generator of (name, config) tuples """ for extension in extensions: yield self._split_extension_config(extension) def _apply_extensions( self, document_extensions: Optional[Iterable[_name_and_config]] = None ) -> None: extensions = self.global_extensions.copy() extensions.extend( self._split_extensions_configs(self.requested_extensions)) if document_extensions is not None: extensions.extend(document_extensions) extension_names = {"markdown.extensions.extra", "mdx_math"} extension_configs = {} for name, config in extensions: if name == 'mathjax': mathjax_config = {"enable_dollar_delimiter": True} extension_configs["mdx_math"] = mathjax_config elif name == 'remove_extra': if "markdown.extensions.extra" in extension_names: extension_names.remove("markdown.extensions.extra") if "mdx_math" in extension_names: extension_names.remove("mdx_math") else: if name in _canonicalized_ext_names: canonical_name = _canonicalized_ext_names[name] else: candidate = self._canonicalize_extension_name(name) if candidate is None: warnings.warn('Extension "%s" does not exist.' % name, ImportWarning) continue canonical_name = candidate _canonicalized_ext_names[name] = canonical_name extension_names.add(canonical_name) extension_configs[canonical_name] = config self.md = self.markdown.Markdown(extensions=list(extension_names), extension_configs=extension_configs, output_format='html5') self.extensions = extension_names self.extension_configs = extension_configs def __init__( self, filename: Optional[str] = None, extensions: Optional[list[str]] = None ): AbstractMarkup.__init__(self, filename) import markdown self.markdown = markdown self.requested_extensions = extensions or [] self.global_extensions: list[_name_and_config] = [] if extensions is None: self.global_extensions.extend( self._get_global_extensions(filename)) self._apply_extensions() def convert(self, text: str) -> ConvertedMarkdown: # Determine body self.md.reset() self._apply_extensions(self._get_document_extensions(text)) body = self.md.convert(text) + '\n' # Determine title if hasattr(self.md, 'Meta') and 'title' in self.md.Meta: title = str.join(' ', self.md.Meta['title']) else: title = '' # Determine stylesheet css_class = None if 'markdown.extensions.codehilite' in self.extensions: config = self.extension_configs.get( 'markdown.extensions.codehilite', {}) css_class = config.get('css_class', 'codehilite') stylesheet = common.get_pygments_stylesheet('.%s' % css_class) elif 'pymdownx.highlight' in self.extensions: config = self.extension_configs.get('pymdownx.highlight', {}) css_class = config.get('css_class', 'highlight') stylesheet = common.get_pygments_stylesheet('.%s' % css_class) else: stylesheet = '' return ConvertedMarkdown(body, title, stylesheet) class ConvertedMarkdown(ConvertedMarkup): def get_javascript(self, webenv: bool = False) -> str: if '' return config + script_tag % (mathjax_url, async_attr) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1673891571.0 Markups-4.0.0/markups/py.typed0000644000175000017500000000000014361307363015754 0ustar00dmitrydmitry././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1673897813.0 Markups-4.0.0/markups/restructuredtext.py0000644000175000017500000000763114361323525020306 0ustar00dmitrydmitry# This file is part of python-markups module # License: 3-clause BSD, see LICENSE file # Copyright: (C) Dmitry Shachnev, 2012-2023 from __future__ import annotations from typing import Any, Optional import markups.common as common from markups.abstract import AbstractMarkup, ConvertedMarkup try: from docutils.core import publish_parts from docutils.writers.html5_polyglot import HTMLTranslator, Writer HAVE_DOCUTILS = True except ImportError: HAVE_DOCUTILS = False if HAVE_DOCUTILS: class CustomHTMLTranslator(HTMLTranslator): # type: ignore def starttag( # type: ignore self, node, tagname, suffix='\n', empty=False, **attributes ): if getattr(node, 'line', None) is not None: attributes['data-posmap'] = node.line return super().starttag(node, tagname, suffix, empty, **attributes) class ReStructuredTextMarkup(AbstractMarkup): """Markup class for reStructuredText language. Inherits :class:`~markups.abstract.AbstractMarkup`. :param settings_overrides: optional dictionary of overrides for the `Docutils settings`_ :type settings_overrides: dict .. _`Docutils settings`: https://docutils.sourceforge.io/docs/user/config.html """ name = 'reStructuredText' attributes = { common.LANGUAGE_HOME_PAGE: 'https://docutils.sourceforge.io/rst.html', common.MODULE_HOME_PAGE: 'https://docutils.sourceforge.io/', common.SYNTAX_DOCUMENTATION: 'https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html' } file_extensions = ('.rst', '.rest') default_extension = '.rst' @staticmethod def available() -> bool: return HAVE_DOCUTILS def __init__( self, filename: Optional[str] = None, settings_overrides: Optional[dict[str, Any]] = None, ): self.overrides = settings_overrides or {} self.overrides.update({ 'math_output': 'MathJax ' + common.MATHJAX_WEB_URL, 'syntax_highlight': 'short', 'halt_level': 5, # Never convert system messages to exceptions 'stylesheet_path': 'minimal.css', # Do not include plain.css }) AbstractMarkup.__init__(self, filename) self.writer = Writer() self.writer.translator_class = CustomHTMLTranslator def convert(self, text: str) -> ConvertedReStructuredText: parts = publish_parts(text, source_path=self.filename, writer=self.writer, settings_overrides=self.overrides) # Determine head head = parts['head'] # Determine body body = parts['html_body'] # Determine title title = parts['title'] # Determine stylesheet origstyle = parts['stylesheet'] # Cut off tags stylestart = '')] stylesheet += common.get_pygments_stylesheet('.code') return ConvertedReStructuredText(head, body, title, stylesheet) class ConvertedReStructuredText(ConvertedMarkup): def __init__(self, head: str, body: str, title: str, stylesheet: str): ConvertedMarkup.__init__(self, body, title, stylesheet) self.head = head def get_javascript(self, webenv: bool = False) -> str: if common.MATHJAX_WEB_URL not in self.head: return '' mathjax_url, mathjax_version = common.get_mathjax_url_and_version( webenv) if mathjax_version == 2: mathjax_url += '?config=TeX-AMS_CHTML' async_attr = ' async' if mathjax_version == 3 else '' script_tag = '\n' return script_tag % (mathjax_url, async_attr) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1673897814.0 Markups-4.0.0/markups/textile.py0000644000175000017500000000241414361323526016317 0ustar00dmitrydmitry# This file is part of python-markups module # License: 3-clause BSD, see LICENSE file # Copyright: (C) Dmitry Shachnev, 2013-2023 import importlib from typing import Optional import markups.common as common from markups.abstract import AbstractMarkup, ConvertedMarkup class TextileMarkup(AbstractMarkup): """Markup class for Textile language. Inherits :class:`~markups.abstract.AbstractMarkup`. """ name = 'Textile' attributes = { common.LANGUAGE_HOME_PAGE: 'http://en.wikipedia.org/wiki/Textile_(markup_language)', common.MODULE_HOME_PAGE: 'https://github.com/textile/python-textile', common.SYNTAX_DOCUMENTATION: 'http://movabletype.org/documentation/author/textile-2-syntax.html' } file_extensions = ('.textile',) default_extension = '.textile' @staticmethod def available() -> bool: try: importlib.import_module('textile') except ImportError: return False return True def __init__(self, filename: Optional[str] = None): AbstractMarkup.__init__(self, filename) from textile import textile self.textile = textile def convert(self, text: str) -> ConvertedMarkup: return ConvertedMarkup(self.textile(text)) ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1673896042.0 Markups-4.0.0/pyproject.toml0000644000175000017500000000337414361320152015517 0ustar00dmitrydmitry[build-system] requires = ["setuptools>=61.2"] build-backend = "setuptools.build_meta" [project] name = "Markups" description = "A wrapper around various text markups" readme = "README.rst" authors = [{name = "Dmitry Shachnev", email = "mitya57@gmail.com"}] license = {text = "BSD 3-Clause License"} classifiers = [ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3 :: Only", "Topic :: Text Processing :: Markup", "Topic :: Text Processing :: General", "Topic :: Software Development :: Libraries :: Python Modules", ] requires-python = ">=3.9" dependencies = ["python-markdown-math"] dynamic = ["version"] [project.urls] Homepage = "https://github.com/retext-project/pymarkups" Documentation = "https://pymarkups.readthedocs.io/en/latest/" "Issue Tracker" = "https://github.com/retext-project/pymarkups/issues/" [project.optional-dependencies] markdown = ["Markdown>=3", "PyYAML"] restructuredtext = ["docutils"] textile = ["textile"] highlighting = ["Pygments"] asciidoc = ["asciidoc", "lxml"] [project.entry-points.pymarkups] markdown = "markups.markdown:MarkdownMarkup" restructuredtext = "markups.restructuredtext:ReStructuredTextMarkup" textile = "markups.textile:TextileMarkup" asciidoc = "markups.asciidoc:AsciiDocMarkup" [tool.setuptools] packages = ["markups"] include-package-data = false [tool.setuptools.package-data] markups = ["py.typed"] [tool.setuptools.dynamic] version = {attr = "markups.__version__"} ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1673898568.943623 Markups-4.0.0/setup.cfg0000644000175000017500000000004614361325111014415 0ustar00dmitrydmitry[egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1673898568.943623 Markups-4.0.0/tests/0000755000175000017500000000000014361325111013736 5ustar00dmitrydmitry././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1666547649.0 Markups-4.0.0/tests/__init__.py0000644000175000017500000000000014325277701016050 0ustar00dmitrydmitry././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1671914760.0 Markups-4.0.0/tests/test_asciidoc.py0000644000175000017500000000347714351662410017145 0ustar00dmitrydmitry# This file is part of python-markups test suite # License: 3-clause BSD, see LICENSE file # Copyright: (C) Dave Kuhlman, 2022 import unittest from markups.asciidoc import AsciiDocMarkup @unittest.skipUnless( AsciiDocMarkup.available(), "asciidoc.py and/or lxml not available" ) class AsciiDocTextTest(unittest.TestCase): def test_basic(self) -> None: self.maxDiff = None markup = AsciiDocMarkup() converted = markup.convert(BASIC_TEXT) body = converted.get_document_body() title = converted.get_document_title() stylesheet = converted.get_stylesheet() title_expected = "Hello, world!" self.assertIn(CONTENT_PART_EXPECTED, body) self.assertEqual(title_expected, title) self.assertGreater(len(stylesheet), 100) def test_error_handling(self) -> None: markup = AsciiDocMarkup() with self.assertWarnsRegex( SyntaxWarning, "section title not allowed in list item" ): converted = markup.convert(INVALID_SYNTAX) self.assertIn("Foo", converted.get_document_body()) def test_unicode(self) -> None: markup = AsciiDocMarkup() converted = markup.convert("Тест") body = converted.get_document_body() self.assertIn("Тест", body) # # ================================================================= # # Data to be used for comparison of correct results. # BASIC_TEXT = """\ = Hello, world! :toc: == Some subtitle This is an example *asciidoc* document. """ CONTENT_PART_EXPECTED = """\

Some subtitle

This is an example asciidoc document.

""" # noqa: E501 INVALID_SYNTAX = """\ - Foo + = Bar """ ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1673898352.0 Markups-4.0.0/tests/test_markdown.py0000644000175000017500000003414514361324560017207 0ustar00dmitrydmitry# This file is part of python-markups test suite # License: 3-clause BSD, see LICENSE file # Copyright: (C) Dmitry Shachnev, 2012-2023 import importlib import unittest import warnings from os.path import join from tempfile import TemporaryDirectory from markups.markdown import MarkdownMarkup, _canonicalized_ext_names try: import pymdownx except ImportError: pymdownx = None try: importlib.import_module("yaml") HAVE_YAML = True except ImportError: HAVE_YAML = False tables_source = \ '''th1 | th2 --- | --- t11 | t21 t12 | t22''' tables_output = \ '''
th1 th2
t11 t21
t12 t22
''' deflists_source = \ '''Apple : Pomaceous fruit of plants of the genus Malus in the family Rosaceae. Orange : The fruit of an evergreen tree of the genus Citrus.''' deflists_output = \ '''
Apple
Pomaceous fruit of plants of the genus Malus in the family Rosaceae.
Orange
The fruit of an evergreen tree of the genus Citrus.
''' mathjax_header = \ '\n\n' mathjax_source = \ r'''$i_1$ some text \$escaped\$ $i_2$ \(\LaTeX\) \\(escaped\) $$m_1$$ text $$m_2$$ \[m_3\] text \[m_4\] \( \sin \alpha \) text \( \sin \beta \) \[ \alpha \] text \[ \beta \] \$$escaped\$$ \\[escaped\] ''' mathjax_output = \ r'''

some text $escaped$

\(escaped)

text

text

text

text

$$escaped$$ \[escaped]

''' # noqa: E501 mathjax_multiline_source = \ r''' $$ \TeX \LaTeX $$ ''' mathjax_multiline_output = \ r'''

''' mathjax_multilevel_source = \ r''' \begin{equation*} \begin{pmatrix} 1 & 0\\ 0 & 1 \end{pmatrix} \end{equation*} ''' mathjax_multilevel_output = \ r'''

''' @unittest.skipUnless(MarkdownMarkup.available(), 'Markdown not available') class MarkdownTest(unittest.TestCase): maxDiff = None def setUp(self) -> None: warnings.simplefilter("ignore", Warning) def test_empty_file(self) -> None: markup = MarkdownMarkup() self.assertEqual(markup.convert('').get_document_body(), '\n') def test_extensions_loading(self) -> None: markup = MarkdownMarkup() self.assertIsNone(markup._canonicalize_extension_name('nonexistent')) self.assertIsNone(markup._canonicalize_extension_name( 'nonexistent(someoption)')) self.assertIsNone(markup._canonicalize_extension_name('.foobar')) self.assertEqual(markup._canonicalize_extension_name( 'meta'), 'markdown.extensions.meta') name, parameters = markup._split_extension_config( 'toc(anchorlink=1, foo=bar)') self.assertEqual(name, 'toc') self.assertEqual(parameters, {'anchorlink': '1', 'foo': 'bar'}) def test_loading_extensions_by_module_name(self) -> None: markup = MarkdownMarkup(extensions=['markdown.extensions.footnotes']) source = ('Footnotes[^1] have a label and the content.\n\n' '[^1]: This is a footnote content.') html = markup.convert(source).get_document_body() self.assertIn(' None: markup = MarkdownMarkup( extensions=['remove_extra', 'toc', 'markdown.extensions.toc']) self.assertEqual(len(markup.extensions), 1) self.assertIn('markdown.extensions.toc', markup.extensions) def test_extensions_parameters(self) -> None: markup = MarkdownMarkup(extensions=['toc(anchorlink=1)']) html = markup.convert('## Header').get_document_body() self.assertEqual(html, '\n') self.assertEqual( _canonicalized_ext_names['toc'], 'markdown.extensions.toc') def test_document_extensions_parameters(self) -> None: markup = MarkdownMarkup(extensions=[]) toc_header = '\n\n' html = markup.convert(toc_header + '## Header').get_document_body() self.assertEqual(html, toc_header + '\n') toc_header = ('\n\n') html = markup.convert( toc_header + '[TOC]\n\n# Header\n[[Link]]').get_document_body() self.assertEqual(html, toc_header + '
' 'Table of contents\n
\n' '\n' '

Link

\n') def test_document_extensions_change(self) -> None: """Extensions from document should be replaced on each run, not added.""" markup = MarkdownMarkup(extensions=[]) toc_header = '\n\n' content = '[TOC]\n\n# Header' html = markup.convert(toc_header + content).get_document_body() self.assertNotIn('

[TOC]

', html) html = markup.convert(content).get_document_body() self.assertIn('

[TOC]

', html) html = markup.convert(toc_header + content).get_document_body() self.assertNotIn('

[TOC]

', html) def test_extra(self) -> None: markup = MarkdownMarkup() html = markup.convert(tables_source).get_document_body() self.assertEqual(tables_output, html) html = markup.convert(deflists_source).get_document_body() self.assertEqual(deflists_output, html) def test_remove_extra(self) -> None: markup = MarkdownMarkup(extensions=['remove_extra']) html = markup.convert(tables_source).get_document_body() self.assertNotIn('', html) def test_remove_extra_document_extension(self) -> None: markup = MarkdownMarkup(extensions=[]) html = markup.convert( 'Required-Extensions: remove_extra\n\n' + tables_source).get_document_body() self.assertNotIn('
', html) def test_remove_extra_double(self) -> None: """Removing extra twice should not cause a crash.""" markup = MarkdownMarkup(extensions=['remove_extra']) markup.convert('Required-Extensions: remove_extra\n') def test_remove_extra_removes_mathjax(self) -> None: markup = MarkdownMarkup(extensions=['remove_extra']) html = markup.convert('$$1$$').get_document_body() self.assertNotIn('math/tex', html) def test_meta(self) -> None: markup = MarkdownMarkup() text = ('Required-Extensions: meta\n' 'Title: Hello, world!\n\n' 'Some text here.') title = markup.convert(text).get_document_title() self.assertEqual('Hello, world!', title) def test_default_math(self) -> None: # by default $...$ delimiter should be disabled markup = MarkdownMarkup(extensions=[]) self.assertEqual( '

$1$

\n', markup.convert('$1$').get_document_body()) self.assertEqual('

\n' '\n' '

\n', markup.convert('$$1$$').get_document_body()) def test_mathjax(self) -> None: markup = MarkdownMarkup(extensions=['mathjax']) # Escaping should work self.assertEqual('', markup.convert( 'Hello, \\$2+2$!').get_javascript()) js = markup.convert(mathjax_source).get_javascript() self.assertIn(' None: markup = MarkdownMarkup() text = mathjax_header + mathjax_source body = markup.convert(text).get_document_body() self.assertEqual(mathjax_header + mathjax_output, body) def test_mathjax_multiline(self) -> None: markup = MarkdownMarkup(extensions=['mathjax']) body = markup.convert(mathjax_multiline_source).get_document_body() self.assertEqual(mathjax_multiline_output, body) def test_mathjax_multilevel(self) -> None: markup = MarkdownMarkup() body = markup.convert(mathjax_multilevel_source).get_document_body() self.assertEqual(mathjax_multilevel_output, body) def test_mathjax_asciimath(self) -> None: markup = MarkdownMarkup(extensions=['mdx_math(use_asciimath=1)']) converted = markup.convert(r'\( [[a,b],[c,d]] \)') body = converted.get_document_body() self.assertIn('