diff --git a/CHANGES b/CHANGES index 2ab614e57..7498d6525 100644 --- a/CHANGES +++ b/CHANGES @@ -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 -------- diff --git a/sphinx/util/nodes.py b/sphinx/util/nodes.py index b39a7ca01..bca6e0bfc 100644 --- a/sphinx/util/nodes.py +++ b/sphinx/util/nodes.py @@ -445,6 +445,7 @@ 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. @@ -452,8 +453,7 @@ def _make_id(string: str) -> str: # 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. @@ -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 diff --git a/tests/test_build_epub.py b/tests/test_build_epub.py index ca6f09cdd..f893e1351 100644 --- a/tests/test_build_epub.py +++ b/tests/test_build_epub.py @@ -320,13 +320,13 @@ def test_epub_anchor_id(app): app.build() html = (app.outdir / 'index.xhtml').read_text() - assert ('
' + assert ('
' '' 'blah blah blah
' in html) - assert ('' + assert ('' '' ''
'prop attribute
' in content)
- assert ('Link to '
''
'prop method
' in content)
@@ -192,20 +192,20 @@ def test_domain_py_find_obj(app, status, warning):
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():
@@ -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):
@@ -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):
@@ -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):
@@ -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):
diff --git a/tests/test_domain_std.py b/tests/test_domain_std.py
index 6f213e565..aa1d29eb1 100644
--- a/tests/test_domain_std.py
+++ b/tests/test_domain_std.py
@@ -353,23 +353,23 @@ def test_productionlist(app, status, warning):
linkText = span.text.strip()
cases.append((text, link, linkText))
assert cases == [
- ('A', 'Bare.html#grammar-token-a', 'A'),
- ('B', 'Bare.html#grammar-token-b', 'B'),
- ('P1:A', 'P1.html#grammar-token-p1-a', 'P1:A'),
- ('P1:B', 'P1.html#grammar-token-p1-b', 'P1:B'),
- ('P2:A', 'P1.html#grammar-token-p1-a', 'P1:A'),
- ('P2:B', 'P2.html#grammar-token-p2-b', 'P2:B'),
- ('Explicit title A, plain', 'Bare.html#grammar-token-a', 'MyTitle'),
- ('Explicit title A, colon', 'Bare.html#grammar-token-a', 'My:Title'),
- ('Explicit title P1:A, plain', 'P1.html#grammar-token-p1-a', 'MyTitle'),
- ('Explicit title P1:A, colon', 'P1.html#grammar-token-p1-a', 'My:Title'),
- ('Tilde A', 'Bare.html#grammar-token-a', 'A'),
- ('Tilde P1:A', 'P1.html#grammar-token-p1-a', 'A'),
- ('Tilde explicit title P1:A', 'P1.html#grammar-token-p1-a', '~MyTitle'),
- ('Tilde, explicit title P1:A', 'P1.html#grammar-token-p1-a', 'MyTitle'),
- ('Dup', 'Dup2.html#grammar-token-dup', 'Dup'),
- ('FirstLine', 'firstLineRule.html#grammar-token-firstline', 'FirstLine'),
- ('SecondLine', 'firstLineRule.html#grammar-token-secondline', 'SecondLine'),
+ ('A', 'Bare.html#grammar-token-A', 'A'),
+ ('B', 'Bare.html#grammar-token-B', 'B'),
+ ('P1:A', 'P1.html#grammar-token-P1-A', 'P1:A'),
+ ('P1:B', 'P1.html#grammar-token-P1-B', 'P1:B'),
+ ('P2:A', 'P1.html#grammar-token-P1-A', 'P1:A'),
+ ('P2:B', 'P2.html#grammar-token-P2-B', 'P2:B'),
+ ('Explicit title A, plain', 'Bare.html#grammar-token-A', 'MyTitle'),
+ ('Explicit title A, colon', 'Bare.html#grammar-token-A', 'My:Title'),
+ ('Explicit title P1:A, plain', 'P1.html#grammar-token-P1-A', 'MyTitle'),
+ ('Explicit title P1:A, colon', 'P1.html#grammar-token-P1-A', 'My:Title'),
+ ('Tilde A', 'Bare.html#grammar-token-A', 'A'),
+ ('Tilde P1:A', 'P1.html#grammar-token-P1-A', 'A'),
+ ('Tilde explicit title P1:A', 'P1.html#grammar-token-P1-A', '~MyTitle'),
+ ('Tilde, explicit title P1:A', 'P1.html#grammar-token-P1-A', 'MyTitle'),
+ ('Dup', 'Dup2.html#grammar-token-Dup', 'Dup'),
+ ('FirstLine', 'firstLineRule.html#grammar-token-FirstLine', 'FirstLine'),
+ ('SecondLine', 'firstLineRule.html#grammar-token-SecondLine', 'SecondLine'),
]
text = (app.outdir / 'LineContinuation.html').read_text()
diff --git a/tests/test_environment_indexentries.py b/tests/test_environment_indexentries.py
index 97882d44b..7f6003d54 100644
--- a/tests/test_environment_indexentries.py
+++ b/tests/test_environment_indexentries.py
@@ -161,5 +161,5 @@ def test_create_index_by_key(app):
index = IndexEntries(app.env).create_index(app.builder)
assert len(index) == 3
assert index[0] == ('D', [('docutils', [[('main', '#term-docutils')], [], None])])
- assert index[1] == ('P', [('Python', [[('main', '#term-python')], [], None])])
+ assert index[1] == ('P', [('Python', [[('main', '#term-Python')], [], None])])
assert index[2] == ('ス', [('スフィンクス', [[('main', '#term-0')], [], 'ス'])])
diff --git a/tests/test_intl.py b/tests/test_intl.py
index 0e7dd4f62..d0c64b589 100644
--- a/tests/test_intl.py
+++ b/tests/test_intl.py
@@ -946,14 +946,14 @@ def test_xml_role_xref(app):
['LINK TO', "I18N ROCK'N ROLE XREF", ',', 'CONTENTS', ',',
'SOME NEW TERM', '.'],
['i18n-role-xref', 'index',
- 'glossary_terms#term-some-term'])
+ 'glossary_terms#term-Some-term'])
para2 = sec2.findall('paragraph')
assert_elem(
para2[0],
['LINK TO', 'SOME OTHER NEW TERM', 'AND', 'SOME NEW TERM', '.'],
- ['glossary_terms#term-some-other-term',
- 'glossary_terms#term-some-term'])
+ ['glossary_terms#term-Some-other-term',
+ 'glossary_terms#term-Some-term'])
assert_elem(
para2[1],
['LINK TO', 'SAME TYPE LINKS', 'AND',
diff --git a/tests/test_util_nodes.py b/tests/test_util_nodes.py
index 01c5d2e3f..8fe9ee773 100644
--- a/tests/test_util_nodes.py
+++ b/tests/test_util_nodes.py
@@ -188,13 +188,13 @@ def test_clean_astext():
[
('', '', 'id0'),
('term', '', 'term-0'),
- ('term', 'Sphinx', 'term-sphinx'),
- ('', 'io.StringIO', 'io.stringio'), # contains a dot
+ ('term', 'Sphinx', 'term-Sphinx'),
+ ('', 'io.StringIO', 'io.StringIO'), # contains a dot
('', 'sphinx.setup_command', 'sphinx.setup_command'), # contains a dot & underscore
- ('', '_io.StringIO', 'io.stringio'), # starts with underscore
+ ('', '_io.StringIO', 'io.StringIO'), # starts with underscore
('', 'sphinx', 'sphinx'), # alphabets in unicode fullwidth characters
('', '悠好', 'id0'), # multibytes text (in Chinese)
- ('', 'Hello=悠好=こんにちは', 'hello'), # alphabets and multibytes text
+ ('', 'Hello=悠好=こんにちは', 'Hello'), # alphabets and multibytes text
('', 'fünf', 'funf'), # latin1 (umlaut)
('', '0sphinx', 'sphinx'), # starts with number
('', 'sphinx-', 'sphinx'), # ends with hyphen
@@ -206,7 +206,7 @@ def test_make_id(app, prefix, term, expected):
def test_make_id_already_registered(app):
document = create_new_document()
- document.ids['term-sphinx'] = True # register "term-sphinx" manually
+ document.ids['term-Sphinx'] = True # register "term-Sphinx" manually
assert make_id(app.env, document, 'term', 'Sphinx') == 'term-0'