4. Examples

This section covers cases that are not necessarily contained in the reference documentation, such as

  • factories

  • properties

  • decorators

  • coroutines

  • classes within classes

  • constants in classes

  • type aliases in classes

4.1. Factories

Classes and functions below this point implicitly belong to package/module test_docitem_factory_vec2d.

This section illustrates the use of the Factory section. In this context, Factory does not refer to the well-known design pattern, but to the practical notion of a function that creates an instance of a class and encapsulates the constructor.

In Python, several patterns exist for defining factory functions. In the sample program, we focus on the following:

  • module-level functions with explicit signatures,

  • class methods with explicit signatures,

  • functions or class methods using runtime polymorphism.

The use of @singledispatch is intentionally omitted.

The inventory of factory functions in this example is intentionally redundant for demonstration purposes. In practice, the choice among these patterns depends on personal style and design preferences.

The class Vec2d represents two-dimensional vectors. We would like to create a vector in the following ways:

  • without arguments, yielding the zero vector,

  • by passing two float-valued components x and y,

  • by passing another two-dimensional vector in order to create a copy.

The corresponding implementation is shown below:

from __future__ import annotations
from typing import Any

class Vec2d:
	def __init__(self,x: float = 0.0,y: float = 0.0):
		self._c = [x,y]
	@classmethod
	def from_zero(cls) -> Vec2d:
		return Vec2d()
	@classmethod
	def from_comp(cls,x: float,y: float) -> Vec2d:
		return Vec2d(x,y)
	@classmethod
	def from_vec2d(cls,v: Vec2d) -> Vec2d:
		return Vec2d(v._c[0],v._c[1])
	@classmethod
	def gen(cls,*arg: Any) -> Vec2d:
		return vec2d(*arg)
	def __str__(self) -> str:
		return str(self._c)

def make_vec2d_from_zero() -> Vec2d:
	return Vec2d()
def make_vec2d_from_comp(x: float,y: float) -> Vec2d:
	return Vec2d(x,y)
def make_vec2d_from_vec2d(v: Vec2d) -> Vec2d:
	return Vec2d(v._c[0],v._c[1])

def vec2d(*arg: Any) -> Vec2d:
	match arg:
		case ():
			return Vec2d()
		case (float() as x, float() as y):
			return Vec2d(x, y)
		case (Vec2d() as other,):
			return Vec2d(other._c[0], other._c[1])
		case _:
			raise ValueError(f"Unsupported input: {arg}")

if __name__ == "__main__":
	print(Vec2d.gen())
	print(Vec2d.gen(2.0,3.0))
	print(Vec2d.gen(Vec2d.gen(5.0,7.0)))

	print(vec2d())
	print(vec2d(2.0,3.0))
	print(vec2d(vec2d(5.0,7.0)))

There are eight factory functions, all of which form part of the class API. The Waterloo docstring format captures this relationship by means of a Factory section, as shown in the following excerpt:

class Vec2d:
	"""
	Preamble:
		profile:
			class
		normative_sections:
			Contract, Factory, Public_methods
	Contract:
		general:
			|Must| represent a two-dimensional vector with |type|`float`-valued components.
		constructor:
			Do not use directly; use the factory functions instead.
	Factory:
		Vec2d.from_zero:
			Null vector
		Vec2d.from_comp:
			Vector from components (x, y)
		Vec2d.from_vec2d:
			Vector by copy
		Vec2d.gen:
			All of the above via runtime polymorphism
		make_vec2d_from_zero:
			Null vector
		make_vec2d_from_comp:
			Vector from components (x, y)
		make_vec2d_from_vec2d:
			Vector by copy
		vec2d:
			All of the above via runtime polymorphism
	Public_methods:
		from_zero, from_comp, from_vec2d,
		gen
	"""

Validation ensures that each factory entry listed in this section is resolvable. In the generated HTML artifact, the labels of the factory entries are rendered as links to the documentation boxes of the referenced objects.

Preamble

  • normative sections

    • Contract, Factory, Public methods

Contract

  • general

    • Must represent a two-dimensional vector with float-valued components.

  • constructor

    • Do not use directly; use the factory functions instead.

Factory

Public methods

