Skip to content

Commit 82e8027

Browse files
committed
Enhanced API docs.
Main motivation was enhancing documentation of running and result related model objects used by pre-run/rebot modifiers and listeners. Also related listener interface docs were enhanced a little. These topics are covered by robotframework#2279. Various enhancements were done for other parts of the API docs too. The only code change was adding setter for the `passed` property of the `result.Keyword` objects.
1 parent 0fa8d6d commit 82e8027

File tree

15 files changed

+377
-162
lines changed

15 files changed

+377
-162
lines changed

src/robot/model/__init__.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,14 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
"""Package with reusable and extendable model classes.
15+
"""Package with generic, reusable and extensible model classes.
1616
17-
This package contains base classes, for example, for
18-
:class:`test suites <robot.model.testsuite.TestSuite>`,
19-
:class:`test cases <robot.model.testcase.TestCase>` and
20-
:class:`keywords <robot.model.keyword.Keyword>`, and for other generic
21-
functionality, such as :mod:`visitors <robot.model.visitor>`.
22-
23-
These classes are extended both in :mod:`robot.result` and :mod:`robot.running`
24-
packages and used also elsewhere.
17+
This package contains, for example, :class:`~robot.model.testsuite.TestSuite`,
18+
:class:`~robot.model.testcase.TestCase`, :class:`~robot.model.keyword.Keyword`
19+
and :class:`~robot.model.visitor.SuiteVisitor` base classes.
20+
These classes are extended both by :mod:`execution <robot.running.model>`
21+
and :mod:`result <robot.result.model>` related model objects and used also
22+
elsewhere.
2523
2624
This package is considered stable.
2725
"""

src/robot/model/keyword.py

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -24,39 +24,35 @@
2424

2525

2626
class Keyword(ModelObject):
27-
"""Base model for single keyword."""
27+
"""Base model for a single keyword.
28+
29+
Extended by :class:`robot.running.model.Keyword` and
30+
:class:`robot.result.model.Keyword`.
31+
"""
2832
__slots__ = ['_name', 'doc', 'args', 'assign', 'timeout', 'type',
2933
'_sort_key', '_next_child_sort_key']
30-
KEYWORD_TYPE = 'kw'
31-
SETUP_TYPE = 'setup'
32-
TEARDOWN_TYPE = 'teardown'
33-
FOR_LOOP_TYPE = 'for'
34-
FOR_ITEM_TYPE = 'foritem'
35-
keyword_class = None
36-
message_class = Message
34+
KEYWORD_TYPE = 'kw' #: Normal keyword :attr:`type`.
35+
SETUP_TYPE = 'setup' #: Setup :attr:`type`.
36+
TEARDOWN_TYPE = 'teardown' #: Teardown :attr:`type`.
37+
FOR_LOOP_TYPE = 'for' #: For loop :attr:`type`.
38+
FOR_ITEM_TYPE = 'foritem' #: Single for loop iteration :attr:`type`.
39+
keyword_class = None #: Internal usage only.
40+
message_class = Message #: Internal usage only.
3741

3842
def __init__(self, name='', doc='', args=(), assign=(), tags=(),
39-
timeout=None, type='kw'):
40-
#: :class:`~.model.testsuite.TestSuite` or
41-
#: :class:`~.model.testcase.TestCase` or
42-
#: :class:`~.model.keyword.Keyword` that contains this keyword.
43+
timeout=None, type=KEYWORD_TYPE):
4344
self.parent = None
4445
self._name = name
45-
#: Keyword documentation.
4646
self.doc = doc
47-
#: Keyword arguments as a list of strings.
48-
self.args = args
49-
#: Assigned variables as a list of strings.
50-
self.assign = assign
51-
#: Keyword tags as a list like :class:`~.model.tags.Tags` object.
47+
self.args = args #: Keyword arguments as a list of strings.
48+
self.assign = assign #: Assigned variables as a list of strings.
5249
self.tags = tags
53-
#: Keyword timeout.
5450
self.timeout = timeout
55-
#: Keyword type as a string. See class level ``XXX_TYPE`` constants.
51+
#: Keyword type as a string. The value is either :attr:`KEYWORD_TYPE`,
52+
#: :attr:`SETUP_TYPE`, :attr:`TEARDOWN_TYPE`, :attr:`FOR_LOOP_TYPE` or
53+
#: :attr:`FOR_ITEM_TYPE` constant defined on the class level.
5654
self.type = type
57-
#: Keyword messages as :class:`~.model.message.Message` instances.
5855
self.messages = None
59-
#: Child keywords as :class:`~.model.keyword.Keyword` instances.
6056
self.keywords = None
6157
self._sort_key = -1
6258
self._next_child_sort_key = 0
@@ -71,6 +67,7 @@ def name(self, name):
7167

7268
@setter
7369
def parent(self, parent):
70+
"""Parent test suite, test case or keyword."""
7471
if parent and parent is not self.parent:
7572
self._sort_key = getattr(parent, '_child_sort_key', -1)
7673
return parent
@@ -82,19 +79,22 @@ def _child_sort_key(self):
8279

8380
@setter
8481
def tags(self, tags):
82+
"""Keyword tags as a :class:`~.model.tags.Tags` object."""
8583
return Tags(tags)
8684

8785
@setter
8886
def keywords(self, keywords):
87+
"""Child keywords as a :class:`~.Keywords` object."""
8988
return Keywords(self.keyword_class or self.__class__, self, keywords)
9089

9190
@setter
9291
def messages(self, messages):
92+
"""Messages as a :class:`~.model.message.Messages` object."""
9393
return Messages(self.message_class, self, messages)
9494

9595
@property
9696
def children(self):
97-
"""Child keywords and messages in creation order."""
97+
"""Child :attr:`keywords` and :attr:`messages` in creation order."""
9898
# It would be cleaner to store keywords/messages in same `children`
9999
# list and turn `keywords` and `messages` to properties that pick items
100100
# from it. That would require bigger changes to the model, though.
@@ -103,34 +103,49 @@ def children(self):
103103

104104
@property
105105
def id(self):
106+
"""Keyword id in format like ``s1-t3-k1``.
107+
108+
See :attr:`TestSuite.id <robot.model.testsuite.TestSuite.id>` for
109+
more information.
110+
"""
106111
if not self.parent:
107112
return 'k1'
108113
return '%s-k%d' % (self.parent.id, self.parent.keywords.index(self)+1)
109114

110115
def visit(self, visitor):
116+
""":mod:`Visitor interface <robot.model.visitor>` entry-point."""
111117
visitor.visit_keyword(self)
112118

113119

114120
class Keywords(ItemList):
121+
"""A list-like object representing keywords in a suite, a test or a keyword.
122+
123+
Possible setup and teardown keywords are directly available as
124+
:attr:`setup` and :attr:`teardown` attributes.
125+
"""
115126
__slots__ = []
116127

117128
def __init__(self, keyword_class=Keyword, parent=None, keywords=None):
118129
ItemList.__init__(self, keyword_class, {'parent': parent}, keywords)
119130

120131
@property
121132
def setup(self):
133+
"""Keyword used as the setup or ``None`` if no setup."""
122134
return self[0] if (self and self[0].type == 'setup') else None
123135

124136
@property
125137
def teardown(self):
138+
"""Keyword used as the teardown or ``None`` if no teardown."""
126139
return self[-1] if (self and self[-1].type == 'teardown') else None
127140

128141
@property
129142
def all(self):
143+
"""Iterates over all keywords, including setup and teardown."""
130144
return self
131145

132146
@property
133147
def normal(self):
148+
"""Iterates over normal keywords, omitting setup and teardown."""
134149
kws = [kw for kw in self if kw.type not in ('setup', 'teardown')]
135150
return Keywords(self._item_class, self._common_attrs['parent'], kws)
136151

src/robot/model/message.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,20 @@
2020

2121
@py2to3
2222
class Message(ModelObject):
23-
"""A message outputted during the test execution.
23+
"""A message created during the test execution.
2424
25-
The message can be a log message triggered by a keyword, or a warning
26-
or an error occurred during the test execution.
25+
Can be a log message triggered by a keyword, or a warning or an error
26+
that occurred during parsing or test execution.
2727
"""
2828
__slots__ = ['message', 'level', 'html', 'timestamp', '_sort_key']
2929

