jedi: improve signature information

This fixes numerous issues where jedi provided incomplete or poorly
formatted signatures and docstrings.

Issue: https://github.com/pybricks/pybricks-code/issues/932
This commit is contained in:
David Lechner
2022-06-25 23:54:49 -05:00
parent a758eaa1db
commit e95386e23e
12 changed files with 1131 additions and 300 deletions
+5
View File
@@ -2,6 +2,11 @@
<!-- refer to https://keepachangelog.com/en/1.0.0/ for guidance -->
## Unreleased
### Fixed
- Fixed more type hints and improved compatibility with jedi.
## 3.2.0b1-r2 - 2022-06-24
### Changed
+13 -1
View File
@@ -60,6 +60,14 @@ category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "docstring-parser"
version = "0.14.1"
description = "Parse Python docstrings in reST, Google and Numpydoc format"
category = "main"
optional = false
python-versions = ">=3.6,<4.0"
[[package]]
name = "flake8"
version = "4.0.1"
@@ -255,7 +263,7 @@ python-versions = ">=3.7"
[metadata]
lock-version = "1.1"
python-versions = ">= 3.10, < 3.11"
content-hash = "9b91afcb8ccd46cc8be150f48170e45e86a6c65686d7e6e97ac3de67988c0ff5"
content-hash = "207f86e378bd5ea0d9751f27aa1b8eff5709ccce56851eeede184a4c343b8ac1"
[metadata.files]
atomicwrites = [
@@ -299,6 +307,10 @@ colorama = [
{file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
{file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
]
docstring-parser = [
{file = "docstring_parser-0.14.1-py3-none-any.whl", hash = "sha256:14ac6ec1f1ba6905c4d8cb90fd0bc55394f5678183752c90e44812bf28d7a515"},
{file = "docstring_parser-0.14.1.tar.gz", hash = "sha256:2c77522e31b7c88b1ab457a1f3c9ae38947ad719732260ba77ee8a3deb58622a"},
]
flake8 = [
{file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"},
{file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"},
+1
View File
@@ -10,6 +10,7 @@ python = ">= 3.10, < 3.11"
pybricks = "^3.2.0b1-r2"
jedi = "^0.18.1"
typing-extensions = "^4.2.0"
docstring-parser = "0.14.1"
[tool.poetry.dev-dependencies]
pytest = "^7.1.2"
+91 -9
View File
@@ -1,8 +1,12 @@
from enum import IntEnum
import io
import json
from typing_extensions import TypedDict, NotRequired
import re
from enum import IntEnum
import docstring_parser
import jedi
from jedi.api.classes import Completion, Signature, ParamName
from jedi.api.classes import BaseName, Completion, Name, ParamName, Signature
from typing_extensions import NotRequired, TypedDict
# Packages included in Pybricks firmware that ships with Pybricks Code.
PYBRICKS_CODE_PACKAGES = {
@@ -132,6 +136,23 @@ class Command(TypedDict):
arguments: NotRequired[list]
class UriComponents(TypedDict):
scheme: str
authority: str
path: str
query: str
fragment: str
class IMarkdownString(TypedDict):
value: str
isTrusted: NotRequired[bool]
supportThemeIcons: NotRequired[bool]
supportHtml: NotRequired[bool]
baseUri: NotRequired[UriComponents]
uris: NotRequired[dict[str, UriComponents]]
class CompletionItemKind(IntEnum):
Method = 0
Function = 1
@@ -251,6 +272,66 @@ def _is_pybricks(c: Completion) -> bool:
return True
def _get_docstring(name: BaseName) -> str:
"""
Gets the docstring for a name.
"""
docstring = name.docstring(raw=True)
# jedi does not appear to be smart enough to use __init__ docstring for class
if name.type == "class" and isinstance(name, Name):
n: Name
for n in name.defined_names():
if n.name == "__init__":
docstring = "\n".join([docstring, _get_docstring(n)])
return docstring
def _parse_docstring(text: str) -> tuple[IMarkdownString, list[IMarkdownString]]:
"""
Parses a doc string, removes the overload declarations, performs some
fixups and extracts the individual parameter strings.
Args:
The raw docstring.
Returns:
A tuple with the fixed up doc string and a list of parameter doc strings.
"""
# docstring_parser does not support signatures at the beginning of the
# docstring, so we have to remove them
# https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html#confval-autodoc_docstring_signature
lines, end_of_signatures = [], False
for line in io.StringIO(text).readlines():
# signatures look like: "name(params...)"
if not end_of_signatures and re.match(r"^\w+\(.*\)", line):
continue
end_of_signatures = True
# TODO: we may want to do some restructured text to markdown fixes,
# e.g. strip off ":class:" from ":class:`SomeClass`" and replace
# ".. some-directive::" with an appropriate header
lines.append(line)
text = "".join(lines)
doc = docstring_parser.parse(text, docstring_parser.DocstringStyle.GOOGLE)
# convert to numpy doc for better markdown rendering (section names are underlined)
numpy_doc = docstring_parser.compose(doc, docstring_parser.Style.NUMPYDOC)
docstring = IMarkdownString(value=numpy_doc)
param_docstrings = [IMarkdownString(value=p.description) for p in doc.params]
return docstring, param_docstrings
def _map_completion_kind(type: str) -> CompletionItemKind:
match type:
case "module":
@@ -294,22 +375,23 @@ def _map_completion_item(
endLineNumber=line,
endColumn=column,
),
documentation=completion.docstring(),
documentation=_parse_docstring(_get_docstring(completion))[0],
)
def _map_parameter(param: ParamName) -> ParameterInformation:
# NB: it is not possible to get docstring for individual parameters from jedi
return ParameterInformation(label=param.to_string())
def _map_parameter(param: ParamName, docstr: str) -> ParameterInformation:
return ParameterInformation(label=param.to_string(), documentation=docstr)
def _map_signature(signature: Signature) -> SignatureInformation:
optional = {} if signature.index is None else dict(activeParameter=signature.index)
docstr, param_docstr = _parse_docstring(_get_docstring(signature))
return SignatureInformation(
label=signature.to_string(),
documentation=signature.docstring(),
parameters=[_map_parameter(p) for p in signature.params],
documentation=docstr,
parameters=[_map_parameter(*p) for p in zip(signature.params, param_docstr)],
**optional,
)
+1 -190
View File
@@ -7,7 +7,7 @@ Tests for correct code completion of the InventorHub class.
import json
from pybricks_jedi import SignatureHelp, complete, get_signatures, CompletionItem
from pybricks_jedi import complete, CompletionItem
IMPORT = "from pybricks.hubs import InventorHub"
CREATE_INSTANCE = "hub = InventorHub()"
@@ -53,22 +53,6 @@ def test_hub_dot_battery_dot():
]
def test_hub_dot_battery_dot_current():
line = "hub.battery.current("
code = _create_snippet(line)
signatures: SignatureHelp = json.loads(get_signatures(code, 3, len(line) + 1))
assert [s["label"] for s in signatures["signatures"]] == ["current() -> int"]
assert [s.get("activeParameter") for s in signatures["signatures"]] == [None]
def test_hub_dot_battery_dot_voltage():
line = "hub.battery.voltage("
code = _create_snippet(line)
signatures: SignatureHelp = json.loads(get_signatures(code, 3, len(line) + 1))
assert [s["label"] for s in signatures["signatures"]] == ["voltage() -> int"]
assert [s.get("activeParameter") for s in signatures["signatures"]] == [None]
def test_hub_dot_buttons_dot():
line = "hub.buttons."
code = _create_snippet(line)
@@ -78,16 +62,6 @@ def test_hub_dot_buttons_dot():
]
def test_hub_dot_battery_dot_pressed():
line = "hub.buttons.pressed("
code = _create_snippet(line)
signatures: SignatureHelp = json.loads(get_signatures(code, 3, len(line) + 1))
assert [s["label"] for s in signatures["signatures"]] == [
"pressed() -> Tuple[Button]"
]
assert [s.get("activeParameter") for s in signatures["signatures"]] == [None]
def test_hub_dot_charger_dot():
line = "hub.charger."
code = _create_snippet(line)
@@ -99,22 +73,6 @@ def test_hub_dot_charger_dot():
]
def test_hub_dot_charger_dot_connected():
line = "hub.charger.connected("
code = _create_snippet(line)
signatures: SignatureHelp = json.loads(get_signatures(code, 3, len(line) + 1))
assert [s["label"] for s in signatures["signatures"]] == ["connected() -> bool"]
assert [s.get("activeParameter") for s in signatures["signatures"]] == [None]
def test_hub_dot_charger_dot_current():
line = "hub.charger.current("
code = _create_snippet(line)
signatures: SignatureHelp = json.loads(get_signatures(code, 3, len(line) + 1))
assert [s["label"] for s in signatures["signatures"]] == ["current() -> int"]
assert [s.get("activeParameter") for s in signatures["signatures"]] == [None]
def test_hub_dot_display_dot():
line = "hub.display."
code = _create_snippet(line)
@@ -157,62 +115,6 @@ def test_hub_dot_light_dot():
]
def test_hub_dot_light_dot_animate():
line = "hub.light.animate("
code = _create_snippet(line)
signatures: SignatureHelp = json.loads(get_signatures(code, 3, len(line) + 1))
assert [s["label"] for s in signatures["signatures"]] == [
"animate(colors: Collection[Color], interval: Number) -> None",
]
assert [s.get("activeParameter") for s in signatures["signatures"]] == [0]
assert [
[p["label"] for p in s["parameters"]] for s in signatures["signatures"]
] == [["colors: Collection[Color]", "interval: Number"]]
def test_hub_dot_light_dot_animate2():
line = "hub.light.animate([],"
code = _create_snippet(line)
signatures: SignatureHelp = json.loads(get_signatures(code, 3, len(line) + 1))
assert [s["label"] for s in signatures["signatures"]] == [
"animate(colors: Collection[Color], interval: Number) -> None",
]
assert [s.get("activeParameter") for s in signatures["signatures"]] == [1]
assert [
[p["label"] for p in s["parameters"]] for s in signatures["signatures"]
] == [["colors: Collection[Color]", "interval: Number"]]
def test_hub_dot_light_dot_blink():
line = "hub.light.blink("
code = _create_snippet(line)
signatures: SignatureHelp = json.loads(get_signatures(code, 3, len(line) + 1))
assert [s["label"] for s in signatures["signatures"]] == [
"blink(color: Color, durations: Collection[int]) -> None",
]
assert [s.get("activeParameter") for s in signatures["signatures"]] == [0]
def test_hub_dot_light_dot_blink2():
line = "hub.light.blink(Color.RED,"
code = _create_snippet(line)
signatures: SignatureHelp = json.loads(get_signatures(code, 3, len(line) + 1))
assert [s["label"] for s in signatures["signatures"]] == [
"blink(color: Color, durations: Collection[int]) -> None",
]
assert [s.get("activeParameter") for s in signatures["signatures"]] == [1]
def test_hub_dot_light_dot_on():
line = "hub.light.on("
code = _create_snippet(line)
signatures: SignatureHelp = json.loads(get_signatures(code, 3, len(line) + 1))
assert [s["label"] for s in signatures["signatures"]] == [
"on(color: Color) -> None",
]
assert [s.get("activeParameter") for s in signatures["signatures"]] == [0]
def test_hub_dot_speaker_dot():
line = "hub.speaker."
code = _create_snippet(line)
@@ -224,57 +126,6 @@ def test_hub_dot_speaker_dot():
]
def test_hub_dot_speaker_dot_beep():
line = "hub.speaker.beep("
code = _create_snippet(line)
signatures: SignatureHelp = json.loads(get_signatures(code, 3, len(line) + 1))
assert [s["label"] for s in signatures["signatures"]] == [
"beep(frequency: Number=500, duration: Number=100) -> None",
]
assert [s.get("activeParameter") for s in signatures["signatures"]] == [0]
def test_hub_dot_speaker_dot_beep2():
line = "hub.speaker.beep(100,"
code = _create_snippet(line)
signatures: SignatureHelp = json.loads(get_signatures(code, 3, len(line) + 1))
assert [s["label"] for s in signatures["signatures"]] == [
"beep(frequency: Number=500, duration: Number=100) -> None",
]
assert [s.get("activeParameter") for s in signatures["signatures"]] == [1]
def test_hub_dot_speaker_dot_play_notes():
line = "hub.speaker.play_notes("
code = _create_snippet(line)
signatures: SignatureHelp = json.loads(get_signatures(code, 3, len(line) + 1))
assert [s["label"] for s in signatures["signatures"]] == [
"play_notes(notes: Iterable[str], tempo: Number=120) -> None",
]
assert [s.get("activeParameter") for s in signatures["signatures"]] == [0]
def test_hub_dot_speaker_dot_play_notes2():
line = "hub.speaker.play_notes([],"
code = _create_snippet(line)
signatures: SignatureHelp = json.loads(get_signatures(code, 3, len(line) + 1))
assert [s["label"] for s in signatures["signatures"]] == [
"play_notes(notes: Iterable[str], tempo: Number=120) -> None",
]
assert [s.get("activeParameter") for s in signatures["signatures"]] == [1]
def test_hub_dot_speaker_dot_volume():
line = "hub.speaker.volume("
code = _create_snippet(line)
signatures: SignatureHelp = json.loads(get_signatures(code, 3, len(line) + 1))
assert [s["label"] for s in signatures["signatures"]] == [
"volume() -> int",
"volume(volume: Number) -> None",
]
assert [s.get("activeParameter") for s in signatures["signatures"]] == [None, 0]
def test_hub_dot_system_dot():
line = "hub.system."
code = _create_snippet(line)
@@ -285,43 +136,3 @@ def test_hub_dot_system_dot():
"set_stop_button",
"shutdown",
]
def test_hub_dot_system_dot_name():
line = "hub.system.name("
code = _create_snippet(line)
signatures: SignatureHelp = json.loads(get_signatures(code, 3, len(line) + 1))
assert [s["label"] for s in signatures["signatures"]] == [
"name() -> str",
]
assert [s.get("activeParameter") for s in signatures["signatures"]] == [None]
def test_hub_dot_system_dot_reset_reason():
line = "hub.system.reset_reason("
code = _create_snippet(line)
signatures: SignatureHelp = json.loads(get_signatures(code, 3, len(line) + 1))
assert [s["label"] for s in signatures["signatures"]] == [
"reset_reason() -> int",
]
assert [s.get("activeParameter") for s in signatures["signatures"]] == [None]
def test_hub_dot_system_dot_set_stop_button():
line = "hub.system.set_stop_button("
code = _create_snippet(line)
signatures: SignatureHelp = json.loads(get_signatures(code, 3, len(line) + 1))
assert [s["label"] for s in signatures["signatures"]] == [
"set_stop_button(button: Optional[Union[Button, Iterable[Button]]]) -> None",
]
assert [s.get("activeParameter") for s in signatures["signatures"]] == [0]
def test_hub_dot_system_dot_shutdown():
line = "hub.system.shutdown("
code = _create_snippet(line)
signatures: SignatureHelp = json.loads(get_signatures(code, 3, len(line) + 1))
assert [s["label"] for s in signatures["signatures"]] == [
"shutdown() -> None",
]
assert [s.get("activeParameter") for s in signatures["signatures"]] == [None]
+779
View File
@@ -0,0 +1,779 @@
# SPDX-License-Identifier: MIT
# Copyright (c) 2022 The Pybricks Authors
"""
Tests for correct signatures of the pupdevices.Motor class.
"""
from itertools import zip_longest
import json
import pytest
from pybricks_jedi import SignatureHelp, get_signatures
def _get_function_signature(module: str, function: str) -> SignatureHelp:
"""
Gets the signature help object for code like::
from {module} import {function}
{function}(
Args:
module: the module name
func: the function name (a function in the module)
"""
code = f"from {module} import {function}; {function}("
return json.loads(get_signatures(code, 1, len(code) + 1))
FUNCTION_PARAMS = [
pytest.param("pybricks.tools", "wait", [(["time: Number"], "None")]),
pytest.param(
"pybricks.geometry",
"vector",
[
(["x: float", "y: float"], "Matrix"),
(["x: float", "y: float", "z: float"], "Matrix"),
],
),
]
@pytest.mark.parametrize("module,function,signatures", FUNCTION_PARAMS)
def test_get_signature_for_functions(
module: str, function: str, signatures: list[tuple[list[str], str]]
):
help = _get_function_signature(module, function)
assert help["activeSignature"] == 0
assert help["activeParameter"] == 0
assert help["signatures"]
for sig, (params, returns) in zip_longest(help["signatures"], signatures):
assert sig["label"] == f"{function}({', '.join(params)}) -> {returns}"
assert sig["documentation"]["value"]
# ensure signatures are stripped from doc comment
assert not sig["documentation"]["value"].startswith(f"{function}(")
for pi, p in zip_longest(sig["parameters"], params):
assert pi["label"] == p
assert pi["documentation"]["value"]
def _get_constructor_signature(module: str, type: str) -> SignatureHelp:
"""
Gets the signature help object for code like::
from {module} import {type}
instance = {type}(
Args:
module: the module name
type: the type name (a type in the module)
"""
code = f"from {module} import {type}; {type}("
return json.loads(get_signatures(code, 1, len(code) + 1))
CONSTRUCTOR_PARAMS = [
pytest.param("pybricks.hubs", "MoveHub", [[]]),
pytest.param("pybricks.hubs", "CityHub", [[]]),
pytest.param(
"pybricks.hubs",
"TechnicHub",
[["top_side: Axis=Axis.Z", "front_side: Axis=Axis.X"]],
),
pytest.param(
"pybricks.hubs",
"PrimeHub",
[["top_side: Axis=Axis.Z", "front_side: Axis=Axis.X"]],
),
pytest.param(
"pybricks.pupdevices",
"DCMotor",
[["port: Port", "positive_direction: Direction=Direction.CLOCKWISE"]],
),
pytest.param(
"pybricks.pupdevices",
"Motor",
[
[
"port: Port",
"positive_direction: Direction=Direction.CLOCKWISE",
"gears: Optional[Union[Collection[int], Collection[Collection[int]]]]=None",
"reset_angle: bool=True",
]
],
),
pytest.param("pybricks.pupdevices", "TiltSensor", [["port: Port"]]),
pytest.param("pybricks.pupdevices", "InfraredSensor", [["port: Port"]]),
pytest.param("pybricks.pupdevices", "ColorDistanceSensor", [["port: Port"]]),
pytest.param(
"pybricks.pupdevices",
"PFMotor",
[
[
"sensor: ColorDistanceSensor",
"channel: int",
"color: Color",
"positive_direction: Direction=Direction.CLOCKWISE",
]
],
),
pytest.param("pybricks.pupdevices", "ColorSensor", [["port: Port"]]),
pytest.param("pybricks.pupdevices", "UltrasonicSensor", [["port: Port"]]),
pytest.param("pybricks.pupdevices", "ForceSensor", [["port: Port"]]),
pytest.param("pybricks.pupdevices", "ColorLightMatrix", [["port: Port"]]),
pytest.param("pybricks.pupdevices", "Light", [["port: Port"]]),
pytest.param(
"pybricks.pupdevices",
"Remote",
[["name: Optional[str]=None", "timeout: int=10000"]],
),
# TODO: iodevices go here
pytest.param(
"pybricks.parameters",
"Color",
[["h: Number", "s: Number=100", "v: Number=100"]],
),
pytest.param(
"pybricks.robotics",
"DriveBase",
[
[
"left_motor: Motor",
"right_motor: Motor",
"wheel_diameter: Number",
"axle_track: Number",
]
],
),
pytest.param(
"pybricks.geometry",
"Matrix",
[["rows: Sequence[Sequence[float]]"]],
),
]
@pytest.mark.parametrize("module,type,signatures", CONSTRUCTOR_PARAMS)
def test_get_signature_for_constructors(
module: str, type: str, signatures: list[list[str]]
):
help = _get_constructor_signature(module, type)
assert help["activeSignature"] == 0
assert help["activeParameter"] == 0
assert help["signatures"]
for sig, params in zip_longest(help["signatures"], signatures):
assert sig["label"] == f"{type}({', '.join(params)})"
assert sig["documentation"]["value"]
# ensure signatures are stripped from doc comment
assert not sig["documentation"]["value"].startswith(f"{type}(")
for pi, p in zip_longest(sig["parameters"], params):
assert pi["label"] == p
assert pi["documentation"]["value"]
def _get_method_signature(module: str, type: str, method: str) -> SignatureHelp:
"""
Gets the signature help object for code like::
from {module} import {type}
instance = {type}()
instance.{method}(
Args:
module: the module name
type: the type name (a type in the module)
method: the method name (a method of the type)
"""
code = f"from {module} import {type}; x = {type}(); x.{method}("
return json.loads(get_signatures(code, 1, len(code) + 1))
METHOD_PARAMS = [
pytest.param("pybricks.hubs", "MoveHub", "light.on", [(["color: Color"], "None")]),
pytest.param("pybricks.hubs", "MoveHub", "light.off", [([], "None")]),
pytest.param(
"pybricks.hubs",
"MoveHub",
"light.blink",
[(["color: Color", "durations: Collection[Number]"], "None")],
),
pytest.param(
"pybricks.hubs",
"MoveHub",
"light.animate",
[(["colors: Collection[Color]", "interval: Number"], "None")],
),
pytest.param("pybricks.hubs", "MoveHub", "imu.up", [([], "Side")]),
pytest.param(
"pybricks.hubs", "MoveHub", "imu.acceleration", [([], "Tuple[int, int, int]")]
),
pytest.param("pybricks.hubs", "MoveHub", "battery.voltage", [([], "int")]),
pytest.param("pybricks.hubs", "MoveHub", "battery.current", [([], "int")]),
pytest.param(
"pybricks.hubs", "MoveHub", "button.pressed", [([], "Collection[Button]")]
),
pytest.param(
"pybricks.hubs",
"MoveHub",
"system.set_stop_button",
[(["button: Optional[Union[Button, Iterable[Button]]]"], "None")],
),
pytest.param("pybricks.hubs", "MoveHub", "system.name", [([], "str")]),
pytest.param("pybricks.hubs", "MoveHub", "system.shutdown", [([], "None")]),
pytest.param("pybricks.hubs", "MoveHub", "system.reset_reason", [([], "int")]),
pytest.param("pybricks.hubs", "CityHub", "light.on", [(["color: Color"], "None")]),
pytest.param("pybricks.hubs", "CityHub", "light.off", [([], "None")]),
pytest.param(
"pybricks.hubs",
"CityHub",
"light.blink",
[(["color: Color", "durations: Collection[Number]"], "None")],
),
pytest.param(
"pybricks.hubs",
"CityHub",
"light.animate",
[(["colors: Collection[Color]", "interval: Number"], "None")],
),
pytest.param("pybricks.hubs", "CityHub", "battery.voltage", [([], "int")]),
pytest.param("pybricks.hubs", "CityHub", "battery.current", [([], "int")]),
pytest.param(
"pybricks.hubs", "CityHub", "button.pressed", [([], "Collection[Button]")]
),
pytest.param(
"pybricks.hubs",
"CityHub",
"system.set_stop_button",
[(["button: Optional[Union[Button, Iterable[Button]]]"], "None")],
),
pytest.param("pybricks.hubs", "CityHub", "system.name", [([], "str")]),
pytest.param("pybricks.hubs", "CityHub", "system.shutdown", [([], "None")]),
pytest.param("pybricks.hubs", "CityHub", "system.reset_reason", [([], "int")]),
pytest.param(
"pybricks.hubs", "TechnicHub", "light.on", [(["color: Color"], "None")]
),
pytest.param("pybricks.hubs", "TechnicHub", "light.off", [([], "None")]),
pytest.param(
"pybricks.hubs",
"TechnicHub",
"light.blink",
[(["color: Color", "durations: Collection[Number]"], "None")],
),
pytest.param(
"pybricks.hubs",
"TechnicHub",
"light.animate",
[(["colors: Collection[Color]", "interval: Number"], "None")],
),
pytest.param("pybricks.hubs", "TechnicHub", "imu.up", [([], "Side")]),
pytest.param("pybricks.hubs", "TechnicHub", "imu.tilt", [([], "Tuple[int, int]")]),
pytest.param(
"pybricks.hubs",
"TechnicHub",
"imu.acceleration",
[(["axis: Axis"], "float"), ([], "Matrix")],
),
pytest.param(
"pybricks.hubs",
"TechnicHub",
"imu.angular_velocity",
[(["axis: Axis"], "float"), ([], "Matrix")],
),
pytest.param("pybricks.hubs", "TechnicHub", "imu.heading", [([], "float")]),
pytest.param(
"pybricks.hubs",
"TechnicHub",
"imu.reset_heading",
[(["angle: Number"], "None")],
),
pytest.param("pybricks.hubs", "TechnicHub", "battery.voltage", [([], "int")]),
pytest.param("pybricks.hubs", "TechnicHub", "battery.current", [([], "int")]),
pytest.param(
"pybricks.hubs", "TechnicHub", "button.pressed", [([], "Collection[Button]")]
),
pytest.param(
"pybricks.hubs",
"TechnicHub",
"system.set_stop_button",
[(["button: Optional[Union[Button, Iterable[Button]]]"], "None")],
),
pytest.param("pybricks.hubs", "TechnicHub", "system.name", [([], "str")]),
pytest.param("pybricks.hubs", "TechnicHub", "system.shutdown", [([], "None")]),
pytest.param("pybricks.hubs", "TechnicHub", "system.reset_reason", [([], "int")]),
pytest.param("pybricks.hubs", "PrimeHub", "light.on", [(["color: Color"], "None")]),
pytest.param("pybricks.hubs", "PrimeHub", "light.off", [([], "None")]),
pytest.param(
"pybricks.hubs",
"PrimeHub",
"light.blink",
[(["color: Color", "durations: Collection[Number]"], "None")],
),
pytest.param(
"pybricks.hubs",
"PrimeHub",
"light.animate",
[(["colors: Collection[Color]", "interval: Number"], "None")],
),
pytest.param(
"pybricks.hubs", "PrimeHub", "display.orientation", [(["up: Side"], "None")]
),
pytest.param("pybricks.hubs", "PrimeHub", "display.off", [([], "None")]),
pytest.param(
"pybricks.hubs",
"PrimeHub",
"display.pixel",
[(["row: Number", "column: Number", "brightness: Number=100"], "None")],
),
pytest.param(
"pybricks.hubs", "PrimeHub", "display.image", [(["matrix: Matrix"], "None")]
),
pytest.param(
"pybricks.hubs",
"PrimeHub",
"display.animate",
[(["matrices: Collection[Matrix]", "interval: Number"], "None")],
),
pytest.param(
"pybricks.hubs", "PrimeHub", "display.number", [(["number: Number"], "None")]
),
pytest.param(
"pybricks.hubs", "PrimeHub", "display.char", [(["char: str"], "None")]
),
pytest.param(
"pybricks.hubs",
"PrimeHub",
"display.text",
[(["text: str", "on: Number=500", "off: Number=50"], "None")],
),
pytest.param(
"pybricks.hubs", "PrimeHub", "buttons.pressed", [([], "Collection[Button]")]
),
pytest.param("pybricks.hubs", "PrimeHub", "imu.up", [([], "Side")]),
pytest.param("pybricks.hubs", "PrimeHub", "imu.tilt", [([], "Tuple[int, int]")]),
pytest.param(
"pybricks.hubs",
"PrimeHub",
"imu.acceleration",
[(["axis: Axis"], "float"), ([], "Matrix")],
),
pytest.param(
"pybricks.hubs",
"PrimeHub",
"imu.angular_velocity",
[(["axis: Axis"], "float"), ([], "Matrix")],
),
pytest.param("pybricks.hubs", "PrimeHub", "imu.heading", [([], "float")]),
pytest.param(
"pybricks.hubs",
"PrimeHub",
"imu.reset_heading",
[(["angle: Number"], "None")],
),
pytest.param(
"pybricks.hubs",
"PrimeHub",
"speaker.volume",
[(["volume: Number"], "None"), ([], "int")],
),
pytest.param(
"pybricks.hubs",
"PrimeHub",
"speaker.beep",
[(["frequency: Number=500", "duration: Number=100"], "None")],
),
pytest.param(
"pybricks.hubs",
"PrimeHub",
"speaker.play_notes",
[(["notes: Iterable[str]", "tempo: Number=120"], "None")],
),
pytest.param("pybricks.hubs", "PrimeHub", "battery.voltage", [([], "int")]),
pytest.param("pybricks.hubs", "PrimeHub", "battery.current", [([], "int")]),
pytest.param("pybricks.hubs", "PrimeHub", "charger.connected", [([], "bool")]),
pytest.param("pybricks.hubs", "PrimeHub", "charger.current", [([], "int")]),
pytest.param("pybricks.hubs", "PrimeHub", "charger.status", [([], "int")]),
pytest.param(
"pybricks.hubs",
"PrimeHub",
"system.set_stop_button",
[(["button: Optional[Union[Button, Iterable[Button]]]"], "None")],
),
pytest.param("pybricks.hubs", "PrimeHub", "system.name", [([], "str")]),
pytest.param("pybricks.hubs", "PrimeHub", "system.shutdown", [([], "None")]),
pytest.param("pybricks.hubs", "PrimeHub", "system.reset_reason", [([], "int")]),
# TODO: iodevices module here
pytest.param("pybricks.pupdevices", "DCMotor", "dc", [(["duty: Number"], "None")]),
pytest.param("pybricks.pupdevices", "DCMotor", "stop", [([], "None")]),
pytest.param("pybricks.pupdevices", "DCMotor", "brake", [([], "None")]),
pytest.param(
"pybricks.pupdevices",
"DCMotor",
"settings",
[(["max_voltage: Number"], "None"), ([], "Tuple[int]")],
),
pytest.param("pybricks.pupdevices", "Motor", "speed", [([], "int")]),
pytest.param("pybricks.pupdevices", "Motor", "angle", [([], "int")]),
pytest.param(
"pybricks.pupdevices",
"Motor",
"reset_angle",
[(["angle: Optional[Number]=None"], "None")],
),
pytest.param("pybricks.pupdevices", "Motor", "stop", [([], "None")]),
pytest.param("pybricks.pupdevices", "Motor", "brake", [([], "None")]),
pytest.param("pybricks.pupdevices", "Motor", "hold", [([], "None")]),
pytest.param("pybricks.pupdevices", "Motor", "run", [(["speed: Number"], "None")]),
pytest.param(
"pybricks.pupdevices",
"Motor",
"run_time",
[
(
[
"speed: Number",
"time: Number",
"then: Stop=Stop.HOLD",
"wait: bool=True",
],
"None",
)
],
),
pytest.param(
"pybricks.pupdevices",
"Motor",
"run_angle",
[
(
[
"speed: Number",
"rotation_angle: Number",
"then: Stop=Stop.HOLD",
"wait: bool=True",
],
"None",
)
],
),
pytest.param(
"pybricks.pupdevices",
"Motor",
"run_target",
[
(
[
"speed: Number",
"target_angle: Number",
"then: Stop=Stop.HOLD",
"wait: bool=True",
],
"None",
)
],
),
pytest.param(
"pybricks.pupdevices",
"Motor",
"track_target",
[(["target_angle: Number"], "None")],
),
pytest.param(
"pybricks.pupdevices",
"Motor",
"run_until_stalled",
[
(
[
"speed: Number",
"then: Stop=Stop.COAST",
"duty_limit: Optional[Number]=None",
],
"int",
)
],
),
pytest.param("pybricks.pupdevices", "Motor", "dc", [(["duty: Number"], "None")]),
pytest.param("pybricks.pupdevices", "Motor", "control.done", [([], "bool")]),
pytest.param("pybricks.pupdevices", "Motor", "control.stalled", [([], "bool")]),
pytest.param("pybricks.pupdevices", "Motor", "control.load", [([], "int")]),
pytest.param(
"pybricks.pupdevices",
"Motor",
"settings",
[(["max_voltage: Number"], "None"), ([], "Tuple[int]")],
),
pytest.param(
"pybricks.pupdevices",
"Motor",
"control.limits",
[
(
[
"speed: Optional[Number]=None",
"acceleration: Optional[Number]=None",
"torque: Optional[Number]=None",
],
"None",
),
([], "Tuple[int, int, int]"),
],
),
pytest.param(
"pybricks.pupdevices",
"Motor",
"control.pid",
[
(
[
"kp: Optional[Number]=None",
"ki: Optional[Number]=None",
"kd: Optional[Number]=None",
"reserved: Optional[Number]=None",
"integral_rate: Optional[Number]=None",
],
"None",
),
([], "Tuple[int, int, int, None, int]"),
],
),
pytest.param(
"pybricks.pupdevices",
"Motor",
"control.target_tolerances",
[
(
[
"speed: Optional[Number]=None",
"position: Optional[Number]=None",
],
"None",
),
([], "Tuple[int, int]"),
],
),
pytest.param(
"pybricks.pupdevices",
"Motor",
"control.stall_tolerances",
[
(
[
"speed: Optional[Number]=None",
"time: Optional[Number]=None",
],
"None",
),
([], "Tuple[int, int]"),
],
),
pytest.param(
"pybricks.pupdevices", "TiltSensor", "tilt", [([], "Tuple[int, int]")]
),
pytest.param("pybricks.pupdevices", "InfraredSensor", "distance", [([], "int")]),
pytest.param("pybricks.pupdevices", "InfraredSensor", "reflection", [([], "int")]),
pytest.param("pybricks.pupdevices", "InfraredSensor", "count", [([], "int")]),
pytest.param(
"pybricks.pupdevices", "ColorDistanceSensor", "color", [([], "Color")]
),
pytest.param(
"pybricks.pupdevices", "ColorDistanceSensor", "reflection", [([], "int")]
),
pytest.param(
"pybricks.pupdevices", "ColorDistanceSensor", "ambient", [([], "int")]
),
pytest.param(
"pybricks.pupdevices", "ColorDistanceSensor", "distance", [([], "int")]
),
pytest.param("pybricks.pupdevices", "ColorDistanceSensor", "hsv", [([], "Color")]),
pytest.param(
"pybricks.pupdevices",
"ColorDistanceSensor",
"detectable_colors",
[(["colors: Collection[Color]"], "None"), ([], "Collection[Color]")],
),
pytest.param(
"pybricks.pupdevices",
"ColorDistanceSensor",
"light.on",
[(["color: Color"], "None")],
),
pytest.param(
"pybricks.pupdevices", "ColorDistanceSensor", "light.off", [([], "None")]
),
pytest.param("pybricks.pupdevices", "PFMotor", "dc", [(["duty: Number"], "None")]),
pytest.param("pybricks.pupdevices", "PFMotor", "stop", [([], "None")]),
pytest.param("pybricks.pupdevices", "PFMotor", "brake", [([], "None")]),
pytest.param(
"pybricks.pupdevices",
"ColorSensor",
"color",
[(["surface: bool=True"], "Optional[Color]")],
),
pytest.param("pybricks.pupdevices", "ColorSensor", "reflection", [([], "int")]),
pytest.param("pybricks.pupdevices", "ColorSensor", "ambient", [([], "int")]),
pytest.param(
"pybricks.pupdevices",
"ColorSensor",
"hsv",
[(["surface: bool=True"], "Color")],
),
pytest.param(
"pybricks.pupdevices",
"ColorSensor",
"detectable_colors",
[(["colors: Collection[Color]"], "None"), ([], "Collection[Color]")],
),
pytest.param(
"pybricks.pupdevices",
"ColorSensor",
"lights.on",
[(["brightness: Union[Number, Tuple[Number, Number, Number]]"], "None")],
),
pytest.param("pybricks.pupdevices", "ColorSensor", "lights.off", [([], "None")]),
pytest.param("pybricks.pupdevices", "UltrasonicSensor", "distance", [([], "int")]),
pytest.param("pybricks.pupdevices", "UltrasonicSensor", "presence", [([], "bool")]),
pytest.param(
"pybricks.pupdevices",
"UltrasonicSensor",
"lights.on",
[
(
["brightness: Union[Number, Tuple[Number, Number, Number, Number]]"],
"None",
)
],
),
pytest.param(
"pybricks.pupdevices", "UltrasonicSensor", "lights.off", [([], "None")]
),
pytest.param("pybricks.pupdevices", "ForceSensor", "force", [([], "float")]),
pytest.param("pybricks.pupdevices", "ForceSensor", "distance", [([], "float")]),
pytest.param(
"pybricks.pupdevices",
"ForceSensor",
"pressed",
[(["force: Number=3"], "bool")],
),
pytest.param("pybricks.pupdevices", "ForceSensor", "touched", [([], "bool")]),
pytest.param(
"pybricks.pupdevices",
"ColorLightMatrix",
"on",
[(["color: Union[Color, Collection[Color]]"], "None")],
),
pytest.param("pybricks.pupdevices", "ColorLightMatrix", "off", [([], "None")]),
pytest.param(
"pybricks.pupdevices", "Light", "on", [(["brightness: Number=100"], "None")]
),
pytest.param("pybricks.pupdevices", "Light", "off", [([], "None")]),
pytest.param(
"pybricks.pupdevices",
"Remote",
"name",
[(["name: str"], "None"), ([], "str")],
),
pytest.param(
"pybricks.pupdevices", "Remote", "light.on", [(["color: Color"], "None")]
),
pytest.param("pybricks.pupdevices", "Remote", "light.off", [([], "None")]),
pytest.param(
"pybricks.pupdevices",
"Remote",
"buttons.pressed",
[([], "Collection[Button]")],
),
pytest.param("pybricks.tools", "StopWatch", "time", [([], "int")]),
pytest.param("pybricks.tools", "StopWatch", "pause", [([], "None")]),
pytest.param("pybricks.tools", "StopWatch", "resume", [([], "None")]),
pytest.param("pybricks.tools", "StopWatch", "reset", [([], "None")]),
pytest.param(
"pybricks.robotics",
"DriveBase",
"straight",
[(["distance: Number", "then: Stop=Stop.HOLD", "wait: bool=True"], "None")],
),
pytest.param(
"pybricks.robotics",
"DriveBase",
"turn",
[(["angle: Number", "then: Stop=Stop.HOLD", "wait: bool=True"], "None")],
),
pytest.param(
"pybricks.robotics",
"DriveBase",
"curve",
[
(
[
"radius: Number",
"angle: Number",
"then: Stop=Stop.HOLD",
"wait: bool=True",
],
"None",
)
],
),
pytest.param(
"pybricks.robotics",
"DriveBase",
"settings",
[
(
[
"straight_speed: Optional[Number]=None",
"straight_acceleration: Optional[Number]=None",
"turn_rate: Optional[Number]=None",
"turn_acceleration: Optional[Number]=None",
],
"None",
),
([], "Tuple[int, int, int, int]"),
],
),
pytest.param(
"pybricks.robotics",
"DriveBase",
"drive",
[(["speed: Number", "turn_rate: Number"], "None")],
),
pytest.param("pybricks.robotics", "DriveBase", "stop", [([], "None")]),
pytest.param("pybricks.robotics", "DriveBase", "distance", [([], "int")]),
pytest.param("pybricks.robotics", "DriveBase", "angle", [([], "int")]),
pytest.param(
"pybricks.robotics", "DriveBase", "state", [([], "Tuple[int, int, int, int]")]
),
pytest.param("pybricks.robotics", "DriveBase", "reset", [([], "None")]),
]
@pytest.mark.parametrize("module,type,method,signatures", METHOD_PARAMS)
def test_get_signature_for_methods(
module: str, type: str, method: str, signatures: list[tuple[list[str], str]]
):
help = _get_method_signature(module, type, method)
# strip method now that code has been generated (may have leading subcomponent)
method = method.split(".")[-1]
assert help["activeSignature"] == 0
assert help["activeParameter"] == 0
assert help["signatures"]
for sig, (params, returns) in zip_longest(help["signatures"], signatures):
assert sig["label"] == f"{method}({', '.join(params)}) -> {returns}"
assert sig["documentation"]["value"]
# ensure signatures are stripped from doc comment
assert not sig["documentation"]["value"].startswith(f"{method}(")
for pi, p in zip_longest(sig["parameters"], params):
assert pi["label"] == p
assert pi["documentation"]["value"]
+46 -47
View File
@@ -107,7 +107,7 @@ class DCMotor:
is generated while the motor is still moving."""
@overload
def settings(self, max_voltage: Optional[int] = None) -> None:
def settings(self, max_voltage: Number) -> None:
...
@overload
@@ -140,16 +140,16 @@ class Control:
"""
@overload
def limits(self) -> Tuple[int, int, int]:
def limits(
self,
speed: Optional[Number] = None,
acceleration: Optional[Number] = None,
torque: Optional[Number] = None,
) -> None:
...
@overload
def limits(
self,
speed: Optional[int] = None,
acceleration: Optional[int] = None,
torque: Optional[int] = None,
) -> None:
def limits(self) -> Tuple[int, int, int]:
...
def limits(self, *args):
@@ -173,18 +173,18 @@ class Control:
"""
@overload
def pid(self) -> Tuple[int, int, int, None, int]:
def pid(
self,
kp: Optional[Number] = None,
ki: Optional[Number] = None,
kd: Optional[Number] = None,
reserved: Optional[Number] = None,
integral_rate: Optional[Number] = None,
) -> None:
...
@overload
def pid(
self,
kp: Optional[int] = None,
ki: Optional[int] = None,
kd: Optional[int] = None,
reserved: Optional[int] = None,
integral_rate: Optional[int] = None,
) -> None:
def pid(self) -> Tuple[int, int, int, None, int]:
...
def pid(self, *args):
@@ -210,13 +210,13 @@ class Control:
"""
@overload
def target_tolerances(self) -> Tuple[int, int]:
def target_tolerances(
self, speed: Optional[Number] = None, position: Optional[Number] = None
) -> None:
...
@overload
def target_tolerances(
self, speed: Optional[int] = None, position: Optional[int] = None
) -> None:
def target_tolerances(self) -> Tuple[int, int]:
...
def target_tolerances(self, *args):
@@ -236,13 +236,13 @@ class Control:
"""
@overload
def stall_tolerances(self) -> Tuple[int, int]:
def stall_tolerances(
self, speed: Optional[Number] = None, time: Optional[Number] = None
) -> None:
...
@overload
def stall_tolerances(
self, speed: Optional[int] = None, time: Optional[int] = None
) -> None:
def stall_tolerances(self) -> Tuple[int, int]:
...
def stall_tolerances(self, speed, time):
@@ -356,7 +356,7 @@ class Motor(DCMotor):
"""
def reset_angle(self, angle: Number) -> None:
def reset_angle(self, angle: Optional[Number]) -> None:
"""
reset_angle(angle)
@@ -485,11 +485,11 @@ class Speaker:
"""Plays beeps and sounds using a speaker."""
@overload
def volume(self) -> int:
def volume(self, volume: Number) -> None:
...
@overload
def volume(self, volume: Number) -> None:
def volume(self) -> int:
...
def volume(self, *args):
@@ -570,7 +570,7 @@ class ColorLight:
Turns off the light."""
def blink(self, color: Color, durations: Collection[int]) -> None:
def blink(self, color: Color, durations: Collection[Number]) -> None:
"""blink(color, durations)
Blinks the light at a given color by turning it on and off for given
@@ -599,7 +599,7 @@ class ColorLight:
keeps running. When the animation completes, it repeats.
Arguments:
colors (iter): Sequence of :class:`Color <.parameters.Color>`
colors (list): Sequence of :class:`Color <.parameters.Color>`
values.
interval (Number, ms): Time between color updates.
"""
@@ -617,7 +617,7 @@ class LightArray:
n (int): Number of lights
"""
def on(self, brightness: Union[int, Collection[int]]) -> None:
def on(self, brightness: Union[Number, Collection[Number]]) -> None:
"""on(brightness)
Turns on the lights at the specified brightness.
@@ -688,15 +688,15 @@ class LightMatrix:
interval (Number, ms): Time to display each image in the list.
"""
def pixel(self, row: int, column: int, brightness: Number = 100) -> None:
def pixel(self, row: Number, column: Number, brightness: Number = 100) -> None:
"""pixel(row, column, brightness=100)
Turns on one pixel at the specified brightness.
Arguments:
row (int): Vertical grid index, starting at 0 from the top.
column (int): Horizontal grid index, starting at 0 from the left.
brightness (:ref:`brightness`): Brightness of the pixel.
row (Number): Vertical grid index, starting at 0 from the top.
column (Number): Horizontal grid index, starting at 0 from the left.
brightness (Number :ref:`brightness`): Brightness of the pixel.
"""
def off(self) -> None:
@@ -749,13 +749,13 @@ class Keypad:
def __init__(self, active_buttons):
...
def pressed(self) -> Tuple[Button]:
"""pressed() -> Tuple[Button]
def pressed(self) -> Collection[Button]:
"""pressed() -> Collection[Button]
Checks which buttons are currently pressed.
Returns:
Tuple of pressed buttons.
Set of pressed buttons.
"""
@@ -846,11 +846,11 @@ class Accelerometer(SimpleAccelerometer):
"""Get measurements from an accelerometer."""
@overload
def acceleration(self) -> Matrix:
def acceleration(self, axis: Axis) -> float:
...
@overload
def acceleration(self, axis: Axis) -> float:
def acceleration(self) -> Matrix:
...
def acceleration(self, *args):
@@ -915,11 +915,11 @@ class IMU(Accelerometer):
"""
@overload
def angular_velocity(self) -> Matrix:
def angular_velocity(self, axis: Axis) -> float:
...
@overload
def angular_velocity(self, axis: Axis) -> float:
def angular_velocity(self) -> Matrix:
...
def angular_velocity(self, *args):
@@ -1003,13 +1003,13 @@ class CommonColorSensor:
...
@overload
def detectable_colors(self) -> Tuple[Color]:
def detectable_colors(self) -> Collection[Color]:
...
def detectable_colors(self, *args):
"""
detectable_colors(colors)
detectable_colors() -> Tuple[Color]
detectable_colors() -> Collection[Color]
Configures which colors the ``color()`` method should detect.
@@ -1017,11 +1017,10 @@ class CommonColorSensor:
This way, the full-color measurements are rounded to the nearest
desired color, and other colors are ignored. This improves reliability.
If you give no arguments, the currently chosen colors will be returned
as a tuple.
If you give no arguments, the currently chosen colors will be returned.
Arguments:
colors (tuple): Tuple of :class:`Color <.parameters.Color>`
colors (list or tuple): List of :class:`Color <.parameters.Color>`
objects: the colors that you want to detect. You can pick
standard colors such as ``Color.MAGENTA``, or provide your
own colors like ``Color(h=348, s=96, v=40)`` for even
+23 -4
View File
@@ -5,7 +5,7 @@
from __future__ import annotations
from typing import Tuple, Collection, overload
from typing import Sequence, Tuple, overload
class Matrix:
@@ -51,7 +51,7 @@ class Matrix:
def __ifloordiv__(self, other) -> Matrix:
...
def __init__(self, rows: Collection[Collection[int]]):
def __init__(self, rows: Sequence[Sequence[float]]):
"""Matrix(rows)
Arguments:
@@ -73,12 +73,31 @@ class Matrix:
@overload
def vector(x: float, y: float) -> Matrix:
...
"""
Convenience function to create a :class:`.Matrix` with the shape (``2``, ``1``).
Arguments:
x (float): x-coordinate of the vector.
y (float): y-coordinate of the vector.
Returns:
A matrix with the shape of a column vector.
"""
@overload
def vector(x: float, y: float, z: float) -> Matrix:
...
"""
Convenience function to create a :class:`.Matrix` with the shape (``3``, ``1``).
Arguments:
x (float): x-coordinate of the vector.
y (float): y-coordinate of the vector.
z (float): z-coordinate of the vector.
Returns:
A matrix with the shape of a column vector.
"""
def vector(*args):
+7 -3
View File
@@ -4,7 +4,7 @@
"""LEGO® Programmable Hubs."""
from . import _common
from .ev3dev import _speaker
from .geometry import Axis as _Axis
from .geometry import Axis
from .media.ev3dev import Image as _Image
from .parameters import Button as _Button
@@ -63,7 +63,7 @@ class TechnicHub:
system = _common.System()
button = _common.Keypad([_Button.CENTER])
def __init__(self, top_side: _Axis = _Axis.Z, front_side: _Axis = _Axis.X):
def __init__(self, top_side: Axis = Axis.Z, front_side: Axis = Axis.X):
"""TechnicHub(top_side=Axis.Z, front_side=Axis.X)
Initializes the hub. Optionally, specify how the hub is
@@ -100,7 +100,7 @@ class PrimeHub:
imu = _common.IMU()
system = _common.System()
def __init__(self, top_side: _Axis = _Axis.Z, front_side: _Axis = _Axis.X):
def __init__(self, top_side: Axis = Axis.Z, front_side: Axis = Axis.X):
"""PrimeHub(top_side=Axis.Z, front_side=Axis.X)
Initializes the hub. Optionally, specify how the hub is
@@ -118,3 +118,7 @@ class PrimeHub:
class InventorHub(PrimeHub):
"""LEGO® MINDSTORMS Inventor Hub."""
# HACK: hide from jedi
del Axis
+127 -28
View File
@@ -8,25 +8,63 @@ from __future__ import annotations
from typing import TYPE_CHECKING, Collection, Optional, Union, overload, Tuple
from . import _common
from .parameters import (
Button as _Button,
Color as _Color,
Direction as _Direction,
Port as _Port,
)
from .parameters import Button, Color, Direction
if TYPE_CHECKING:
from .parameters import Number as _Number
from .parameters import Number, Port
class DCMotor(_common.DCMotor):
"""LEGO® Powered Up motor without rotation sensors."""
# HACK: jedi can't find inherited __init__ so we have to duplicate docs
def __init__(self, port: Port, positive_direction: Direction = Direction.CLOCKWISE):
"""__init__(port, positive_direction=Direction.CLOCKWISE)
Arguments:
port (Port): Port to which the motor is connected.
positive_direction (Direction): Which direction the motor should
turn when you give a positive duty cycle value.
"""
class Motor(_common.Motor):
"""LEGO® Powered Up motor with rotation sensors."""
def reset_angle(self, angle: Optional[int]) -> None:
# HACK: jedi can't find inherited __init__ so we have to duplicate docs
def __init__(
self,
port: Port,
positive_direction: Direction = Direction.CLOCKWISE,
gears: Optional[Union[Collection[int], Collection[Collection[int]]]] = None,
reset_angle: bool = True,
):
"""__init__(port, positive_direction=Direction.CLOCKWISE, gears=None, reset_angle=True)
Arguments:
port (Port): Port to which the motor is connected.
positive_direction (Direction): Which direction the motor should
turn when you give a positive speed value or
angle.
gears (list):
List of gears linked to the motor.
For example: ``[12, 36]`` represents a gear train with a
12-tooth and a 36-tooth gear. Use a list of lists for multiple
gear trains, such as ``[[12, 36], [20, 16, 40]]``.
When you specify a gear train, all motor commands and settings
are automatically adjusted to account for the resulting gear
ratio. The motor direction remains unchanged by this.
reset_angle(bool):
Choose ``True`` to reset the rotation sensor value to the
absolute marker angle (between -180 and 179).
Choose ``False`` to keep the
current value, so your program knows where it left off last
time.
"""
def reset_angle(self, angle: Optional[Number] = None) -> None:
"""reset_angle(angle=None)
Sets the accumulated rotation angle of the motor to a desired value.
@@ -45,13 +83,13 @@ class Remote:
light = _common.ColorLight()
buttons = _common.Keypad(
(
_Button.LEFT_MINUS,
_Button.RIGHT_MINUS,
_Button.LEFT,
_Button.CENTER,
_Button.RIGHT,
_Button.LEFT_PLUS,
_Button.RIGHT_PLUS,
Button.LEFT_MINUS,
Button.RIGHT_MINUS,
Button.LEFT,
Button.CENTER,
Button.RIGHT,
Button.LEFT_PLUS,
Button.RIGHT_PLUS,
)
)
addresss: Union[str, None]
@@ -94,7 +132,7 @@ class Remote:
class TiltSensor:
"""LEGO® Powered Up Tilt Sensor."""
def __init__(self, port: _Port):
def __init__(self, port: Port):
"""TiltSensor(port)
Arguments:
@@ -116,6 +154,14 @@ class ColorDistanceSensor(_common.CommonColorSensor):
light = _common.ColorLight()
# HACK: jedi can't find inherited __init__ so docs have to be duplicated
def __init__(self, port: Port):
"""__init__(port)
Arguments:
port (Port): Port to which the sensor is connected.
"""
def distance(self) -> int:
"""distance() -> int: %
@@ -135,8 +181,8 @@ class PFMotor(DCMotor):
self,
sensor: ColorDistanceSensor,
channel: int,
color: _Color,
positive_direction: _Direction = _Direction.CLOCKWISE,
color: Color,
positive_direction: Direction = Direction.CLOCKWISE,
):
"""PFMotor(sensor, channel, color, positive_direction=Direction.CLOCKWISE)
@@ -157,15 +203,59 @@ class PFMotor(DCMotor):
class ColorSensor(_common.AmbientColorSensor):
"""LEGO® SPIKE Color Sensor."""
lights = _common.LightArray(3)
class _LightArray(_common.LightArray):
def __init__(self):
super().__init__(3)
def on(self, brightness: Union[Number, Tuple[Number, Number, Number]]) -> None:
"""on(brightness)
Turns on the lights at the specified brightness.
Arguments:
brightness (Number or tuple, %):
A single value will set the brightness of all three lights
to the same value. A tuple of 3 values will set the
brightness of each LED individually.
"""
return super().on(brightness)
lights = _LightArray()
# HACK: jedi can't find inherited __init__ so docs have to be duplicated
def __init__(self, port: Port):
"""__init__(port)
Arguments:
port (Port): Port to which the sensor is connected.
"""
class UltrasonicSensor:
"""LEGO® SPIKE Color Sensor."""
lights = _common.LightArray(3)
class _LightArray(_common.LightArray):
def __init__(self):
super().__init__(4)
def __init__(self, port: _Port):
def on(
self, brightness: Union[Number, Tuple[Number, Number, Number, Number]]
) -> None:
"""on(brightness)
Turns on the lights at the specified brightness.
Arguments:
brightness (Number or tuple, %):
A single value will set the brightness of all four lights
to the same value. A tuple of 4 values will set the
brightness of each LED individually.
"""
return super().on(brightness)
lights = _LightArray()
def __init__(self, port: Port):
"""UltrasonicSensor(port)
Arguments:
@@ -199,7 +289,7 @@ class UltrasonicSensor:
class ForceSensor:
"""LEGO® SPIKE Force Sensor."""
def __init__(self, port: _Port):
def __init__(self, port: Port):
"""ForceSensor(port)
Arguments:
@@ -224,7 +314,7 @@ class ForceSensor:
Movement up to approximately 8.00 mm.
"""
def pressed(self, force: _Number = 3) -> bool:
def pressed(self, force: Number = 3) -> bool:
"""pressed(force=3) -> bool
Checks if the sensor button is pressed.
@@ -255,7 +345,7 @@ class ColorLightMatrix:
LEGO® SPIKE 3x3 Color Light Matrix.
"""
def __init__(self, port: _Port):
def __init__(self, port: Port):
"""ColorLightMatrix(port)
Arguments:
@@ -264,7 +354,7 @@ class ColorLightMatrix:
"""
...
def on(self, color: Union[_Color, Collection[_Color]]) -> None:
def on(self, color: Union[Color, Collection[Color]]) -> None:
"""on(colors)
Turns the lights on.
@@ -288,7 +378,7 @@ class ColorLightMatrix:
class InfraredSensor:
"""LEGO® Powered Up Infrared Sensor."""
def __init__(self, port: _Port):
def __init__(self, port: Port):
"""InfraredSensor(port)
Arguments:
@@ -328,14 +418,14 @@ class InfraredSensor:
class Light:
"""LEGO® Powered Up Light."""
def __init__(self, port: _Port):
def __init__(self, port: Port):
"""Light(port)
Arguments:
port (Port): Port to which the device is connected.
"""
def on(self, brightness: _Number = 100) -> None:
def on(self, brightness: Number = 100) -> None:
"""on(brightness=100)
Turns on the light at the specified brightness.
@@ -349,3 +439,12 @@ class Light:
"""off()
Turns off the light."""
# HACK: exclude from jedi
if TYPE_CHECKING:
del Button
del Color
del Direction
del Number
del Port
+25 -15
View File
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: MIT
# Copyright (c) 2018-2021 The Pybricks Authors
# Copyright (c) 2018-2022 The Pybricks Authors
"""Robotics module for the Pybricks API."""
@@ -8,10 +8,11 @@ from __future__ import annotations
from typing import Tuple, Optional, overload, TYPE_CHECKING
from . import _common
from .parameters import Stop as _Stop
from .parameters import Stop
if TYPE_CHECKING:
from .parameters import Number as _Number
from ._common import Motor
from .parameters import Number
class DriveBase:
@@ -50,10 +51,10 @@ class DriveBase:
def __init__(
self,
left_motor: _common.Motor,
right_motor: _common.Motor,
wheel_diameter: _Number,
axle_track: _Number,
left_motor: Motor,
right_motor: Motor,
wheel_diameter: Number,
axle_track: Number,
):
"""DriveBase(left_motor, right_motor, wheel_diameter, axle_track)
@@ -67,7 +68,7 @@ class DriveBase:
both wheels touch the ground.
"""
def drive(self, speed: _Number, turn_rate: _Number) -> None:
def drive(self, speed: Number, turn_rate: Number) -> None:
"""drive(speed, turn_rate)
Starts driving at the specified speed and turn rate. Both values are
@@ -118,10 +119,10 @@ class DriveBase:
@overload
def settings(
self,
straight_speed: Optional[_Number],
straight_acceleration: Optional[_Number],
turn_rate: Optional[_Number],
turn_acceleration: Optional[_Number],
straight_speed: Optional[Number] = None,
straight_acceleration: Optional[Number] = None,
turn_rate: Optional[Number] = None,
turn_acceleration: Optional[Number] = None,
) -> None:
...
@@ -147,7 +148,9 @@ class DriveBase:
deceleration of the robot.
"""
def straight(self, distance: _Number, then=_Stop.HOLD, wait=True) -> None:
def straight(
self, distance: Number, then: Stop = Stop.HOLD, wait: bool = True
) -> None:
"""straight(distance, then=Stop.HOLD, wait=True)
Drives straight for a given distance and then stops.
@@ -159,7 +162,7 @@ class DriveBase:
with the rest of the program.
"""
def turn(self, angle: _Number, then=_Stop.HOLD, wait=True) -> None:
def turn(self, angle: Number, then: Stop = Stop.HOLD, wait: bool = True) -> None:
"""turn(angle, then=Stop.HOLD, wait=True)
Turns in place by a given angle and then stops.
@@ -172,7 +175,7 @@ class DriveBase:
"""
def curve(
self, radius: _Number, angle: _Number, then=_Stop.HOLD, wait=True
self, radius: Number, angle: Number, then: Stop = Stop.HOLD, wait: bool = True
) -> None:
"""curve(radius, angle, then=Stop.HOLD, wait=True)
@@ -185,3 +188,10 @@ class DriveBase:
wait (bool): Wait for the maneuver to complete before continuing
with the rest of the program.
"""
# HACK: hide from jedi
if TYPE_CHECKING:
del Motor
del Number
del Stop
+13 -3
View File
@@ -3,10 +3,15 @@
"""Common tools for timing and data logging."""
from typing import Any
from __future__ import annotations
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from .parameters import Number
def wait(time: int) -> None:
def wait(time: Number) -> None:
"""wait(time)
Pauses the user program for a specified amount of time.
@@ -63,7 +68,7 @@ class DataLog:
name: str = "log",
timestamp: bool = True,
extension: str = "csv",
append: bool = False
append: bool = False,
):
"""DataLog(*headers, name='log', timestamp=True, extension='csv', append=False)
@@ -90,3 +95,8 @@ class DataLog:
Arguments:
values (object, object, ...): One or more objects or values.
"""
# HACK: hide from jedi
if TYPE_CHECKING:
del Number