from_zero, from_comp, from_vec2d, gen

Class

Vec2d

Public Methods in class Vec2d

Signature

@classmethod
test_docitem_factory_vec2d.Vec2d.gen(
*arg: Any
) -> Vec2d

Preamble

  • normative sections

    • Contract, Parameters, Returns, Raises

Contract

  • general

    • Must delegate to the standalone vec2d factory.

    • Must support all input patterns listed in the Vec2d docstring Factory section.

Parameters

  • arg

    Must match one of the patterns supported by vec2d (see below).

Returns

Must return a new Vec2d instance.

Raises

  • ValueError

    • May propagate from vec2d.

Method

Vec2d.gen

Signature

test_docitem_factory_vec2d.vec2d(
*arg: Any
) -> Vec2d

Preamble

  • normative sections

    • Contract, Parameters, Returns, Raises

Contract

  • general

    • Must support all input patterns listed in the Vec2d docstring Factory section.

Parameters

  • arg

    Must match one of the patterns supported by vec2d (see below).

Returns

Must return a new Vec2d instance.

Raises

  • ValueError

    • Must raise if arg does not match any supported pattern.

Function

vec2d

Default module qualifier test_docitem_factory_vec2d ends here. No default module active.

4.2. Properties

The following example shows how properties can be documented. A property is conceptually a member variable of a class and therefore it is listed under Public_variables.

Internally, the accessor methods fget, fset, and fdel are extracted from the property object and rendered as regular methods. Their docstrings provide the normative specification of the property’s behavior, while the property itself defines the public API surface.

Preamble

  • normative sections

    • Contract, Public variables

Contract

  • general

    • Must provide a property for demonstration purposes.

  • constructor

    • default

Public variables

  • value

    A property object providing controlled access to an internal value.

Class

X

Signature

@property
test_docitem_method_property.X.value(
) -> int

Preamble

  • normative sections

    • Contract, Parameters, Returns, Raises

Contract

  • general

    • Must provide the implementation of the getter of property self.value.

Parameters

Returns

Must return the current value of the property.

Raises

<empty>
Method

X.value

Signature

@value.setter
test_docitem_method_property.X.value(
v: int
) -> None

Preamble

  • normative sections

    • Contract, Parameters, Returns, Raises

Contract

  • general

    • Must provide the implementation of the setter of property self.value.

Parameters

  • v

    Value to set.

Returns

None

Raises

<empty>
Method

X.value

4.3. Decorators

Classes and functions below this point implicitly belong to package/module test_docitem_method_decorator.

In this section we test the graphic representation of function and method signatures in presence of decorators, such as @classmethod and @staticmethod. Consider the following test snippet:

#!/usr/bin/env python3

from __future__ import annotations
from types import ModuleType
from typing import Any,Callable,NoReturn,cast

import functools
from abc import ABC, abstractmethod

class B:
	@abstractmethod
	def f_abstractmethod(self) -> NoReturn:
		r"""
		Preamble:
			profile:
				function
			normative_sections:
				Contract, Parameters, Returns, Raises
		Contract:
			general:
				|Must| always raise an exception because\
				it is an abstract method.
		Parameters:
		Returns:
			Does not return.
		Raises:
			NotImplementedError:
				|Must| always raise. Method |must| be\
				implemented by derived class.
		"""
		raise NotImplementedError

class X:
	def f_method(self,q: int) -> None:
		print(f"{self.__class__.__name__}.f_method")
	@classmethod
	def f_classmethod(cls,q: float) -> None:
		print(f"{cls.__name__}.f_classmethod")
	@staticmethod
	def f_staticmethod(q: bool) -> None:
		"""
		Preamble:
			profile:
				function
			normative_sections:
				Contract, Parameters, Returns, Raises
		Contract:
			general:
				|Must| print its name
		Parameters:
			q:
				A dummy parameter.
		Returns:
			|None|
		Raises:
		"""
		print("f_staticmethod")


def trace(fn: Callable[...,Any]) -> Any:
	@functools.wraps(fn)
	def wrapper(*args, **kwargs): #type: ignore[no-untyped-def]
			print("call", fn.__name__)
			return fn(*args, **kwargs)
	return wrapper

