``."""
return '\n' + text + '\n'
def table(self, header, body):
"""Rendering table element. Wrap header and body in it.
:param header: header part of the table.
:param body: body part of the table.
"""
table = '\n.. list-table::\n'
if header and not header.isspace():
table = (table + self.indent + ':header-rows: 1\n\n' +
self._indent_block(header) + '\n')
else:
table = table + '\n'
table = table + self._indent_block(body) + '\n\n'
return table
def table_row(self, content):
"""Rendering a table row. Like ``
``.
:param content: content of current table row.
"""
contents = content.splitlines()
if not contents:
return ''
clist = ['* ' + contents[0]]
if len(contents) > 1:
for c in contents[1:]:
clist.append(' ' + c)
return '\n'.join(clist) + '\n'
def table_cell(self, content, **flags):
"""Rendering a table cell. Like ```` `` | ``.
:param content: content of current table cell.
:param header: whether this is header or not.
:param align: align of current table cell.
"""
return '- ' + content + '\n'
def double_emphasis(self, text):
"""Rendering **strong** text.
:param text: text content for emphasis.
"""
return r'\ **{}**\ '.format(text)
def emphasis(self, text):
"""Rendering *emphasis* text.
:param text: text content for emphasis.
"""
return r'\ *{}*\ '.format(text)
def codespan(self, text):
"""Rendering inline `code` text.
:param text: text content for inline code.
"""
if '``' not in text:
return r'\ ``{}``\ '.format(text)
else:
# actually, docutils split spaces in literal
return self._raw_html(
''
'{}'
' '.format(text.replace('`', '`')))
def linebreak(self):
"""Rendering line break like `` ``."""
if self.options.get('use_xhtml'):
return self._raw_html(' ') + '\n'
return self._raw_html(' ') + '\n'
def strikethrough(self, text):
"""Rendering ~~strikethrough~~ text.
:param text: text content for strikethrough.
"""
return self._raw_html('{}'.format(text))
def text(self, text):
"""Rendering unformatted text.
:param text: text content.
"""
return text
def autolink(self, link, is_email=False):
"""Rendering a given link or email address.
:param link: link content or email address.
:param is_email: whether this is an email or not.
"""
return link
def link(self, link, title, text):
"""Rendering a given link with content and title.
:param link: href link for ```` tag.
:param title: title content for `title` attribute.
:param text: text content for description.
"""
if self.anonymous_references:
underscore = '__'
else:
underscore = '_'
if title:
return self._raw_html(
'{text}'.format(
link=link, title=title, text=text
)
)
if not self.parse_relative_links:
return r'\ `{text} <{target}>`{underscore}\ '.format(
target=link,
text=text,
underscore=underscore
)
else:
url_info = urlparse(link)
if url_info.scheme:
return r'\ `{text} <{target}>`{underscore}\ '.format(
target=link,
text=text,
underscore=underscore
)
else:
link_type = 'doc'
anchor = url_info.fragment
if url_info.fragment:
if url_info.path:
# Can't link to anchors via doc directive.
anchor = ''
else:
# Example: [text](#anchor)
link_type = 'ref'
doc_link = '{doc_name}{anchor}'.format(
# splittext approach works whether or not path is set. It
# will return an empty string if unset, which leads to
# anchor only ref.
doc_name=os.path.splitext(url_info.path)[0],
anchor=anchor
)
return r'\ :{link_type}:`{text} <{doc_link}>`\ '.format(
link_type=link_type,
doc_link=doc_link,
text=text
)
def image(self, src, title, text):
"""Rendering a image with title and text.
:param src: source link of the image.
:param title: title text of the image.
:param text: alt text of the image.
"""
# rst does not support title option
# and I couldn't find title attribute in HTML standard
return '\n'.join([
'',
'.. image:: {}'.format(src),
' :target: {}'.format(src),
' :alt: {}'.format(text),
'',
])
def inline_html(self, html):
"""Rendering span level pure html content.
:param html: text content of the html snippet.
"""
return self._raw_html(html)
def newline(self):
"""Rendering newline element."""
return ''
def footnote_ref(self, key, index):
"""Rendering the ref anchor of a footnote.
:param key: identity key for the footnote.
:param index: the index count of current footnote.
"""
return r'\ [#fn-{}]_\ '.format(key)
def footnote_item(self, key, text):
"""Rendering a footnote item.
:param key: identity key for the footnote.
:param text: text content of the footnote.
"""
return '.. [#fn-{0}] {1}\n'.format(key, text.strip())
def footnotes(self, text):
"""Wrapper for all footnotes.
:param text: contents of all footnotes.
"""
if text:
return '\n\n' + text
else:
return ''
"""Below outputs are for rst."""
def image_link(self, url, target, alt):
return '\n'.join([
'',
'.. image:: {}'.format(url),
' :target: {}'.format(target),
' :alt: {}'.format(alt),
'',
])
def rest_role(self, text):
return text
def rest_link(self, text):
return text
def inline_math(self, math):
"""Extension of recommonmark"""
return r'\ :math:`{}`\ '.format(math)
def eol_literal_marker(self, marker):
"""Extension of recommonmark"""
return marker
def directive(self, text):
return '\n' + text
def rest_code_block(self):
return '\n\n'
class M2R(mistune.Markdown):
def __init__(self, renderer=None, inline=RestInlineLexer,
block=RestBlockLexer, **kwargs):
if renderer is None:
renderer = RestRenderer(**kwargs)
super(M2R, self).__init__(renderer, inline=inline, block=block,
**kwargs)
def parse(self, text):
output = super(M2R, self).parse(text)
return self.post_process(output)
def output_directive(self):
return self.renderer.directive(self.token['text'])
def output_rest_code_block(self):
return self.renderer.rest_code_block()
def post_process(self, text):
output = (text
.replace('\\ \n', '\n')
.replace('\n\\ ', '\n')
.replace(' \\ ', ' ')
.replace('\\ ', ' ')
.replace('\\ .', '.')
)
if self.renderer._include_raw_html:
return prolog + output
else:
return output
class M2RParser(rst.Parser, object):
# Explicitly tell supported formats to sphinx
supported = ('markdown', 'md', 'mkd')
def parse(self, inputstrings, document):
if isinstance(inputstrings, statemachine.StringList):
inputstring = '\n'.join(inputstrings)
else:
inputstring = inputstrings
config = document.settings.env.config
converter = M2R(
no_underscore_emphasis=config.no_underscore_emphasis,
parse_relative_links=config.m2r_parse_relative_links,
anonymous_references=config.m2r_anonymous_references,
disable_inline_math=config.m2r_disable_inline_math
)
super(M2RParser, self).parse(converter(inputstring), document)
class MdInclude(rst.Directive):
"""Directive class to include markdown in sphinx.
Load a file and convert it to rst and insert as a node. Currently
directive-specific options are not implemented.
"""
required_arguments = 1
optional_arguments = 0
option_spec = {
'start-line': int,
'end-line': int,
}
def run(self):
"""Most of this method is from ``docutils.parser.rst.Directive``.
docutils version: 0.12
"""
if not self.state.document.settings.file_insertion_enabled:
raise self.warning('"%s" directive disabled.' % self.name)
source = self.state_machine.input_lines.source(
self.lineno - self.state_machine.input_offset - 1)
source_dir = os.path.dirname(os.path.abspath(source))
path = rst.directives.path(self.arguments[0])
path = os.path.normpath(os.path.join(source_dir, path))
path = utils.relative_path(None, path)
path = nodes.reprunicode(path)
# get options (currently not use directive-specific options)
encoding = self.options.get(
'encoding', self.state.document.settings.input_encoding)
e_handler = self.state.document.settings.input_encoding_error_handler
tab_width = self.options.get(
'tab-width', self.state.document.settings.tab_width)
# open the including file
try:
self.state.document.settings.record_dependencies.add(path)
include_file = io.FileInput(source_path=path,
encoding=encoding,
error_handler=e_handler)
except UnicodeEncodeError:
raise self.severe('Problems with "%s" directive path:\n'
'Cannot encode input file path "%s" '
'(wrong locale?).' %
(self.name, path))
except IOError as error:
raise self.severe('Problems with "%s" directive path:\n%s.' %
(self.name, io.error_string(error)))
# read from the file
startline = self.options.get('start-line', None)
endline = self.options.get('end-line', None)
try:
if startline or (endline is not None):
lines = include_file.readlines()
rawtext = ''.join(lines[startline:endline])
else:
rawtext = include_file.read()
except UnicodeError as error:
raise self.severe('Problem with "%s" directive:\n%s' %
(self.name, io.error_string(error)))
config = self.state.document.settings.env.config
converter = M2R(
no_underscore_emphasis=config.no_underscore_emphasis,
parse_relative_links=config.m2r_parse_relative_links,
anonymous_references=config.m2r_anonymous_references,
disable_inline_math=config.m2r_disable_inline_math
)
include_lines = statemachine.string2lines(converter(rawtext),
tab_width,
convert_whitespace=True)
self.state_machine.insert_input(include_lines, path)
return []
def setup(app):
"""When used for sphinx extension."""
global _is_sphinx
_is_sphinx = True
app.add_config_value('no_underscore_emphasis', False, 'env')
app.add_config_value('m2r_parse_relative_links', False, 'env')
app.add_config_value('m2r_anonymous_references', False, 'env')
app.add_config_value('m2r_disable_inline_math', False, 'env')
if hasattr(app, 'add_source_suffix'):
app.add_source_suffix('.md', 'markdown')
app.add_source_parser(M2RParser)
else:
app.add_source_parser('.md', M2RParser)
app.add_directive('mdinclude', MdInclude)
metadata = dict(
version=__version__,
parallel_read_safe=True,
parallel_write_safe=True,
)
return metadata
def convert(text, **kwargs):
return M2R(**kwargs)(text)
def parse_from_file(file, encoding='utf-8', **kwargs):
if not os.path.exists(file):
raise OSError('No such file exists: {}'.format(file))
with open(file, encoding=encoding) as f:
src = f.read()
output = convert(src, **kwargs)
return output
def save_to_file(file, src, encoding='utf-8', **kwargs):
target = os.path.splitext(file)[0] + '.rst'
if not options.overwrite and os.path.exists(target):
confirm = input('{} already exists. overwrite it? [y/n]: '.format(
target))
if confirm.upper() not in ('Y', 'YES'):
print('skip {}'.format(file))
return
with open(target, 'w', encoding=encoding) as f:
f.write(src)
def main():
parse_options() # parse cli options
if not options.input_file:
parser.print_help()
parser.exit(0)
for file in options.input_file:
output = parse_from_file(file)
if options.dry_run:
print(output)
else:
save_to_file(file, output)
if __name__ == '__main__':
main()
m2r-0.3.1/requirements-dev.txt 0000664 0000000 0000000 00000000065 14335365355 0016270 0 ustar 00root root 0000000 0000000 -r requirements-test.txt
doit
green
tox
livereload
m2r-0.3.1/requirements-test.txt 0000664 0000000 0000000 00000000064 14335365355 0016470 0 ustar 00root root 0000000 0000000 coverage
sphinx
flake8
mock; python_version < '3.3'
m2r-0.3.1/setup.py 0000664 0000000 0000000 00000002616 14335365355 0013746 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
# -*- coding: utf-8 -*-
from os import path
try:
from setuptools import setup
except ImportError:
from distutils.core import setup
readme_file = path.join(path.dirname(path.abspath(__file__)), 'README.md')
try:
from m2r import parse_from_file
readme = parse_from_file(readme_file)
except ImportError:
with open(readme_file) as f:
readme = f.read()
install_requires = ['mistune<2', 'docutils']
test_requirements = ['pygments']
setup(
name='m2r',
version='0.3.1',
description='Markdown and reStructuredText in a single file.',
long_description=readme,
author='Hiroyuki Takagi',
author_email='miyako.dev@gmail.com',
url='https://github.com/miyakogi/m2r',
py_modules=['m2r'],
entry_points={'console_scripts': 'm2r = m2r:main'},
include_package_data=True,
license="MIT",
zip_safe=False,
keywords='Markdown reStructuredText sphinx-extension',
classifiers=[
'Development Status :: 4 - Beta',
'Framework :: Sphinx :: Extension',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Natural Language :: English',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Topic :: Text Processing',
],
install_requires=install_requires,
test_suite='tests',
tests_require=test_requirements,
)
m2r-0.3.1/tests/ 0000775 0000000 0000000 00000000000 14335365355 0013371 5 ustar 00root root 0000000 0000000 m2r-0.3.1/tests/__init__.py 0000664 0000000 0000000 00000000000 14335365355 0015470 0 ustar 00root root 0000000 0000000 m2r-0.3.1/tests/test.md 0000664 0000000 0000000 00000000204 14335365355 0014666 0 ustar 00root root 0000000 0000000 # Title
## SubTitle
__content__
## サブタイトル
[A link to GitHub](http://github.com/)
This is `$E = mc^2$` inline math.
m2r-0.3.1/tests/test.rst 0000664 0000000 0000000 00000000237 14335365355 0015104 0 ustar 00root root 0000000 0000000
Title
=====
SubTitle
--------
**content**
サブタイトル
------------
`A link to GitHub `_
This is :math:`E = mc^2` inline math.
m2r-0.3.1/tests/test_cli.py 0000664 0000000 0000000 00000011123 14335365355 0015547 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import print_function, unicode_literals
import sys
import os
from os import path
from copy import copy
from unittest import TestCase
import subprocess
from m2r import parse_from_file, main, options
from unittest.mock import patch
_builtin = 'builtins'
curdir = path.dirname(path.abspath(__file__))
test_md = path.join(curdir, 'test.md')
test_rst = path.join(curdir, 'test.rst')
class TestConvert(TestCase):
def setUp(self):
# reset cli options
options.overwrite = False
options.dry_run = False
options.no_underscore_emphasis = False
options.anonymous_references = False
options.disable_inline_math = False
self._orig_argv = copy(sys.argv)
if path.exists(test_rst):
with open(test_rst) as f:
self._orig_rst = f.read()
def tearDown(self):
sys.argv = self._orig_argv
with open(test_rst, 'w') as f:
f.write(self._orig_rst)
def test_no_file(self):
p = subprocess.Popen(
[sys.executable, '-m', 'm2r'],
stdout=subprocess.PIPE,
)
p.wait()
self.assertEqual(p.returncode, 0)
with p.stdout as buffer:
message = buffer.read().decode()
self.assertIn('usage', message)
self.assertIn('underscore-emphasis', message)
self.assertIn('anonymous-references', message)
self.assertIn('inline-math', message)
self.assertIn('options:', message)
def test_parse_file(self):
output = parse_from_file(test_md)
with open(test_rst) as f:
expected = f.read()
self.assertEqual(output.strip(), expected.strip())
def test_dryrun(self):
sys.argv = [sys.argv[0], '--dry-run', test_md]
target_file = path.join(curdir, 'test.rst')
with open(target_file) as f:
rst = f.read()
os.remove(target_file)
self.assertFalse(path.exists(target_file))
with patch(_builtin + '.print') as m:
main()
self.assertFalse(path.exists(target_file))
m.assert_called_once_with(rst)
def test_write_file(self):
sys.argv = [sys.argv[0], test_md]
target_file = path.join(curdir, 'test.rst')
os.remove(target_file)
self.assertFalse(path.exists(target_file))
main()
self.assertTrue(path.exists(target_file))
def test_overwrite_file(self):
sys.argv = [sys.argv[0], test_md]
target_file = path.join(curdir, 'test.rst')
with open(target_file, 'w') as f:
f.write('test')
with open(target_file) as f:
first_line = f.readline()
self.assertIn('test', first_line)
with patch(_builtin + '.input', return_value='y'):
main()
self.assertTrue(path.exists(target_file))
with open(target_file) as f:
first_line = f.readline()
self.assertNotIn('test', first_line)
def test_overwrite_option(self):
sys.argv = [sys.argv[0], '--overwrite', test_md]
target_file = path.join(curdir, 'test.rst')
with open(target_file, 'w') as f:
f.write('test')
with open(target_file) as f:
first_line = f.readline()
self.assertIn('test', first_line)
with patch(_builtin + '.input', return_value='y') as m_input:
with patch(_builtin + '.print') as m_print:
main()
self.assertTrue(path.exists(target_file))
self.assertFalse(m_input.called)
self.assertFalse(m_print.called)
with open(target_file) as f:
first_line = f.readline()
self.assertNotIn('test', first_line)
def test_underscore_option(self):
sys.argv = [
sys.argv[0], '--no-underscore-emphasis', '--dry-run', test_md]
with patch(_builtin + '.print') as m:
main()
self.assertIn('__content__', m.call_args[0][0])
self.assertNotIn('**content**', m.call_args[0][0])
def test_anonymous_reference_option(self):
sys.argv = [
sys.argv[0], '--anonymous-references', '--dry-run', test_md]
with patch(_builtin + '.print') as m:
main()
self.assertIn("`A link to GitHub `__",
m.call_args[0][0])
def test_disable_inline_math(self):
sys.argv = [
sys.argv[0], '--disable-inline-math', '--dry-run', test_md]
with patch(_builtin + '.print') as m:
main()
self.assertIn('``$E = mc^2$``', m.call_args[0][0])
self.assertNotIn(':math:', m.call_args[0][0])
m2r-0.3.1/tests/test_renderer.py 0000664 0000000 0000000 00000053424 14335365355 0016620 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import print_function, unicode_literals
from unittest import TestCase, skip
from docutils.core import Publisher
from docutils import io
from m2r import prolog, convert
class RendererTestBase(TestCase):
def conv(self, src, **kwargs):
out = convert(src, **kwargs)
self.check_rst(out)
return out
def conv_no_check(self, src, **kwargs):
out = convert(src, **kwargs)
return out
def check_rst(self, rst):
pub = Publisher(reader=None, parser=None, writer=None, settings=None,
source_class=io.StringInput,
destination_class=io.StringOutput)
pub.set_components(reader_name='standalone',
parser_name='restructuredtext',
writer_name='pseudoxml')
pub.process_programmatic_settings(
settings_spec=None,
settings_overrides={'output_encoding': 'unicode'},
config_section=None,
)
pub.set_source(rst, source_path=None)
pub.set_destination(destination=None, destination_path=None)
output = pub.publish(enable_exit_status=False)
self.assertLess(pub.document.reporter.max_level, 0)
return output, pub
class TestBasic(RendererTestBase):
def test_fail_rst(self):
with self.assertRaises(AssertionError):
# This check should be failed and report warning
self.check_rst('```')
def test_simple_paragraph(self):
src = 'this is a sentence.\n'
out = self.conv(src)
self.assertEqual(out, '\n' + src)
def test_multiline_paragraph(self):
src = '\n'.join([
'first sentence.',
'second sentence.',
])
out = self.conv(src)
self.assertEqual(out, '\n' + src + '\n')
def test_multi_paragraph(self):
src = '\n'.join([
'first paragraph.',
'',
'second paragraph.',
])
out = self.conv(src)
self.assertEqual(out, '\n' + src + '\n')
def test_hr(self):
src = 'a\n\n---\n\nb'
out = self.conv(src)
self.assertEqual(out, '\na\n\n----\n\nb\n')
def test_linebreak(self):
src = 'abc def \nghi'
out = self.conv(src)
self.assertEqual(
out,
prolog + '\nabc def\\ :raw-html-m2r:` `\nghi' + '\n',
)
class TestInlineMarkdown(RendererTestBase):
def test_inline_code(self):
src = '`a`'
out = self.conv(src)
self.assertEqual(out.replace('\n', ''), '``a``')
def test_inline_code_with_backticks(self):
src = '```a``a```'
out = self.conv(src)
self.assertEqual(out.strip(),
'.. role:: raw-html-m2r(raw)\n'
' :format: html\n\n\n'
':raw-html-m2r:`'
'a``a `'
)
def test_strikethrough(self):
src = ('~~a~~')
self.conv(src)
def test_emphasis(self):
src = '*a*'
out = self.conv(src)
self.assertEqual(out.replace('\n', ''), '*a*')
def test_emphasis_(self):
src = '_a_'
out = self.conv(src)
self.assertEqual(out.replace('\n', ''), '*a*')
def test_emphasis_no_(self):
src = '_a_'
out = self.conv(src, no_underscore_emphasis=True)
self.assertEqual(out.replace('\n', ''), '_a_')
def test_double_emphasis(self):
src = '**a**'
out = self.conv(src)
self.assertEqual(out.replace('\n', ''), '**a**')
def test_double_emphasis__(self):
src = '__a__'
out = self.conv(src)
self.assertEqual(out.replace('\n', ''), '**a**')
def test_emphasis_no__(self):
src = '__a__'
out = self.conv(src, no_underscore_emphasis=True)
self.assertEqual(out.replace('\n', ''), '__a__')
def test_autolink(self):
src = 'link to http://example.com/ in sentence.'
out = self.conv(src)
self.assertEqual(out, '\n' + src + '\n')
def test_link(self):
src = 'this is a [link](http://example.com/).'
out = self.conv(src)
self.assertEqual(
out, '\nthis is a `link `_.\n')
def test_anonymous_link(self):
src = 'this is a [link](http://example.com/).'
out = self.conv(src, anonymous_references=True)
self.assertEqual(
out, '\nthis is a `link `__.\n')
def test_link_with_rel_link_enabled(self):
src = 'this is a [link](http://example.com/).'
out = self.conv_no_check(
src,
parse_relative_links=True
)
self.assertEqual(
out, '\nthis is a `link `_.\n')
def test_anonymous_link_with_rel_link_enabled(self):
src = 'this is a [link](http://example.com/).'
out = self.conv_no_check(
src,
parse_relative_links=True,
anonymous_references=True
)
self.assertEqual(
out, '\nthis is a `link `__.\n')
def test_anchor(self):
src = 'this is an [anchor](#anchor).'
out = self.conv_no_check(
src,
parse_relative_links=True
)
self.assertEqual(
out, '\nthis is an :ref:`anchor `.\n')
def test_relative_link(self):
src = 'this is a [relative link](a_file.md).'
out = self.conv_no_check(
src,
parse_relative_links=True
)
self.assertEqual(
out, '\nthis is a :doc:`relative link `.\n')
def test_relative_link_with_anchor(self):
src = 'this is a [relative link](a_file.md#anchor).'
out = self.conv_no_check(
src,
parse_relative_links=True
)
self.assertEqual(
out, '\nthis is a :doc:`relative link `.\n')
def test_link_title(self):
src = 'this is a [link](http://example.com/ "example").'
out = self.conv(src)
self.assertEqual(
out,
'.. role:: raw-html-m2r(raw)\n'
' :format: html\n\n\n'
'this is a :raw-html-m2r:'
'`link`.\n'
)
def test_image_link(self):
src = '[](link_target_url)'
out = self.conv(src)
self.assertEqual(
out,
'\n\n.. image:: image_taget_url\n'
' :target: link_target_url\n :alt: Alt Text\n\n',
)
def test_rest_role(self):
src = 'a :code:`some code` inline.'
out = self.conv(src)
self.assertEqual(out, '\n' + src + '\n')
def test_rest_role2(self):
src = 'a `some code`:code: inline.'
out = self.conv(src)
self.assertEqual(out, '\n' + src + '\n')
def test_rest_link(self):
src = 'a `RefLink `_ here.'
out = self.conv(src)
self.assertEqual(out, '\n' + src + '\n')
def test_rest_link_and_role(self):
src = 'a :code:`a` and `RefLink `_ here.'
out = self.conv(src)
self.assertEqual(out, '\n' + src + '\n')
def test_rest_link_and_role2(self):
src = 'a `a`:code: and `RefLink `_ here.'
out = self.conv(src)
self.assertEqual(out, '\n' + src + '\n')
def test_rest_role_incomplete(self):
src = 'a co:`de` and `RefLink `_ here.'
out = self.conv(src)
self.assertEqual(
out,
'\na co:\\ ``de`` and `RefLink `_ here.\n',
)
def test_rest_role_incomplete2(self):
src = 'a `RefLink `_ and co:`de` here.'
out = self.conv(src)
self.assertEqual(
out,
'\na `RefLink `_ and co:\\ ``de`` here.\n',
)
def test_rest_role_with_code(self):
src = 'a `code` and :code:`rest` here.'
out = self.conv(src)
self.assertEqual(out, '\na ``code`` and :code:`rest` here.\n')
def test_rest2_role_with_code(self):
src = 'a `code` and `rest`:code: here.'
out = self.conv(src)
self.assertEqual(out, '\na ``code`` and `rest`:code: here.\n')
def test_code_with_rest_role(self):
src = 'a :code:`rest` and `code` here.'
out = self.conv(src)
self.assertEqual(out, '\na :code:`rest` and ``code`` here.\n')
def test_code_with_rest_role2(self):
src = 'a `rest`:code: and `code` here.'
out = self.conv(src)
self.assertEqual(out, '\na `rest`:code: and ``code`` here.\n')
def test_rest_link_with_code(self):
src = 'a `RefLink `_ and `code` here.'
out = self.conv(src)
self.assertEqual(out, '\na `RefLink `_ and ``code`` here.\n')
def test_code_with_rest_link(self):
src = 'a `code` and `RefLink `_ here.'
out = self.conv(src)
self.assertEqual(out, '\na ``code`` and `RefLink `_ here.\n')
def test_inline_math(self):
src = 'this is `$E = mc^2$` inline math.'
out = self.conv(src)
self.assertEqual(out, '\nthis is :math:`E = mc^2` inline math.\n')
def test_disable_inline_math(self):
src = 'this is `$E = mc^2$` inline math.'
out = self.conv(src, disable_inline_math=True)
self.assertEqual(out, '\nthis is ``$E = mc^2$`` inline math.\n')
def test_inline_html(self):
src = 'this is html.'
out = self.conv(src)
self.assertEqual(
out, prolog + '\nthis is :raw-html-m2r:`html`.\n')
def test_block_html(self):
src = 'title'
out = self.conv(src)
self.assertEqual(out, '\n\n.. raw:: html\n\n title\n\n')
class TestBlockQuote(RendererTestBase):
def test_block_quote(self):
src = '> q1\n> q2'
out = self.conv(src)
self.assertEqual(out, '\n..\n\n q1\n q2\n\n')
def test_block_quote_nested(self):
src = '> q1\n> > q2'
out = self.conv(src)
# one extra empty line is inserted, but still valid rst anyway
self.assertEqual(out, '\n..\n\n q1\n\n ..\n\n q2\n\n')
@skip('markdown does not support dedent in block quote')
def test_block_quote_nested_2(self):
src = '> q1\n> > q2\n> q3'
out = self.conv(src)
self.assertEqual(out, '\n..\n\n q1\n\n ..\n q2\n\n q3\n\n')
class TestCodeBlock(RendererTestBase):
def test_plain_code_block(self):
src = '\n'.join([
'```',
'pip install sphinx',
'```',
])
out = self.conv(src)
self.assertEqual(out, '\n.. code-block::\n\n pip install sphinx\n')
def test_plain_code_block_tilda(self):
src = '\n'.join([
'~~~',
'pip install sphinx',
'~~~',
])
out = self.conv(src)
self.assertEqual(out, '\n.. code-block::\n\n pip install sphinx\n')
def test_code_block_math(self):
src = '\n'.join([
'```math',
'E = mc^2',
'```',
])
out = self.conv(src)
self.assertEqual(out, '\n.. math::\n\n E = mc^2\n')
def test_plain_code_block_indent(self):
src = '\n'.join([
'```',
'pip install sphinx',
' new line',
'```',
])
out = self.conv(src)
self.assertEqual(
out,
'\n.. code-block::\n\n pip install sphinx\n new line\n',
)
def test_python_code_block(self):
src = '\n'.join([
'```python',
'print(1)',
'```',
])
out = self.conv(src)
self.assertEqual(out, '\n.. code-block:: python\n\n print(1)\n')
def test_python_code_block_indent(self):
src = '\n'.join([
'```python',
'def a(i):',
' print(i)',
'```',
])
out = self.conv(src)
self.assertEqual(
out,
'\n.. code-block:: python\n\n def a(i):\n print(i)\n',
)
class TestImage(RendererTestBase):
def test_image(self):
src = ''
out = self.conv(src)
# first and last newline is inserted by paragraph
self.assertEqual(
out,
'\n\n.. image:: a.png\n :target: a.png\n :alt: alt text\n\n',
)
def test_image_title(self):
src = ''
self.conv(src)
# title is not supported now
class TestHeading(RendererTestBase):
def test_heading(self):
src = '# head 1'
out = self.conv(src)
self.assertEqual(out, '\nhead 1\n' + '=' * 6 + '\n')
def test_heading_multibyte(self):
src = '# マルチバイト文字\n'
out = self.conv(src)
self.assertEqual(out, '\nマルチバイト文字\n' + '=' * 16 + '\n')
class TestList(RendererTestBase):
def test_ul(self):
src = '* list'
out = self.conv(src)
self.assertEqual(out, '\n\n* list\n')
def test_ol(self):
src = '1. list'
out = self.conv(src)
self.assertEqual(out, '\n\n#. list\n')
def test_nested_ul(self):
src = '\n'.join([
'* list 1',
'* list 2',
' * list 2.1',
' * list 2.2',
'* list 3',
])
out = self.conv(src)
self.assertEqual(
out,
'\n\n* list 1\n'
'* list 2\n\n'
' * list 2.1\n'
' * list 2.2\n\n'
'* list 3\n',
)
def test_nested_ul_2(self):
src = '\n'.join([
'* list 1',
'* list 2',
' * list 2.1',
' * list 2.2',
' * list 2.2.1',
' * list 2.2.2',
'* list 3',
])
out = self.conv(src)
self.assertEqual(
out,
'\n\n* list 1\n'
'* list 2\n\n'
' * list 2.1\n'
' * list 2.2\n\n'
' * list 2.2.1\n'
' * list 2.2.2\n\n'
'* list 3\n'
)
def test_nested_ol(self):
src = '\n'.join([
'1. list 1',
'2. list 2',
' 2. list 2.1',
' 3. list 2.2',
'3. list 3',
])
out = self.conv(src)
self.assertEqual(
out,
'\n\n#. list 1\n'
'#. list 2\n'
'\n'
' #. list 2.1\n'
' #. list 2.2\n'
'\n'
'#. list 3\n',
)
def test_nested_ol_2(self):
src = '\n'.join([
'1. list 1',
'2. list 2',
' 3. list 2.1',
' 4. list 2.2',
' 5. list 2.2.1',
' 6. list 2.2.2',
'7. list 3',
])
out = self.conv(src)
self.assertEqual(
out,
'\n'.join([
'\n\n#. list 1',
'#. list 2',
'',
' #. list 2.1',
' #. list 2.2',
'',
' #. list 2.2.1',
' #. list 2.2.2',
'',
'#. list 3\n',
])
)
def test_nested_mixed_1(self):
src = '\n'.join([
'1. list 1',
'2. list 2',
' * list 2.1',
' * list 2.2',
' 1. list 2.2.1',
' 2. list 2.2.2',
'7. list 3',
])
out = self.conv(src)
self.assertEqual(
out,
'\n'.join([
'\n\n#. list 1',
'#. list 2',
'',
' * list 2.1',
' * list 2.2',
'',
' #. list 2.2.1',
' #. list 2.2.2',
'',
'#. list 3\n',
])
)
def test_nested_multiline_1(self):
src = '\n'.join([
'* list 1',
' list 1 cont',
'* list 2',
' list 2 cont',
' * list 2.1',
' list 2.1 cont',
' * list 2.2',
' list 2.2 cont',
' * list 2.2.1',
' * list 2.2.2',
'* list 3',
])
out = self.conv(src)
self.assertEqual(
out,
'\n'.join([
'\n\n* list 1',
' list 1 cont',
'* list 2',
' list 2 cont',
'',
' * list 2.1',
' list 2.1 cont',
' * list 2.2',
' list 2.2 cont',
'',
' * list 2.2.1',
' * list 2.2.2',
'',
'* list 3\n',
])
)
def test_nested_multiline_2(self):
src = '\n'.join([
'1. list 1',
' list 1 cont',
'1. list 2',
' list 2 cont',
' 1. list 2.1',
' list 2.1 cont',
' 1. list 2.2',
' list 2.2 cont',
' 1. list 2.2.1',
' 1. list 2.2.2',
'1. list 3',
])
out = self.conv(src)
self.assertEqual(
out,
'\n'.join([
'\n\n#. list 1',
' list 1 cont',
'#. list 2',
' list 2 cont',
'',
' #. list 2.1',
' list 2.1 cont',
' #. list 2.2',
' list 2.2 cont',
'',
' #. list 2.2.1',
' #. list 2.2.2',
'',
'#. list 3\n',
])
)
def test_nested_multiline_3(self):
src = '\n'.join([
'1. list 1',
' list 1 cont',
'1. list 2',
' list 2 cont',
' * list 2.1',
' list 2.1 cont',
' * list 2.2',
' list 2.2 cont',
' 1. list 2.2.1',
' 1. list 2.2.2',
'1. list 3',
])
out = self.conv(src)
self.assertEqual(
out,
'\n'.join([
'\n\n#. list 1',
' list 1 cont',
'#. list 2',
' list 2 cont',
'',
' * list 2.1',
' list 2.1 cont',
' * list 2.2',
' list 2.2 cont',
'',
' #. list 2.2.1',
' #. list 2.2.2',
'',
'#. list 3\n',
])
)
class TestConplexText(RendererTestBase):
def test_code(self):
src = '''
some sentence
```python
print(1)
```
some sentence
# title
```python
print(1)
```
---
end
'''
self.conv(src)
class TestTable(RendererTestBase):
def test_table(self):
src = '''h1 | h2 | h3\n--- | --- | ---\n1 | 2 | 3\n4 | 5 | 6'''
out = self.conv(src)
self.assertEqual(out, '\n'.join([
'',
'.. list-table::',
' :header-rows: 1',
'',
' * - h1',
' - h2',
' - h3',
' * - 1',
' - 2',
' - 3',
' * - 4',
' - 5',
' - 6',
'',
'',
]))
class TestFootNote(RendererTestBase):
def test_footnote(self):
src = '\n'.join([
'This is a[^1] footnote[^2] ref[^ref] with rst [#a]_.',
'',
'[^1]: note 1',
'[^2]: note 2',
'[^ref]: note ref',
'.. [#a] note rst',
])
out = self.conv(src)
self.assertEqual(out, '\n'.join([
'',
'This is a\\ [#fn-1]_ '
'footnote\\ [#fn-2]_ ref\\ [#fn-ref]_ with rst [#a]_.',
'',
'.. [#a] note rst', # one empty line inserted...
'',
'.. [#fn-1] note 1',
'.. [#fn-2] note 2',
'.. [#fn-ref] note ref',
'',
]))
def test_sphinx_ref(self):
src = 'This is a sphinx [ref]_ global ref.\n\n.. [ref] ref text'
out = self.conv(src)
self.assertEqual(out, '\n' + src)
class TestDirective(RendererTestBase):
def test_comment_oneline(self):
src = '.. a'
out = self.conv(src)
self.assertEqual(out, '\n.. a')
def test_comment_indented(self):
src = ' .. a'
out = self.conv(src)
self.assertEqual(out, '\n .. a')
def test_comment_newline(self):
src = '..\n\n comment\n\nnewline'
out = self.conv(src)
self.assertEqual(out, '\n..\n\n comment\n\nnewline\n')
def test_comment_multiline(self):
comment = (
'.. this is comment.\n'
' this is also comment.\n'
'\n'
'\n'
' comment may include empty line.\n'
'\n\n')
src = comment + '`eoc`'
out = self.conv(src)
self.assertEqual(out, '\n' + comment + '``eoc``\n')
class TestRestCode(RendererTestBase):
def test_rest_code_block_empty(self):
src = '\n\n::\n\n'
out = self.conv(src)
self.assertEqual(out, '\n\n')
def test_eol_marker(self):
src = 'a::\n\n code\n'
out = self.conv(src)
self.assertEqual(out, '\na:\n\n.. code-block::\n\n code\n')
def test_eol_marker_remove(self):
src = 'a ::\n\n code\n'
out = self.conv(src)
self.assertEqual(out, '\na\n\n.. code-block::\n\n code\n')
m2r-0.3.1/tox.ini 0000664 0000000 0000000 00000001374 14335365355 0013547 0 ustar 00root root 0000000 0000000 [tox]
envlist = py{37,38,39,310,py,py3},sphinx-dev,flake8
[testenv]
whitelist_externals =
make
deps =
-rrequirements-test.txt
commands =
coverage run -m unittest discover tests
coverage run -a -m sphinx -b html -d docs/_build/toctree -E -W -n -j auto -q docs docs/_build/html
coverage report
make clean
[testenv:sphinx-dev]
recreate = true
deps =
git+https://github.com/sphinx-doc/sphinx
commands =
sphinx-build -b html -d docs/_build/toctree -E -W -n -j auto docs docs/_build/html
make clean
[testenv:flake8]
deps =
flake8
commands =
flake8 m2r.py setup.py tests
[testenv:codecov]
passenv = CI TRAVIS TRAVIS_*
deps = codecov
skip_install = true
commands =
codecov
[flake8]
exclude = docs/*,testdoc/*,.git,__pycache__,.tox,.eggs,*.egg
|