Skip to content

Commit

Permalink
Fix sphinx-doc#7301: capital characters are not allowed for node_id
Browse files Browse the repository at this point in the history
  • Loading branch information
tk0miya committed Mar 29, 2020
1 parent 70c61e4 commit 7aa5584
Show file tree
Hide file tree
Showing 10 changed files with 76 additions and 75 deletions.
1 change: 1 addition & 0 deletions CHANGES
Expand Up @@ -26,6 +26,7 @@ Bugs fixed
* #6477: Escape first "!" in a cross reference linking no longer possible
* #7219: py domain: The index entry generated by ``py:function`` directive is
different with one from ``index`` directive with "builtin" type
* #7301: capital characters are not allowed for node_id

Testing
--------
Expand Down
6 changes: 3 additions & 3 deletions sphinx/util/nodes.py
Expand Up @@ -445,15 +445,15 @@ def _make_id(string: str) -> str:
Changes:
* Allow to use capital alphabet characters
* Allow to use dots (".") and underscores ("_") for an identifier
without a leading character.
# Author: David Goodger <goodger@python.org>
# Maintainer: docutils-develop@lists.sourceforge.net
# Copyright: This module has been placed in the public domain.
"""
id = string.lower()
id = id.translate(_non_id_translate_digraphs)
id = string.translate(_non_id_translate_digraphs)
id = id.translate(_non_id_translate)
# get rid of non-ascii characters.
# 'ascii' lowercase to prevent problems with turkish locale.
Expand All @@ -464,7 +464,7 @@ def _make_id(string: str) -> str:
return str(id)


_non_id_chars = re.compile('[^a-z0-9._]+')
_non_id_chars = re.compile('[^a-zA-Z0-9._]+')
_non_id_at_ends = re.compile('^[-0-9._]+|-+$')
_non_id_translate = {
0x00f8: u'o', # o with stroke
Expand Down
6 changes: 3 additions & 3 deletions tests/test_build_epub.py
Expand Up @@ -320,13 +320,13 @@ def test_epub_anchor_id(app):
app.build()

html = (app.outdir / 'index.xhtml').read_text()
assert ('<p id="std-setting-staticfiles_finders">'
assert ('<p id="std-setting-STATICFILES_FINDERS">'
'<span id="std-setting-STATICFILES_FINDERS"></span>'
'blah blah blah</p>' in html)
assert ('<span id="std-setting-staticfiles_section"></span>'
assert ('<span id="std-setting-STATICFILES_SECTION"></span>'
'<span id="std-setting-STATICFILES_SECTION"></span>'
'<h1>blah blah blah</h1>' in html)
assert 'see <a class="reference internal" href="#std-setting-staticfiles_finders">' in html
assert 'see <a class="reference internal" href="#std-setting-STATICFILES_FINDERS">' in html


@pytest.mark.sphinx('epub', testroot='html_assets')
Expand Down
18 changes: 9 additions & 9 deletions tests/test_build_html.py
Expand Up @@ -176,7 +176,7 @@ def test_html4_output(app, status, warning):
r'-| |-'),
],
'autodoc.html': [
(".//dl[@class='py class']/dt[@id='autodoc_target.class']", ''),
(".//dl[@class='py class']/dt[@id='autodoc_target.Class']", ''),
(".//dl[@class='py function']/dt[@id='autodoc_target.function']/em/span", r'\*\*'),
(".//dl[@class='py function']/dt[@id='autodoc_target.function']/em/span", r'kwds'),
(".//dd/p", r'Return spam\.'),
Expand Down Expand Up @@ -219,7 +219,7 @@ def test_html4_output(app, status, warning):
"[@class='rfc reference external']/strong", 'RFC 1'),
(".//a[@href='https://tools.ietf.org/html/rfc1.html']"
"[@class='rfc reference external']/strong", 'Request for Comments #1'),
(".//a[@href='objects.html#envvar-home']"
(".//a[@href='objects.html#envvar-HOME']"
"[@class='reference internal']/code/span[@class='pre']", 'HOME'),
(".//a[@href='#with']"
"[@class='reference internal']/code/span[@class='pre']", '^with$'),
Expand Down Expand Up @@ -275,18 +275,18 @@ def test_html4_output(app, status, warning):
(".//p", 'Il dit : « C’est “super” ! »'),
],
'objects.html': [
(".//dt[@id='mod.cls.meth1']", ''),
(".//dt[@id='errmod.error']", ''),
(".//dt[@id='mod.Cls.meth1']", ''),
(".//dt[@id='errmod.Error']", ''),
(".//dt/code", r'long\(parameter,\s* list\)'),
(".//dt/code", 'another one'),
(".//a[@href='#mod.cls'][@class='reference internal']", ''),
(".//a[@href='#mod.Cls'][@class='reference internal']", ''),
(".//dl[@class='std userdesc']", ''),
(".//dt[@id='userdesc-myobj']", ''),
(".//a[@href='#userdesc-myobj'][@class='reference internal']", ''),
# docfields
(".//a[@class='reference internal'][@href='#timeint']/em", 'TimeInt'),
(".//a[@class='reference internal'][@href='#time']", 'Time'),
(".//a[@class='reference internal'][@href='#errmod.error']/strong", 'Error'),
(".//a[@class='reference internal'][@href='#TimeInt']/em", 'TimeInt'),
(".//a[@class='reference internal'][@href='#Time']", 'Time'),
(".//a[@class='reference internal'][@href='#errmod.Error']/strong", 'Error'),
# C references
(".//span[@class='pre']", 'CFunction()'),
(".//a[@href='#c.Sphinx_DoSomething']", ''),
Expand Down Expand Up @@ -323,7 +323,7 @@ def test_html4_output(app, status, warning):
'perl'),
(".//a[@class='reference internal'][@href='#cmdoption-perl-arg-p']/code/span",
'\\+p'),
(".//a[@class='reference internal'][@href='#cmdoption-perl-objc']/code/span",
(".//a[@class='reference internal'][@href='#cmdoption-perl-ObjC']/code/span",
'--ObjC\\+\\+'),
(".//a[@class='reference internal'][@href='#cmdoption-perl-plugin.option']/code/span",
'--plugin.option'),
Expand Down
16 changes: 8 additions & 8 deletions tests/test_domain_js.py
Expand Up @@ -120,25 +120,25 @@ def find_obj(mod_name, prefix, obj_name, obj_type, searchmode=0):

assert (find_obj(None, None, 'NONEXISTANT', 'class') == (None, None))
assert (find_obj(None, None, 'NestedParentA', 'class') ==
('NestedParentA', ('roles', 'nestedparenta', 'class')))
('NestedParentA', ('roles', 'NestedParentA', 'class')))
assert (find_obj(None, None, 'NestedParentA.NestedChildA', 'class') ==
('NestedParentA.NestedChildA',
('roles', 'nestedparenta.nestedchilda', 'class')))
('roles', 'NestedParentA.NestedChildA', 'class')))
assert (find_obj(None, 'NestedParentA', 'NestedChildA', 'class') ==
('NestedParentA.NestedChildA',
('roles', 'nestedparenta.nestedchilda', 'class')))
('roles', 'NestedParentA.NestedChildA', 'class')))
assert (find_obj(None, None, 'NestedParentA.NestedChildA.subchild_1', 'func') ==
('NestedParentA.NestedChildA.subchild_1',
('roles', 'nestedparenta.nestedchilda.subchild_1', 'function')))
('roles', 'NestedParentA.NestedChildA.subchild_1', 'function')))
assert (find_obj(None, 'NestedParentA', 'NestedChildA.subchild_1', 'func') ==
('NestedParentA.NestedChildA.subchild_1',
('roles', 'nestedparenta.nestedchilda.subchild_1', 'function')))
('roles', 'NestedParentA.NestedChildA.subchild_1', 'function')))
assert (find_obj(None, 'NestedParentA.NestedChildA', 'subchild_1', 'func') ==
('NestedParentA.NestedChildA.subchild_1',
('roles', 'nestedparenta.nestedchilda.subchild_1', 'function')))
('roles', 'NestedParentA.NestedChildA.subchild_1', 'function')))
assert (find_obj('module_a.submodule', 'ModTopLevel', 'mod_child_2', 'meth') ==
('module_a.submodule.ModTopLevel.mod_child_2',
('module', 'module_a.submodule.modtoplevel.mod_child_2', 'method')))
('module', 'module_a.submodule.ModTopLevel.mod_child_2', 'method')))
assert (find_obj('module_b.submodule', 'ModTopLevel', 'module_a.submodule', 'mod') ==
('module_a.submodule',
('module', 'module-module_a.submodule', 'module')))
Expand Down Expand Up @@ -205,7 +205,7 @@ def test_js_class(app):
[desc_parameterlist, ()])],
[desc_content, ()])]))
assert_node(doctree[0], addnodes.index,
entries=[("single", "Application() (class)", "application", "", None)])
entries=[("single", "Application() (class)", "Application", "", None)])
assert_node(doctree[1], addnodes.desc, domain="js", objtype="class", noindex=False)


Expand Down
52 changes: 26 additions & 26 deletions tests/test_domain_py.py
Expand Up @@ -171,11 +171,11 @@ def test_resolve_xref_for_properties(app, status, warning):
app.builder.build_all()

content = (app.outdir / 'module.html').read_text()
assert ('Link to <a class="reference internal" href="#module_a.submodule.modtoplevel.prop"'
assert ('Link to <a class="reference internal" href="#module_a.submodule.ModTopLevel.prop"'
' title="module_a.submodule.ModTopLevel.prop">'
'<code class="xref py py-attr docutils literal notranslate"><span class="pre">'
'prop</span> <span class="pre">attribute</span></code></a>' in content)
assert ('Link to <a class="reference internal" href="#module_a.submodule.modtoplevel.prop"'
assert ('Link to <a class="reference internal" href="#module_a.submodule.ModTopLevel.prop"'
' title="module_a.submodule.ModTopLevel.prop">'
'<code class="xref py py-meth docutils literal notranslate"><span class="pre">'
'prop</span> <span class="pre">method</span></code></a>' in content)
Expand All @@ -192,20 +192,20 @@ def find_obj(modname, prefix, obj_name, obj_type, searchmode=0):

assert (find_obj(None, None, 'NONEXISTANT', 'class') == [])
assert (find_obj(None, None, 'NestedParentA', 'class') ==
[('NestedParentA', ('roles', 'nestedparenta', 'class'))])
[('NestedParentA', ('roles', 'NestedParentA', 'class'))])
assert (find_obj(None, None, 'NestedParentA.NestedChildA', 'class') ==
[('NestedParentA.NestedChildA', ('roles', 'nestedparenta.nestedchilda', 'class'))])
[('NestedParentA.NestedChildA', ('roles', 'NestedParentA.NestedChildA', 'class'))])
assert (find_obj(None, 'NestedParentA', 'NestedChildA', 'class') ==
[('NestedParentA.NestedChildA', ('roles', 'nestedparenta.nestedchilda', 'class'))])
[('NestedParentA.NestedChildA', ('roles', 'NestedParentA.NestedChildA', 'class'))])
assert (find_obj(None, None, 'NestedParentA.NestedChildA.subchild_1', 'meth') ==
[('NestedParentA.NestedChildA.subchild_1',
('roles', 'nestedparenta.nestedchilda.subchild_1', 'method'))])
('roles', 'NestedParentA.NestedChildA.subchild_1', 'method'))])
assert (find_obj(None, 'NestedParentA', 'NestedChildA.subchild_1', 'meth') ==
[('NestedParentA.NestedChildA.subchild_1',
('roles', 'nestedparenta.nestedchilda.subchild_1', 'method'))])
('roles', 'NestedParentA.NestedChildA.subchild_1', 'method'))])
assert (find_obj(None, 'NestedParentA.NestedChildA', 'subchild_1', 'meth') ==
[('NestedParentA.NestedChildA.subchild_1',
('roles', 'nestedparenta.nestedchilda.subchild_1', 'method'))])
('roles', 'NestedParentA.NestedChildA.subchild_1', 'method'))])


def test_get_full_qualified_name():
Expand Down Expand Up @@ -525,61 +525,61 @@ def test_pymethod_options(app):

# method
assert_node(doctree[1][1][0], addnodes.index,
entries=[('single', 'meth1() (Class method)', 'class.meth1', '', None)])
entries=[('single', 'meth1() (Class method)', 'Class.meth1', '', None)])
assert_node(doctree[1][1][1], ([desc_signature, ([desc_name, "meth1"],
[desc_parameterlist, ()])],
[desc_content, ()]))
assert 'Class.meth1' in domain.objects
assert domain.objects['Class.meth1'] == ('index', 'class.meth1', 'method')
assert domain.objects['Class.meth1'] == ('index', 'Class.meth1', 'method')

# :classmethod:
assert_node(doctree[1][1][2], addnodes.index,
entries=[('single', 'meth2() (Class class method)', 'class.meth2', '', None)])
entries=[('single', 'meth2() (Class class method)', 'Class.meth2', '', None)])
assert_node(doctree[1][1][3], ([desc_signature, ([desc_annotation, "classmethod "],
[desc_name, "meth2"],
[desc_parameterlist, ()])],
[desc_content, ()]))
assert 'Class.meth2' in domain.objects
assert domain.objects['Class.meth2'] == ('index', 'class.meth2', 'method')
assert domain.objects['Class.meth2'] == ('index', 'Class.meth2', 'method')

# :staticmethod:
assert_node(doctree[1][1][4], addnodes.index,
entries=[('single', 'meth3() (Class static method)', 'class.meth3', '', None)])
entries=[('single', 'meth3() (Class static method)', 'Class.meth3', '', None)])
assert_node(doctree[1][1][5], ([desc_signature, ([desc_annotation, "static "],
[desc_name, "meth3"],
[desc_parameterlist, ()])],
[desc_content, ()]))
assert 'Class.meth3' in domain.objects
assert domain.objects['Class.meth3'] == ('index', 'class.meth3', 'method')
assert domain.objects['Class.meth3'] == ('index', 'Class.meth3', 'method')

# :async:
assert_node(doctree[1][1][6], addnodes.index,
entries=[('single', 'meth4() (Class method)', 'class.meth4', '', None)])
entries=[('single', 'meth4() (Class method)', 'Class.meth4', '', None)])
assert_node(doctree[1][1][7], ([desc_signature, ([desc_annotation, "async "],
[desc_name, "meth4"],
[desc_parameterlist, ()])],
[desc_content, ()]))
assert 'Class.meth4' in domain.objects
assert domain.objects['Class.meth4'] == ('index', 'class.meth4', 'method')
assert domain.objects['Class.meth4'] == ('index', 'Class.meth4', 'method')

# :property:
assert_node(doctree[1][1][8], addnodes.index,
entries=[('single', 'meth5() (Class property)', 'class.meth5', '', None)])
entries=[('single', 'meth5() (Class property)', 'Class.meth5', '', None)])
assert_node(doctree[1][1][9], ([desc_signature, ([desc_annotation, "property "],
[desc_name, "meth5"])],
[desc_content, ()]))
assert 'Class.meth5' in domain.objects
assert domain.objects['Class.meth5'] == ('index', 'class.meth5', 'method')
assert domain.objects['Class.meth5'] == ('index', 'Class.meth5', 'method')

# :abstractmethod:
assert_node(doctree[1][1][10], addnodes.index,
entries=[('single', 'meth6() (Class method)', 'class.meth6', '', None)])
entries=[('single', 'meth6() (Class method)', 'Class.meth6', '', None)])
assert_node(doctree[1][1][11], ([desc_signature, ([desc_annotation, "abstract "],
[desc_name, "meth6"],
[desc_parameterlist, ()])],
[desc_content, ()]))
assert 'Class.meth6' in domain.objects
assert domain.objects['Class.meth6'] == ('index', 'class.meth6', 'method')
assert domain.objects['Class.meth6'] == ('index', 'Class.meth6', 'method')


def test_pyclassmethod(app):
Expand All @@ -594,13 +594,13 @@ def test_pyclassmethod(app):
[desc_content, (addnodes.index,
desc)])]))
assert_node(doctree[1][1][0], addnodes.index,
entries=[('single', 'meth() (Class class method)', 'class.meth', '', None)])
entries=[('single', 'meth() (Class class method)', 'Class.meth', '', None)])
assert_node(doctree[1][1][1], ([desc_signature, ([desc_annotation, "classmethod "],
[desc_name, "meth"],
[desc_parameterlist, ()])],
[desc_content, ()]))
assert 'Class.meth' in domain.objects
assert domain.objects['Class.meth'] == ('index', 'class.meth', 'method')
assert domain.objects['Class.meth'] == ('index', 'Class.meth', 'method')


def test_pystaticmethod(app):
Expand All @@ -615,13 +615,13 @@ def test_pystaticmethod(app):
[desc_content, (addnodes.index,
desc)])]))
assert_node(doctree[1][1][0], addnodes.index,
entries=[('single', 'meth() (Class static method)', 'class.meth', '', None)])
entries=[('single', 'meth() (Class static method)', 'Class.meth', '', None)])
assert_node(doctree[1][1][1], ([desc_signature, ([desc_annotation, "static "],
[desc_name, "meth"],
[desc_parameterlist, ()])],
[desc_content, ()]))
assert 'Class.meth' in domain.objects
assert domain.objects['Class.meth'] == ('index', 'class.meth', 'method')
assert domain.objects['Class.meth'] == ('index', 'Class.meth', 'method')


def test_pyattribute(app):
Expand All @@ -638,13 +638,13 @@ def test_pyattribute(app):
[desc_content, (addnodes.index,
desc)])]))
assert_node(doctree[1][1][0], addnodes.index,
entries=[('single', 'attr (Class attribute)', 'class.attr', '', None)])
entries=[('single', 'attr (Class attribute)', 'Class.attr', '', None)])
assert_node(doctree[1][1][1], ([desc_signature, ([desc_name, "attr"],
[desc_annotation, ": str"],
[desc_annotation, " = ''"])],
[desc_content, ()]))
assert 'Class.attr' in domain.objects
assert domain.objects['Class.attr'] == ('index', 'class.attr', 'attribute')
assert domain.objects['Class.attr'] == ('index', 'Class.attr', 'attribute')


def test_pydecorator_signature(app):
Expand Down

0 comments on commit 7aa5584

Please sign in to comment.