3030
def __init__(self, message='', level='INFO', html=False, timestamp=None,
3131
parent=None):
3232
#: The message content as a string.
3333
self.message = message
34-
#: Severity of the message. Either ``TRACE``, ``INFO``,
35-
#: ``WARN``, ``DEBUG`` or ``FAIL``/``ERROR``.
34+
#: Severity of the message. Either ``TRACE``, ``DEBUG``, ``INFO``,
35+
#: ``WARN``, ``ERROR``, or ``FAIL``. The latest one is only used with
36+
#: keyword failure messages.
3637
self.level = level
3738
#: ``True`` if the content is in HTML, ``False`` otherwise.
3839
self.html = html
@@ -54,6 +55,7 @@ def html_message(self):
5455
return self.message if self.html else html_escape(self.message)
5556

5657
def visit(self, visitor):
58+
""":mod:`Visitor interface <robot.model.visitor>` entry-point."""
5759
visitor.visit_message(self)
5860

5961
def __unicode__(self):

src/robot/model/testcase.py

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,46 +21,55 @@
2121

2222

2323
class TestCase(ModelObject):
24-
"""Base model for single test case."""
24+
"""Base model for a single test case.
25+
26+
Extended by :class:`robot.running.model.TestCase` and
27+
:class:`robot.result.model.TestCase`.
28+
"""
2529
__slots__ = ['parent', 'name', 'doc', 'timeout']
26-
keyword_class = Keyword
30+
keyword_class = Keyword #: Internal usage only
2731

