Skip to content

Commit e62cb0a

Browse files
authored
Bugfix release 5.1.2 (#498)
* Avoid multiline annotations in Traits documenter. * Regression test. * Use a temporary source directory. * Flake8: remove unused variable * Fix import order, docstrings * Another import order fix. * Update changelog, bump version. * Fix release date. * Improve description.
1 parent e2fe1b9 commit e62cb0a

File tree

4 files changed

+125
-19
lines changed

4 files changed

+125
-19
lines changed

CHANGES.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
Traits CHANGELOG
22
================
33

4+
Release 5.1.2
5+
-------------
6+
7+
Released: 2019-07-08
8+
9+
Fixes
10+
11+
* Traits documenter no longer generates bad reST for traits whose definition
12+
spans multiple source lines. (#494)
13+
14+
415
Release 5.1.1
516
-------------
617

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
MAJOR = 5
1111
MINOR = 1
12-
MICRO = 1
12+
MICRO = 2
1313

1414
IS_RELEASED = True
1515

traits/util/tests/test_trait_documenter.py

Lines changed: 107 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,66 @@
11
# -*- coding: utf-8 -*-
22
""" Tests for the trait documenter. """
33

4-
4+
import contextlib
5+
import io
6+
import os
7+
import shutil
8+
import tempfile
59
import textwrap
610
import tokenize
711
import unittest
12+
try:
13+
# Python 3: mock in the standard library.
14+
import unittest.mock as mock
15+
except ImportError:
16+
# Python 2: need to use 3rd-party mock.
17+
import mock
818

919
import six
10-
if six.PY2:
11-
import mock
12-
else:
13-
import unittest.mock as mock
1420

21+
try:
22+
import sphinx # noqa: F401
23+
except ImportError:
24+
sphinx_available = False
25+
else:
26+
sphinx_available = True
1527

16-
def _sphinx_present():
17-
try:
18-
import sphinx # noqa
19-
except ImportError:
20-
return False
28+
from traits.api import HasTraits, Int
2129

22-
return True
30+
if sphinx_available:
31+
from sphinx.ext.autodoc import Options
32+
from sphinx.ext.autodoc.directive import DocumenterBridge
33+
from sphinx.testing.path import path
34+
from sphinx.testing.util import SphinxTestApp
35+
from sphinx.util.docutils import LoggingReporter
2336

37+
from traits.util.trait_documenter import (
38+
_get_definition_tokens,
39+
TraitDocumenter,
40+
)
2441

25-
@unittest.skipIf(
26-
not _sphinx_present(), "Sphinx not available. Cannot test documenter"
42+
skip_unless_sphinx_present = unittest.skipUnless(
43+
sphinx_available,
44+
"Sphinx is not available. Cannot test documenter.",
2745
)
46+
47+
48+
class MyTestClass(HasTraits):
49+
"""
50+
Class-level docstring.
51+
"""
52+
#: I'm a troublesome trait with a long definition.
53+
bar = Int(42, desc=""" First line
54+
55+
The answer to
56+
Life,
57+
the Universe,
58+
59+
and Everything.
60+
""")
61+
62+
63+
@skip_unless_sphinx_present
2864
class TestTraitDocumenter(unittest.TestCase):
2965
""" Tests for the trait documenter. """
3066

@@ -38,9 +74,6 @@ def setUp(self):
3874
self.tokens = tokens
3975

4076
def test_get_definition_tokens(self):
41-
42-
from traits.util.trait_documenter import _get_definition_tokens
43-
4477
src = textwrap.dedent(
4578
"""\
4679
depth_interval = Property(Tuple(Float, Float),
@@ -59,8 +92,6 @@ def test_get_definition_tokens(self):
5992

6093
def test_add_line(self):
6194

62-
from traits.util.trait_documenter import TraitDocumenter
63-
6495
src = textwrap.dedent(
6596
"""\
6697
class Fake(HasTraits):
@@ -87,3 +118,61 @@ class Fake(HasTraits):
87118

88119
self.assertEqual(
89120
len(documenter.directive.result.append.mock_calls), 1)
121+
122+
def test_abbreviated_annotations(self):
123+
# Regression test for enthought/traits#493.
124+
with self.create_test_directive() as directive:
125+
documenter = TraitDocumenter(
126+
directive, __name__ + ".MyTestClass.bar")
127+
documenter.generate(all_members=True)
128+
result = directive.result
129+
130+
# Find annotations line.
131+
for item in result:
132+
if item.lstrip().startswith(":annotation:"):
133+
break
134+
else:
135+
self.fail("Didn't find the expected trait :annotation:")
136+
137+
# Annotation should be a single line.
138+
self.assertIn("First line", item)
139+
self.assertNotIn("\n", item)
140+
141+
@contextlib.contextmanager
142+
def create_test_directive(self):
143+
"""
144+
Helper function to create a a "directive" suitable
145+
for instantiating the TraitDocumenter with, along with resources
146+
to support that directive, and clean up the resources afterwards.
147+
148+
Returns
149+
-------
150+
contextmanager
151+
A context manager that returns a DocumenterBridge instance.
152+
"""
153+
with self.tmpdir() as tmpdir:
154+
# The configuration file must exist, but it's okay if it's empty.
155+
conf_file = os.path.join(tmpdir, "conf.py")
156+
with io.open(conf_file, "w", encoding="utf-8"):
157+
pass
158+
159+
app = SphinxTestApp(srcdir=path(tmpdir))
160+
app.builder.env.app = app
161+
app.builder.env.temp_data["docname"] = "dummy"
162+
yield DocumenterBridge(app.env, LoggingReporter(''), Options(), 1)
163+
164+
@contextlib.contextmanager
165+
def tmpdir(self):
166+
"""
167+
Helper function to create a temporary directory.
168+
169+
Returns
170+
-------
171+
contextmanager
172+
Context manager that returns the path to a temporary directory.
173+
"""
174+
tmpdir = tempfile.mkdtemp()
175+
try:
176+
yield tmpdir
177+
finally:
178+
shutil.rmtree(tmpdir)

traits/util/trait_documenter.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,12 @@ def add_directive_header(self, sig):
112112
"""
113113
ClassLevelDocumenter.add_directive_header(self, sig)
114114
definition = self._get_trait_definition()
115+
116+
# Workaround for enthought/traits#493: if the definition is multiline,
117+
# throw away all lines after the first.
118+
if "\n" in definition:
119+
definition = definition.partition("\n")[0] + u" …"
120+
115121
self.add_line(u" :annotation: = {0}".format(definition), "<autodoc>")
116122

117123
# Private Interface #####################################################

0 commit comments

Comments
 (0)