@trace
@functools.lru_cache(maxsize=128)
def fib(n: int) -> int:
	"""
	Preamble:
		profile:
			function
		normative_sections:
			Contract, Parameters, Returns, Raises
	Contract:
		general:
			|Must| compute the |var|`n`-th Fibonacci number.
			|Must| cache intermediate results.
	Parameters:
		n:
			The |var|`n` in "compute the |var|`n`-th Fibonacci number".
	Returns:
		The Fibonacci number
	Raises:
		BaseException:
			|May| propagate from |mod| functools.
	"""
	if n < 2:
		return n
	return cast(int,fib(n - 1) + fib(n - 2))


if __name__ == "__main__":
	X().f_method(123)
	X.f_classmethod(4.56)
	X.f_staticmethod(True)

In the document source we have:

.. wtrl_method_signature:: X.f_method

.. wtrl_method_signature_block:: X.f_method

.. wtrl_method_signature:: X.f_classmethod

.. wtrl_method_signature_block:: X.f_classmethod

.. wtrl_method_signature:: X.f_staticmethod

.. wtrl_method_signature_block:: X.f_staticmethod

The output for methods without decorator is:

f_method(q: int) -> None
test_docitem_method_decorator.X.f_method(
q: int
) -> None

For class method we have:

@classmethod f_classmethod(q: float) -> None
@classmethod
test_docitem_method_decorator.X.f_classmethod(
q: float
) -> None

And for static methods:

@staticmethod f_staticmethod(q: bool) -> None
@staticmethod
test_docitem_method_decorator.X.f_staticmethod(
q: bool
) -> None

An example with more than one decorator. The inline representation is not really helpful, and we recommend to switch to the block representation.

@trace @functools.lru_cache(maxsize=128) fib(n: int) -> int
@trace
@functools.lru_cache(maxsize=128)
test_docitem_method_decorator.fib(
n: int
) -> int

Default module qualifier test_docitem_method_decorator ends here. No default module active.

4.4. Coroutines

Let’s take a look at how coroutines are rendered. The important keyword is async. The inline signature looks like this:

async make_coffee() -> str

In the block representation, we get:

async test_docitem_coroutine.make_coffee(
) -> str

Apart from that, coroutines behave like other methods and functions in terms of documentation. The code segment

async def make_coffee() -> str:
	"""
	Preamble:
		profile:
			function
		normative_sections:
			Contract, Parameters, Returns, Raises
	Contract:
		general:
			|Must| represent a non-blocking asynchronous task.
			|Must| take 3 seconds.
	Parameters:
	Returns:
		|Must| return a string telling what has been prepared.
	Raises:
		asyncio.CancelledError:
			|May| throw if the task running the coroutine is cancelled from outside.
	"""
	print("  [Coffee] Coffee machine started...")
	await asyncio.sleep(3)
	print("  [Coffee] Coffee is ready!")
	return "Hot coffee"

is rendered as:

Signature

async test_docitem_coroutine.make_coffee(
) -> str

Preamble

  • normative sections

    • Contract, Parameters, Returns, Raises

Contract

  • general

    • Must represent a non-blocking asynchronous task.

    • Must take 3 seconds.

Parameters

Returns

Must return a string telling what has been prepared.

Raises

Function

make_coffee

For generators we get:

async get_marmalade() -> AsyncGenerator[str, None]

and

async test_docitem_coroutine.get_marmalade(
) -> AsyncGenerator[str, None]

from source code:

async def get_marmalade() -> AsyncGenerator[str,None]:
	"""
	Preamble:
		profile:
			function
		normative_sections:
			Contract, Parameters, Returns, Raises
	Contract:
		general:
			|Must| represent a non-blocking asynchronous task.
			|Must| serve cherry marmalade after a second.
			|Must| serve strawberry marmalade after two seconds.
	Parameters:
	Returns:
		|Must| produce an asynchronous generator yielding strings of marmalade types.
	Raises:
		asyncio.CancelledError:
			|May| be raised if the task is cancelled during an await point.
	"""
	await asyncio.sleep(1)
	yield "Cherry"
	
	await asyncio.sleep(1)
	yield "Strawberry"

4.5. Definitions

