doc/main/signaltypes: Change how units are documented.

Previously, arguments had "types" like ":ref:`speed`", which would be rendered by Sphinx as a hyperlink and displayed as "rotational speed: deg/s".

This worked well enough but it had a few shortcomings:
- It looks bad on autocomplete, which doesn't know how to render RST with cross references.
- No real type information: int, float, ... ?
- Some units are very long, making the docs less concise.
- The unified approach doesn't always work. Some method arguments can be either deg/s or mm/s, depending on the application. This gets even lengthier.
- It's hard to maintain if you don't know what to link to.

In the new approach, the unit is just added to the type as plain text. Instead of giving the physical signal name, we give the real data type, which is more standard. The physical signal name is usually apparent from the docstring anyway, and can be inferred from the unit as well. This also solves all of the above problems.

Since most Pybricks methods allow both int and float for numeric inputs, we document these as a Number type, which is the union of int and float. Since the type is no longer directly tied to the type, we can still document the return type correctly, since this is never a union.

Instead of having complicated hacks in Sphinx to make this work (I have tried many), it turns out we can conveniently suppress broken references and still display the units. The only downside is that units will no longer have hyperlinks to the signal pages, at least for the moment. We should be able to make on_missing_reference a bit smarter to format the units as we see fit if needed.

The approach here is exemplified in the Motor.run_until_stalled method. Subsequent commits will apply it everywhere. Also clarify the behavior for duty_limit=None while we are updating this code anyway.
This commit is contained in:
Laurens Valk
2022-05-09 10:28:31 +02:00
parent f934fdd9bd
commit 5e9a8c5737
3 changed files with 40 additions and 5 deletions
+11
View File
@@ -304,8 +304,19 @@ class AvailabilityDirective(Directive):
nodes.Text(', '.join(self.options))]
def on_missing_reference(app, env, node, contnode):
# References with special characters can't exist, so we have to supress
# warnings when Sphinx tries to cross reference units like deg/s. For
# consistency, we also treat units without special characters this way.
for unit in ['deg', 'deg/s', 'mm/s', '%']:
if unit == contnode.rawsource or unit == str(contnode):
return contnode
def setup(app: Sphinx):
app.add_directive('availability', AvailabilityDirective)
app.connect('missing-reference', on_missing_reference)
# -- Python domain hacks ---------------------------------------------------
+20
View File
@@ -4,6 +4,26 @@ Signals and Units
Many commands allow you to specify arguments in terms of well-known physical
quantities. This page gives an overview of each quantity and its unit.
Numbers
~~~~~~~
.. class:: Number
Numbers can be represented as integers or floating point values:
* Integers (:class:`int <ubuiltins.int>`) are whole numbers
like ``15`` or ``-123``.
* Floating point values (:class:`float <ubuiltins.float>`) are decimal
numbers like ``3.14`` or ``-123.45``.
If you see :class:`Number <Number>` as the argument type, both
:class:`int <ubuiltins.int>` and :class:`float <ubuiltins.float>` may be used.
For example, :func:`wait(15) <pybricks.tools.wait>` and
:func:`wait(15.75) <pybricks.tools.wait>` are both allowed. In most functions,
however, your input value will be truncated to a whole number anyway. In this
example, either command makes the program pause for just 15 milliseconds.
Time
~~~~~~
+9 -5
View File
@@ -335,17 +335,21 @@ class Motor(DCMotor):
pass
def run_until_stalled(self, speed, then=Stop.COAST, duty_limit=None):
"""Runs the motor at a constant speed until it stalls.
"""
run_until_stalled(speed, then=Stop.COAST, duty_limit=None) -> int, deg
Runs the motor at a constant speed until it stalls.
Arguments:
speed (:ref:`speed`): Speed of the motor.
speed (Number, deg/s): Speed of the motor.
then (Stop): What to do after coming to a standstill.
duty_limit (:ref:`percentage`): Duty cycle limit during this
duty_limit (Number, %): Duty cycle limit during this
command. This is useful to avoid applying the full motor
torque to a geared or lever mechanism.
torque to a geared or lever mechanism. If it is ``None``, the
duty limit won't be changed during this command.
Returns:
:ref:`angle`: Angle at which the motor becomes stalled.
Angle at which the motor becomes stalled.
"""
pass