2832
def __init__(self, name='', doc='', tags=None, timeout=None):
29-
#: :class:`~.model.testsuite.TestSuite` that contains this test.
30-
self.parent = None
31-
#: Test case name.
32-
self.name = name
33-
#: Test case documentation.
34-
self.doc = doc
35-
#: Test case tags as a list like :class:`~.model.tags.Tags` object.
33+
self.parent = None #: Parent suite.
34+
self.name = name #: Test case name.
35+
self.doc = doc #: Test case documentation.
36+
self.timeout = timeout #: Test case timeout.
3637
self.tags = tags
37-
#: Test case timeout.
38-
self.timeout = timeout
39-
#: Keyword results, a list of :class:`~.model.keyword.Keyword`
40-
#: instances and contains also possible setup and teardown keywords.
4138
self.keywords = None
4239

4340
@setter
4441
def tags(self, tags):
42+
"""Test tags as a :class:`~.model.tags.Tags` object."""
4543
return Tags(tags)
4644

4745
@setter
4846
def keywords(self, keywords):
47+
"""Keywords as a :class:`~.Keywords` object.
48+
49+
Contains also possible setup and teardown keywords.
50+
"""
4951
return Keywords(self.keyword_class, self, keywords)
5052

5153
@property
5254
def id(self):
55+
"""Test case id in format like ``s1-t3``.
56+
57+
See :attr:`TestSuite.id <robot.model.testsuite.TestSuite.id>` for
58+
more information.
59+
"""
5360
if not self.parent:
5461
return 't1'
5562
return '%s-t%d' % (self.parent.id, self.parent.tests.index(self)+1)
5663

5764
@property
5865
def longname(self):
66+
"""Test name prefixed with the long name of the parent suite."""
5967
if not self.parent:
6068
return self.name
6169
return '%s.%s' % (self.parent.longname, self.name)
6270

6371
def visit(self, visitor):
72+
""":mod:`Visitor interface <robot.model.visitor>` entry-point."""
6473
visitor.visit_test(self)
6574

6675

src/robot/model/testsuite.py

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,26 +26,22 @@
2626

2727
class TestSuite(ModelObject):
2828
"""Base model for single suite.
29+
30+
Extended by :class:`robot.running.model.TestSuite` and
31+
:class:`robot.result.model.TestSuite`.
2932
"""
3033
__slots__ = ['parent', 'source', '_name', 'doc', '_my_visitors']
31-
test_class = TestCase
32-
keyword_class = Keyword
34+
test_class = TestCase #: Internal usage only.
35+
keyword_class = Keyword #: Internal usage only.
3336