A Definitions section can be provided for modules, classes, and callables. Each subsection label consists of a comma-separated list of identifiers. The first identifier denotes the Term, while any following identifiers denote Variations of that term.

The purpose of allowing multiple identifiers is to capture morpho-syntactic and orthographic variations of the same term. In the following example, we define the term sensitive, as well as the capitalized form Sensitive (for sentence-initial usage) and the nominalized form Sensitivity.

r"""
Preamble:
	profile:
		module
	normative_sections:
		Contract, Definitions
Contract:
	general:
		|Must| provide a |label|`Definitions` section\
		for demonstration purposes.
Definitions:
	sensitive, Sensitive, Sensitivity:
		* A widget is |dfn|`sensitive` if and only if it responds to user input.
		* |dfn|`Sensitivity` can be set or unset via the Python API.
"""

The result is

Preamble

  • normative sections

    • Contract, Definitions

Contract

  • general

    • Must provide a Definitions section for demonstration purposes.

Definitions

sensitive [Sensitive, Sensitivity]

  • A widget is sensitive if and only if it responds to user input.

  • Sensitivity can be set or unset via the Python API.

Module

test_docitem_definitions

4.6. Inherited Definitions

The following example demonstrates how definition items can be inherited from module level to class or function level in order to avoid unnecessary repetition.

r"""
Preamble:
	profile:
		module
	normative_sections:
		Contract, Definitions
Contract:
	general:
		|Must| provide a |label|`Definitions` section for demonstration purposes.
Definitions:
	Identifier:
		A string that matches the regular expression |value|`[a-zA-Z_][a-zA-Z0-9_]*`
	Qualified_Identifier:
		A |term|`Qualified_Identifier` is a string formed by concatenating |term|`Identifier` values\
		separated by a dot.
	Term_Z:
		|term|`Term_Z` is another term inherited by objects in this module.
"""

class X:
	r"""
	Preamble:
		profile:
			class
		normative_sections:
			Contract, Definitions
	Contract:
		general:
			|Must| demonstrate inherited definition items.
		constructor:
			default
	Definitions:
		Private_Identifier:
			A |term|`Private_Identifier` is an |term|`Identifier` that starts with a double underscore.
		_inherit:
			Identifier, Term_Z
	"""
	def spam(self) -> None:
		r"""
		Preamble:
			profile:
				method
			normative_sections:
				Contract, Definitions, Parameters, Returns, Raises
		Contract:
			general:
				|Must| demonstrate inherited definition items.
		Definitions:
			_inherit:
				Identifier,Qualified_Identifier
			Anchor:
				An |term|`Anchor` is a special string computed from a |term|`Qualified_Identifier`
				in the following way: 1. The |term|`Qualified_Identifier` is split into segments,
				where each segment is an |term|`Identifier`. 2. To each segment the string
				|value|`"<n>:"` is prepended where |var|`<n>` stands for the length of the segment.
				3. The results are concatenated with |value|`"-"` as separator.
				Example: The |term|`Anchor` of |value|`AAA.BB.C` is |value|`3:AAA-2:BB-1:C`.
		Parameters:
		Returns:
			|None|
		Raises:
		"""

The module docstring is rendered as shown below. It contains commonly used definitions for Identifier and Qualified_Identifier. By means of the subsection Definitions._inherited, each class or function object defined in the module can reference these definitions in its free-text sections.

Preamble

  • normative sections

    • Contract, Definitions

Contract

  • general

    • Must provide a Definitions section for demonstration purposes.

Definitions

Identifier

A string that matches the regular expression [a-zA-Z_][a-zA-Z0-9_]*

Qualified_Identifier

A Qualified_Identifier is a string formed by concatenating Identifier values separated by a dot.

Term_Z

Term_Z is another term inherited by objects in this module.

Module

test_docitem_definitions_inherited

The class docstring inherits the terms Identifier and Term_Z, and defines a new term Private_Identifier based on Identifier. Referencing inherited terms using |term| is valid and results in semantic highlighting in the generated HTML artifact.

Preamble

  • normative sections

    • Contract, Definitions

Contract

  • general

    • Must demonstrate inherited definition items.

  • constructor

    • default

Definitions

<Terms inherited from module>

Identifier, Term_Z

Private_Identifier

A Private_Identifier is an Identifier that starts with a double underscore.

Class

X

The method docstring inherits the terms Identifier and Qualified_Identifier and uses them to define a new term Anchor.

Signature

test_docitem_definitions_inherited.X.spam(
) -> None

Preamble

  • normative sections

    • Contract, Definitions, Parameters, Returns, Raises

Contract

  • general

    • Must demonstrate inherited definition items.

Definitions

<Terms inherited from module>

Identifier, Qualified_Identifier

Anchor

An Anchor is a special string computed from a Qualified_Identifier in the following way: 1. The Qualified_Identifier is split into segments, where each segment is an Identifier. 2. To each segment the string "<n>:" is prepended where <n> stands for the length of the segment. 3. The results are concatenated with "-" as separator. Example: The Anchor of AAA.BB.C is 3:AAA-2:BB-1:C.

Parameters

Returns

None

Raises

<empty>
Method

X.spam

4.7. A display cabinet of sections

The following example shows the docstring of one of Waterloo’s built-in functions, get_num_indent. The function has the following signature:

sdv.doc.waterloo.docitem.get_num_indent(
tr: tracer
line: str
indent_scheme: int
) -> int

and the docstring reads:

r"""
Preamble:
        profile:
                function
        normative_sections:
                Definitions, Contract, Parameters, Returns, Raises
        status:
                stable
Definitions:
        TAB:
                A scheme that demands indentation by means of an integer number\
                of tab characters (ASCII |value|`0x09`).\
                For this scheme, |var|`INDENT_UNIT` is a single tab character.
        SPC4:
                A scheme that demands indentation by means of an integer multiple\
                of four space characters (ASCII |value|`0x20`).
                For this scheme, |var|`INDENT_UNIT` consists of four space characters.
Contract:
        general:
                |Must| accept a single line string and an indentation scheme.
                |Must| count the number of leading indentations of the input according\
                to the scheme passed.
                |Must| accept an empty string.
Parameters:
        tr:
                Tracer for better error messages
        line:
                A single line string.
        indent_scheme:
                A symbolic value representing one of the two possible indentation\
                schemes |term|`TAB` or |term|`SPC4`.
Returns:
        |Must| return the number of indentations found at the beginning of the string\
        in units as decribed by the indentation scheme passed.
Raises:
        RuntimeError:
                |Must| raise if prefix contains a mix not representable\
                as |var|`n` repetitions of |var|`INDENT_UNIT`.
                |Must| raise if the leading white space characters (tabs or four spaces)\
                at the beginning of the line\
                cannot be described by the indentation scheme passed.
"""

Note that we have a section here called Definitions, which significantly reduces the load on the normative sections Parameters and Raises. Definitions is normative because we list it under Preamble.normative_sections, although it does not contain a normativity keyword. Yet the definitions given there are binding for the entire documentation box rendered from this docstring.

4.8. Subections requires, ensures, and invariants

This example illustrates the use of the subsections requires, ensures, and invariants. These labels follow the classic Design-by-Contract distinction between preconditions, postconditions, and invariants:

  • requires specifies obligations of the caller. If a requirement is violated, the function’s behaviour is undefined within the normative model.

  • ensures specifies guarantees provided by the function upon successful completion, assuming all requirements were met.

  • invariants specifies properties that must hold independently of a particular call. They typically express structural, semantic, or algebraic constraints that characterize the function itself (e.g. idempotence, determinism, monotonicity).

Syntactically, these subsections are not treated differently from general. Their purpose is semantic refinement: they allow normative statements to be grouped according to their logical role within the contract. This separation improves clarity for both human readers and automated tooling, as normative statements can be classified according to their contractual role.

test_docitem_invariants_requires_ensures.normalize_identifier(
name: str
) -> str

Signature

test_docitem_invariants_requires_ensures.normalize_identifier(
name: str
) -> str

Preamble

  • normative sections

    • Contract, Parameters, Returns, Raises