3437
def __init__(self, name='', doc='', metadata=None, source=None):
35-
#: Parent :class:`TestSuite` or `None`.
36-
self.parent = None
38+
self.parent = None #: Parent suite. ``None`` with the root suite.
3739
self._name = name
38-
self.doc = doc
39-
#: Test suite metadata as a dictionary.
40+
self.doc = doc #: Test suite documentation.
4041
self.metadata = metadata
41-
#: Path to the source file or directory.
42-
self.source = source
43-
#: A list of child :class:`~.model.testsuite.TestSuite` instances.
42+
self.source = source #: Path to the source file or directory.
4443
self.suites = None
45-
#: A list of :class:`~.model.testcase.TestCase` instances.
4644
self.tests = None
47-
#: A list containing setup and teardown as
48-
#: :class:`~.model.keyword.Keyword` instances.
4945
self.keywords = None
5046
self._my_visitors = []
5147

@@ -56,51 +52,56 @@ def _visitors(self):
5652

5753
@property
5854
def name(self):
55+
"""Test suite name. If not set, constructed from child suite names."""
5956
return self._name or ' & '.join(s.name for s in self.suites)
6057

6158
@name.setter
6259
def name(self, name):
6360
self._name = name
6461

62+
@property
63+
def longname(self):
64+
"""Suite name prefixed with the long name of the parent suite."""
65+
if not self.parent:
66+
return self.name
67+
return '%s.%s' % (self.parent.longname, self.name)
68+
6569
@setter
6670
def metadata(self, metadata):
6771
"""Free test suite metadata as a dictionary."""
6872
return Metadata(metadata)
6973

7074
@setter
7175
def suites(self, suites):
72-
"""A list-like :class:`~.TestSuites` object containing child suites."""
76+
"""Child suites as a :class:`~.TestSuites` object."""
7377
return TestSuites(self.__class__, self, suites)
7478

7579
@setter
7680
def tests(self, tests):
77-
"""A list-like :class:`~.TestCases` object containing tests."""
81+
"""Tests as a :class:`~.TestCases` object."""
7882
return TestCases(self.test_class, self, tests)
7983

8084
@setter
8185
def keywords(self, keywords):
82-
"""A list-like :class:`~.Keywords` object containing keywords."""
86+
"""Suite setup and teardown as a :class:`~.Keywords` object."""
8387
return Keywords(self.keyword_class, self, keywords)
8488

8589
@property
8690
def id(self):
8791
"""An automatically generated unique id.
8892
89-
The root suite has id ``s1``, its children have ids ``s1-s1``,
90-
``s1-s2``, ..., their children get ids ``s1-s1-s1``, ``s1-s1-s2``,
93+
The root suite has id ``s1``, its child suites have ids ``s1-s1``,
94+
``s1-s2``, ..., their child suites get ids ``s1-s1-s1``, ``s1-s1-s2``,
9195
..., ``s1-s2-s1``, ..., and so on.
96+
97+
The first test in a suite has an id like ``s1-t1``, the second has an
98+
id ``s1-t2``, and so on. Similarly keywords in suites (setup/teardown)
99+
and in tests get ids like ``s1-k1``, ``s1-t1-k1``, and ``s1-s4-t2-k5``.
92100
"""
93101
if not self.parent:
94102
return 's1'
95103
return '%s-s%d' % (self.parent.id, self.parent.suites.index(self)+1)
96104

97-
@property
98-
def longname(self):
99-
"""Suite name prefixed with all parent suite names."""
100-
if not self.parent:
101-
return self.name
102-
return '%s.%s' % (self.parent.longname, self.name)
103-
104105
@property
105106
def test_count(self):
106107
"""Number of the tests in this suite, recursively."""
@@ -142,13 +143,20 @@ def filter(self, included_suites=None, included_tests=None,
142143
included_tags, excluded_tags))
143144

144145
def configure(self, **options):
146+
"""A shortcut to configure a suite using one method call.
147+
148+
:param options: Passed to
149+
:class:`~robot.model.configurer.SuiteConfigurer` that will then
150+
set suite attributes, call :meth:`filter`, etc. as needed.
151+
"""
145152
self.visit(SuiteConfigurer(**options))
146153

147154
def remove_empty_suites(self):
148155
"""Removes all child suites not containing any tests, recursively."""
149156
self.visit(EmptySuiteRemover())
150157

151158
def visit(self, visitor):
159+
""":mod:`Visitor interface <robot.model.visitor>` entry-point."""
152160
visitor.visit_suite(self)
153161

154162

0 commit comments

Comments
 (0)