Contract

  • general

    • Must normalize a user-provided identifier to a canonical Waterloo Identifier form.

  • requires

    • Must be passed a non-empty string.

  • ensures

    • Must return a string that matches the Identifier pattern [a-zA-Z_][a-zA-Z0-9_]*.

    • Must return a value that is semantically equivalent to the input name under the tool’s normalization policy.

  • invariants

    • Must be idempotent: normalize_identifier(normalize_identifier(name)) == normalize_identifier(name).

    • Must be deterministic: repeated calls with equal input return equal output.

Parameters

  • name

    Input identifier string.

Returns

Must return the canonical identifier.

Raises

<empty>
Function

normalize_identifier

4.9. Class within class

Classes and functions below this point implicitly belong to package/module test_docitem_class_class.

The following example of a class Y nested in another class X, both equipped with a method, can be rendered by a single command

.. wtrl_autodoc_class_full:: X

This directive recusrsively iterates over the class hierarchy and ramifies to the other member, like e.g. methods. The source code is:

class X:
	"""
	Preamble:
		profile:
			class
		normative_sections:
			Contract, Public_methods, Public_classes
	Contract:
		general:
			|Must| provide a method for writing a greeting message.
		constructor:
			|Must| be default-constructible
	Public_methods:
		greeting
	Method_overview:
		greeting:
			A simple test method.
	Public_classes:
		Y
	Class_overview:
		Y:
			A nested class for testing purposes.
	"""
	def greeting(self):
		"""
		Preamble:
			profile:
				method
			normative_sections:
				Contract, Parameters, Returns,Raises
		Contract:
			general:
				|Must| render a greeting message to ``stdout``.
		Parameters:
		Returns:
			|None|
		Raises:
		"""
		print("Hello world!")
	class Y:
		"""
		Preamble:
			profile:
				class
			normative_sections:
				Contract, Public_methods
		Contract:
			general:
				A class, nested in class :wtrl_type:`X`, |must| do nothing.
			constructor:
				|Must| be default-constructible
		Public_methods:
			greeting_but_in_Y
		"""
		def greeting_but_in_Y(self):
			"""
			Preamble:
				profile:
					method
				normative_sections:
					Contract, Parameters, Returns,Raises
			Contract:
				general:
					|Must| render a greeting message to ``stdout``.
			Parameters:
			Returns:
				|None|
			Raises:
			"""
			print("Hello world from Y!")

And the result is:

Preamble

  • normative sections

    • Contract, Public methods, Public classes

Contract

  • general

    • Must provide a method for writing a greeting message.

  • constructor

    • Must be default-constructible

Public methods

greeting

Method overview

  • greeting

    A simple test method.

Public classes

Y

Class overview

  • Y

    A nested class for testing purposes.

Class

X

Nested classes in X

Preamble

  • normative sections

    • Contract, Public methods

Contract

  • general

    • A class, nested in class X, must do nothing.

  • constructor

    • Must be default-constructible

Public methods

greeting_but_in_Y

Class

X.Y

Public Methods in class X.Y

Signature

test_docitem_class_class.X.Y.greeting_but_in_Y(
) -> Any

Preamble

  • normative sections

    • Contract, Parameters, Returns, Raises

Contract

  • general

    • Must render a greeting message to stdout.

Parameters

Returns

None

Raises

<empty>
Method

X.Y.greeting_but_in_Y

Public Methods in class X

Signature

test_docitem_class_class.X.greeting(
) -> Any

Preamble

  • normative sections

    • Contract, Parameters, Returns, Raises

Contract

  • general

    • Must render a greeting message to stdout.

Parameters

Returns

None

Raises

<empty>
Method

X.greeting

Default module qualifier test_docitem_class_class ends here. No default module active.

4.10. Formatting tests

A simple test for demonstrating the pipe operator convention in the output layer.

Signature

test_docitem_function_pipe.f(
) -> None

Preamble

  • normative sections

    • Contract, Parameters, Returns, Raises

Contract

  • general

Description

A paragraph starting in a physical line continued in the next physical line.

1. Item 1

2. Item 2

Parameters

Returns

A paragraph starting in a physical line continued in the next physical line.

Another paragraph

Raises

<empty>

Notes

A note

A paragraph starting in a physical line continued in the next physical line.

Another paragraph

A second note

A paragraph starting in a physical line continued in the next physical line.

Another paragraph

Function

f