Compare commits

..
Author SHA1 Message Date
David Lechner 61d18ebe6f @pybricks/ide-docs v2.9.0 2023-05-16 15:33:45 -05:00
David Lechner ff0b767e3e v3.3.0a5 2023-05-16 15:27:22 -05:00
David Lechner 3274cef849 pybricks.common: Add new BLE class.
This adds a new BLE class that is used for connectionless broadcasting/
observing on hubs with built-in Bluetooth Low Energy.

Also see https://github.com/pybricks/pybricks-micropython/pull/158.
2023-05-16 14:46:53 -05:00
David Lechner b4ff3af36d pyproject: split lint and doc dependencies
This allows a subset of dependencies to be installed if needed and
allows sharing requirements with readthedocs.
2023-05-10 14:35:08 -05:00
David Lechner 8e5891d9e8 all: spelling
Misc spelling fixes.
2023-05-10 14:16:15 -05:00
David Lechner 7de67852f7 vscode: change to ms-python.black-formatter
Microsoft is breaking out various Python tools into separate extensions.
2023-05-10 13:43:49 -05:00
David Lechner 31d3a117d6 doc/main/conf: update copyright year 2023-04-21 17:39:28 -05:00
David Lechner 263fe3a1cb npm/jedi: v1.8.0 2023-04-21 17:31:17 -05:00
David Lechner db8de447d5 jedi: v1.8.0 2023-04-21 17:26:38 -05:00
David Lechner 46d6189eb1 jedi: update pybricks package version
also bump python requirement since Pyodide is now using Python 3.11.
2023-04-21 17:24:47 -05:00
David Lechner 2278c7bcda @pybricks/ide-docs v2.8.0 2023-04-21 17:10:51 -05:00
David Lechner 4da3618428 v3.3.0b4 2023-04-21 17:00:18 -05:00
Laurens Valk 5bc11b185d pybricks.tools: Add versionchanged note. 2023-04-21 14:07:46 +02:00
Laurens Valk be9f99d41b pybricks.tools: Add category headings. 2023-04-21 13:59:16 +02:00
Laurens Valk 870aae83c0 pybricks.tools: Document cross product. 2023-04-21 13:39:08 +02:00
Laurens Valk 9cde749e8a pybricks.geometry: Drop module.
See https://github.com/pybricks/pybricks-micropython/pull/160
2023-04-21 13:33:19 +02:00
Laurens Valk 8bca5eb6e4 pybricks.robotics: Add GyroDriveBase. 2023-04-21 10:42:30 +02:00
Laurens Valk 95a12a7386 pybricks.robotics: Drop note suggesting to flip motors.
This will still work, but it is asking for trouble when including the gyro.

So it is probably better not to mention this at all.
2023-04-21 10:42:30 +02:00
Laurens Valk d6c78f1f39 pybricks.robotics: Drop note restricting when to change settings. 2023-04-21 10:42:30 +02:00
Laurens Valk 98f2bb27fe pybricks.robotics.DriveBase: Drop positive direction.
This reverts commit e4650cb1c9.

This is not needed for gyro support, so it is better to remove it
before it is ever released.
2023-04-21 10:42:30 +02:00
Laurens Valk fdbd078388 pybricks._common.IMU: Heading is clockwise positive.
This is not a breaking change because heading() was never implemented until now.
2023-04-21 10:42:30 +02:00
Laurens Valk ccd2b46819 pybricks.robotics.DriveBase: Revert gyro use via init.
This reverts commit dadaab6f61.

We will introduce a separate class instead.
2023-04-21 10:42:30 +02:00
Laurens Valk c0cb05dd74 pybricks.robotics.DriveBase: Document use of gyro. 2023-04-21 10:42:30 +02:00
Laurens Valk b183e1420a pybricks.pupdevices.Motor: Document speed time window. 2023-04-21 10:42:30 +02:00
Laurens Valk b311c54261 pybricks._common.IMU: Change settings setter. 2023-04-21 10:42:30 +02:00
Laurens Valk bb37b94f5e pybricks._common.IMU: Add imu status. 2023-04-21 10:42:30 +02:00
Laurens Valk 5b25606afa pybricks._common.IMU: Add rotation and orientation.
Also update implementation status for heading and reset_heading.
2023-04-21 10:42:30 +02:00
Laurens Valk 8172d11ef6 urandom: Fix formula in getrandbits. 2023-03-28 08:50:00 +02:00
Laurens Valk c415296b71 pybricks._common.Motor: Document profile.
Fixes https://github.com/pybricks/support/issues/966
2023-03-20 15:12:36 +01:00
Laurens Valk e4650cb1c9 pybricks.robotics.DriveBase: Document positive direction.
Fixes https://github.com/pybricks/support/issues/992
2023-03-20 14:22:25 +01:00
Laurens Valk 91154a394c pybricks.robotics.DriveBase: Clarify settings method.
Fixes https://github.com/pybricks/support/issues/882
2023-03-20 14:00:42 +01:00
David Lechner 3c36010c04 pybricks.ev3devices: Fix Gyro direction parameter name.
The actual implemented name is `direction`, not `positive_direction`.

Fixes: https://github.com/pybricks/support/issues/509
2023-03-20 13:56:16 +01:00
Laurens Valk a553df3b03 pybricks.iodevices.PUPDevice: Add device class as comments. 2023-02-23 16:31:38 +01:00
Hans Willemen dbd1cc8dc9 pybricks.iodevices.PUPDevice: Add more devices.
Added:
- non-uart DCMotors  (Medium and Train)
- non-uart Light
- SPIKE 3x3 Color Matrix
- SPIKE Small Angular Motor
2023-02-23 16:31:38 +01:00
Laurens Valk b40c3989aa pybricks.common.Motor: Add Model() class instance.
This is mainly used to debug the motor model, and to quickly select the
feedback parameters. This is useful when adding or configuring new motor
types.

Reading the estimated speed can also be useful in advanced applications
where the user builds their own PID controller.

We may choose to hide this method from the documentation before the
release.
2023-02-09 10:25:15 +01:00
Laurens Valk 13612bdb1a README: Fix D002 Trailing whitespace. 2023-02-09 10:25:10 +01:00
kai-morich 031e905e12 README: document stub usage in vs code
Fixes: pybricks/support#937
2023-02-07 15:37:15 -06:00
Laurens Valk 428bca79ab pybricks.common.Control: Document deadzone. 2023-02-02 14:45:34 +01:00
Laurens Valk 0e8b6cfd97 doc/main/cad/devices: Generate motor model. 2023-02-02 14:44:50 +01:00
David Lechner eea8ff0924 examples/ev3/bluetooth: remove 3rd party dependency
Since Python 3.10, RFCOMM sockets are available in Python on Windows
so we no longer need 3rd party code.

Fixes: pybricks/support#902
2023-01-06 14:17:29 -06:00
David Lechner 8c673ab280 github: update actions/checkout to v3
This fixes deprecation warnings about node v12.
2022-12-28 16:16:01 -06:00
David Lechner 73477169ae npm/jedi: v1.7.0
Update pybricks_jedi package to v1.7.0 and bump version for release.
2022-12-28 16:13:28 -06:00
David Lechner 5ccc60c177 jedi/pyproject: v1.7.0 2022-12-28 16:09:37 -06:00
David Lechner 93c4fcc884 jedi: add update_user_modules() function
This function will be used to fix import completion of user module
names in Pybricks Code.

Issue: https://github.com/pybricks/support/issues/759
2022-12-28 16:06:47 -06:00
David Lechner aa72605e99 jedi: fix code completion for local _*
This fixes code completion for identifier names starting with "_" in
the local ("__main__") file.
2022-12-28 15:33:33 -06:00
David Lechner 8ee725722f jedi: fix completion of builtin types
Completions for `x.` where `x` is an instance of a builtin type was
broken because of filtering on the `builtins` modules. This extends
the filtering to allow attributes available on common builtin types
in Pybricks MicroPython.
2022-12-28 15:33:33 -06:00
David Lechner 236bda5f31 ubuiltins.round: Fix example.
The example did not have proper line breaks between statements.

Also add an additional example using f-strings.
2022-12-28 14:09:18 -06:00
69 changed files with 3534 additions and 1305 deletions
+3 -11
View File
@@ -6,7 +6,7 @@ name: Build Python package and docs
on: on:
push: push:
tags-ignore: tags-ignore:
- "*" - '**'
pull_request: pull_request:
paths: paths:
- doc/** - doc/**
@@ -20,19 +20,11 @@ on:
jobs: jobs:
build: build:
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
strategy:
matrix:
python-version: [3.8, 3.9]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
with: with:
submodules: recursive submodules: recursive
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies - name: Install dependencies
run: | run: |
pip install poetry pip install poetry
+4 -4
View File
@@ -7,17 +7,17 @@ on:
jobs: jobs:
publish_ide_docs: publish_ide_docs:
runs-on: ubuntu-20.04 runs-on: ubuntu-22.04
steps: steps:
- name: Ubuntu packages - name: Ubuntu packages
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y dvisvgm preview-latex-style texlive texlive-fonts-extra texlive-latex-extra sudo apt-get install -y dvisvgm preview-latex-style texlive texlive-fonts-extra texlive-latex-extra
- uses: actions/checkout@v2 - uses: actions/checkout@v3
with: with:
submodules: recursive submodules: recursive
- name: Set up Python 3.8 - name: Set up Python 3.8
uses: actions/setup-python@v1 uses: actions/setup-python@v4
with: with:
python-version: 3.8 python-version: 3.8
- name: Install dependencies - name: Install dependencies
@@ -26,7 +26,7 @@ jobs:
poetry run python -m pip install --upgrade pip poetry run python -m pip install --upgrade pip
poetry run python -m pip install --upgrade setuptools poetry run python -m pip install --upgrade setuptools
poetry install poetry install
- uses: actions/setup-node@v1 - uses: actions/setup-node@v3
with: with:
node-version: '14.x' node-version: '14.x'
registry-url: 'https://registry.npmjs.org' registry-url: 'https://registry.npmjs.org'
+1 -1
View File
@@ -9,7 +9,7 @@ jobs:
publish_jedi: publish_jedi:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
# Setup .npmrc file to publish to npm # Setup .npmrc file to publish to npm
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
with: with:
+26
View File
@@ -0,0 +1,26 @@
# .readthedocs.yaml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
build:
os: "ubuntu-22.04"
tools:
python: "3.10"
jobs:
post_create_environment:
- pip install poetry
- poetry config virtualenvs.create false
post_install:
- poetry install --only=doc
# Build documentation in the doc/main/ directory with Sphinx
sphinx:
configuration: doc/main/conf.py
fail_on_warning: true
# Optionally build your docs in additional formats such as PDF
formats:
- pdf
+2 -1
View File
@@ -4,7 +4,8 @@
// List of extensions which should be recommended for users of this workspace. // List of extensions which should be recommended for users of this workspace.
"recommendations": [ "recommendations": [
"ms-python.python" "ms-python.python",
"ms-python.black-formatter"
], ],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace. // List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": [ "unwantedRecommendations": [
+2 -2
View File
@@ -4,14 +4,14 @@
}, },
"python.defaultInterpreterPath": ".venv/bin/python", "python.defaultInterpreterPath": ".venv/bin/python",
"python.autoComplete.extraPaths": ["jedi/src"], "python.autoComplete.extraPaths": ["jedi/src"],
"python.formatting.provider": "black", "python.formatting.provider": "none",
"python.linting.pylintEnabled": false, "python.linting.pylintEnabled": false,
"python.linting.flake8Enabled": true, "python.linting.flake8Enabled": true,
"python.linting.pycodestyleEnabled": false, "python.linting.pycodestyleEnabled": false,
"python.linting.enabled": true, "python.linting.enabled": true,
"[python]": { "[python]": {
"editor.formatOnSave": true, "editor.formatOnSave": true,
"editor.defaultFormatter": "ms-python.python" "editor.defaultFormatter": "ms-python.black-formatter"
}, },
"python.languageServer": "Pylance", "python.languageServer": "Pylance",
"python.testing.pytestArgs": [ "python.testing.pytestArgs": [
+24
View File
@@ -4,6 +4,30 @@
## Unreleased ## Unreleased
## 3.3.0b5 - 2023-05-16
### Added
- Documented new `hub.ble` methods.
## 3.3.0b4 - 2023-04-21
### Added
- Documented `integral_deadzone` in `Control.pid()`.
- Documented `Motor.model`. This can be used to view the estimated motor
state and change its settings.
- Added `rotation`, `orientation`, `ready`, `stationary` and `settings` methods
to `IMU` class.
- Added `GyroDriveBase` class to `pybricks.robotics`.
### Changed
- Change implementation status of `IMU.heading` and `IMU.reset_heading`. They
are now implemented, with some limitations as noted in a note box.
- Moved `Matrix` and `vector` from `pybricks.geometry` to `pybricks.tools`.
- Moved `Axis` from `pybricks.geometry` to `pybricks.parameters`.
### Removed
- Removed `pybricks.geometry` module.
## 3.2.0 - 2022-12-20 ## 3.2.0 - 2022-12-20
### Changed ### Changed
+5
View File
@@ -12,6 +12,11 @@ used to generate the `official documentation`_.
See the `contributor's guide <CONTRIBUTING.md>`_ for acceptable changes and See the `contributor's guide <CONTRIBUTING.md>`_ for acceptable changes and
instructions to build the documentation locally. instructions to build the documentation locally.
You can use the API stubs in this repository for syntax highlighting and code
completion when programming the EV3 with VS Code. To enable, remove the
``"python.languageServer"="None"`` line in the ``.vscode/settings.json`` file
generated by the *LEGO® MINDSTORMS® EV3 MicroPython* extension.
For general discussion, please visit the `support`_ issue tracker. For general discussion, please visit the `support`_ issue tracker.
.. _Pybricks package: pybricks .. _Pybricks package: pybricks
+2 -1
View File
@@ -321,10 +321,11 @@ def on_missing_reference(
app.builder, node["refdoc"], "signaltypes", "numbers", contnode app.builder, node["refdoc"], "signaltypes", "numbers", contnode
) )
# References with special characters can't exist, so we have to supress # References with special characters can't exist, so we have to suppress
# warnings when Sphinx tries to cross reference units like deg/s. For # warnings when Sphinx tries to cross reference units like deg/s. For
# consistency, we also treat units without special characters this way. # consistency, we also treat units without special characters this way.
for unit in [ for unit in [
"dBm",
"deg", "deg",
"deg/s", "deg/s",
"deg/s²", "deg/s²",
+4 -5
View File
@@ -10,7 +10,6 @@ FEATURES_SMALL = set()
# Medium feature set. # Medium feature set.
FEATURES_MEDIUM = FEATURES_SMALL | { FEATURES_MEDIUM = FEATURES_SMALL | {
"pybricks-geometry",
"pybricks-common-control", "pybricks-common-control",
"pybricks-iodevices", "pybricks-iodevices",
"stm32-extra", "stm32-extra",
@@ -24,10 +23,10 @@ FEATURES_LARGE = FEATURES_MEDIUM | set()
HUB_FEATURES = { HUB_FEATURES = {
"movehub": {"movehub"} | FEATURES_SMALL, "movehub": {"movehub"} | FEATURES_SMALL,
"cityhub": {"cityhub"} | FEATURES_MEDIUM, "cityhub": {"cityhub"} | FEATURES_MEDIUM,
"technichub": {"technichub"} | FEATURES_MEDIUM, "technichub": {"technichub", "gyro"} | FEATURES_MEDIUM,
"primehub": {"primehub", "inventorhub", "light-matrix"} | FEATURES_LARGE, "primehub": {"primehub", "inventorhub", "light-matrix", "gyro"} | FEATURES_LARGE,
"inventorhub": {"primehub", "inventorhub", "light-matrix"} | FEATURES_LARGE, "inventorhub": {"primehub", "inventorhub", "light-matrix", "gyro"} | FEATURES_LARGE,
"essentialhub": {"essentialhub"} | FEATURES_LARGE, "essentialhub": {"essentialhub", "gyro"} | FEATURES_LARGE,
} }
+1169 -6
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -8,7 +8,7 @@ import sys
# General information about the project. # General information about the project.
project = "pybricks" project = "pybricks"
copyright = "2018-2021 The Pybricks Authors" copyright = "2018-2023 The Pybricks Authors"
author = "" author = ""
_TITLE = "Pybricks Modules and Examples" _TITLE = "Pybricks Modules and Examples"
@@ -29,7 +29,7 @@ if os.environ.get("READTHEDOCS", None) == "True":
# HACK: this allows Number type alias to be imported by Sphinx # HACK: this allows Number type alias to be imported by Sphinx
os.environ["SPHINX_BUILD"] = "True" os.environ["SPHINX_BUILD"] = "True"
# Addtional configuration of the IDE docs # Additional configuration of the IDE docs
if "ide" in tags.tags: # noqa F821 if "ide" in tags.tags: # noqa F821
_DISCLAIMER = "" _DISCLAIMER = ""
html_show_copyright = False html_show_copyright = False
@@ -45,7 +45,7 @@ if "ide" in tags.tags: # noqa F821
exec(open(os.path.abspath("../common/conf.py")).read()) exec(open(os.path.abspath("../common/conf.py")).read())
# Addtional configuration of the IDE docs # Additional configuration of the IDE docs
if "ide" in tags.tags: # noqa F821 if "ide" in tags.tags: # noqa F821
extensions.remove("sphinx.ext.mathjax") # noqa F821 extensions.remove("sphinx.ext.mathjax") # noqa F821
+2 -2
View File
@@ -20,12 +20,12 @@ Motors
.. rubric:: Measuring .. rubric:: Measuring
.. automethod:: pybricks.ev3devices.Motor.speed
.. automethod:: pybricks.ev3devices.Motor.angle .. automethod:: pybricks.ev3devices.Motor.angle
.. automethod:: pybricks.ev3devices.Motor.reset_angle .. automethod:: pybricks.ev3devices.Motor.reset_angle
.. automethod:: pybricks.ev3devices.Motor.speed
.. automethod:: pybricks.ev3devices.Motor.load .. automethod:: pybricks.ev3devices.Motor.load
.. automethod:: pybricks.ev3devices.Motor.stalled .. automethod:: pybricks.ev3devices.Motor.stalled
-51
View File
@@ -1,51 +0,0 @@
.. pybricks-requirements:: stm32-float
:mod:`geometry <pybricks.geometry>` -- Geometry and algebra
============================================================
.. module:: pybricks.geometry
.. autoclass:: pybricks.geometry.Matrix
:no-members:
.. autoattribute:: pybricks.geometry::Matrix.T
.. autoattribute:: pybricks.geometry::Matrix.shape
.. autofunction:: pybricks.geometry.vector
.. autoclass:: pybricks.geometry.Axis
:no-members:
.. _robotframe:
Reference frames
-----------------------
The Pybricks module and this documentation use the following conventions:
- X: Positive means forward. Negative means backward.
- Y: Positive means to the left. Negative means to the right.
- Z: Positive means upward. Negative means downward.
To make sure that all hub measurements (such as acceleration) have the correct
value and sign, you can specify how the hub is mounted in your creation. This
adjust the measurements so that it is easy to see how your *robot* is moving,
rather than how the *hub* is moving.
For example, the hub may be mounted upside down in your design. If you
configure the settings as shown in :numref:`fig_imuexamples`, the hub
measurements will be adjusted accordingly. This way, a positive acceleration
value in the X direction means that your *robot* accelerates forward, even
though the *hub* accelerates backward.
.. _fig_imuexamples:
.. figure:: ../main/diagrams/imuexamples.png
:width: 100 %
How to configure the ``top_side`` and ``front_side`` settings for three
different robot designs. The same technique can be applied to other hubs
and other creations, by noting which way the top and
front :class:`Side <Side>` of the hub are pointing. The example
on the left is the default configuration.
+22
View File
@@ -19,6 +19,17 @@ City Hub
.. automethod:: pybricks.hubs::CityHub.light.animate .. automethod:: pybricks.hubs::CityHub.light.animate
.. rubric:: Using connectionless Bluetooth messaging
``ble.broadcast()`` does not work on ``CityHub`` due to a bug in the
Bluetooth chip firmware.
.. automethod:: pybricks.hubs::CityHub.ble.observe
.. automethod:: pybricks.hubs::CityHub.ble.signal_strength
.. automethod:: pybricks.hubs::CityHub.ble.version
.. rubric:: Using the battery .. rubric:: Using the battery
.. automethod:: pybricks.hubs::CityHub.battery.voltage .. automethod:: pybricks.hubs::CityHub.battery.voltage
@@ -70,6 +81,17 @@ Creating light animations
.. literalinclude:: .. literalinclude::
../../../examples/pup/hub_common/build/light_animate_cityhub.py ../../../examples/pup/hub_common/build/light_animate_cityhub.py
Bluetooth examples
------------------
Observing data from other hubs
******************************
.. literalinclude::
../../../examples/pup/hub_common/build/ble_observe_cityhub.py
Button and system examples Button and system examples
---------------------------------- ----------------------------------
+37
View File
@@ -25,6 +25,10 @@ Essential Hub
.. rubric:: Using the IMU .. rubric:: Using the IMU
.. automethod:: pybricks.hubs::EssentialHub.imu.ready
.. automethod:: pybricks.hubs::EssentialHub.imu.stationary
.. automethod:: pybricks.hubs::EssentialHub.imu.up .. automethod:: pybricks.hubs::EssentialHub.imu.up
.. automethod:: pybricks.hubs::EssentialHub.imu.tilt .. automethod:: pybricks.hubs::EssentialHub.imu.tilt
@@ -37,6 +41,22 @@ Essential Hub
.. automethod:: pybricks.hubs::EssentialHub.imu.reset_heading .. automethod:: pybricks.hubs::EssentialHub.imu.reset_heading
.. automethod:: pybricks.hubs::EssentialHub.imu.rotation
.. automethod:: pybricks.hubs::EssentialHub.imu.orientation
.. automethod:: pybricks.hubs::EssentialHub.imu.settings
.. rubric:: Using connectionless Bluetooth messaging
.. automethod:: pybricks.hubs::EssentialHub.ble.broadcast
.. automethod:: pybricks.hubs::EssentialHub.ble.observe
.. automethod:: pybricks.hubs::EssentialHub.ble.signal_strength
.. automethod:: pybricks.hubs::EssentialHub.ble.version
.. rubric:: Using the battery .. rubric:: Using the battery
.. automethod:: pybricks.hubs::EssentialHub.battery.voltage .. automethod:: pybricks.hubs::EssentialHub.battery.voltage
@@ -126,6 +146,23 @@ Reading acceleration and angular velocity on one axis
.. literalinclude:: .. literalinclude::
../../../examples/pup/hub_common/build/imu_read_scalar_essentialhub.py ../../../examples/pup/hub_common/build/imu_read_scalar_essentialhub.py
Bluetooth examples
------------------
Broadcasting data to other hubs
*******************************
.. literalinclude::
../../../examples/pup/hub_common/build/ble_broadcast_essentialhub.py
Observing data from other hubs
******************************
.. literalinclude::
../../../examples/pup/hub_common/build/ble_observe_essentialhub.py
System examples System examples
---------------------------------- ----------------------------------
+27
View File
@@ -31,6 +31,16 @@ Move Hub
Changed acceleration units from m/s² to mm/s². Changed acceleration units from m/s² to mm/s².
.. rubric:: Using connectionless Bluetooth messaging
.. automethod:: pybricks.hubs::MoveHub.ble.broadcast
.. automethod:: pybricks.hubs::MoveHub.ble.observe
.. automethod:: pybricks.hubs::MoveHub.ble.signal_strength
.. automethod:: pybricks.hubs::MoveHub.ble.version
.. rubric:: Using the battery .. rubric:: Using the battery
.. automethod:: pybricks.hubs::MoveHub.battery.voltage .. automethod:: pybricks.hubs::MoveHub.battery.voltage
@@ -85,6 +95,23 @@ Reading acceleration
.. literalinclude:: .. literalinclude::
../../../examples/pup/hub_movehub/imu_read_acceleration.py ../../../examples/pup/hub_movehub/imu_read_acceleration.py
Bluetooth examples
------------------
Broadcasting data to other hubs
*******************************
.. literalinclude::
../../../examples/pup/hub_common/build/ble_broadcast_movehub.py
Observing data from other hubs
******************************
.. literalinclude::
../../../examples/pup/hub_common/build/ble_observe_movehub.py
Button and system examples Button and system examples
---------------------------------- ----------------------------------
+37
View File
@@ -59,6 +59,10 @@ Prime Hub / Inventor Hub
.. rubric:: Using the IMU .. rubric:: Using the IMU
.. automethod:: pybricks.hubs::PrimeHub.imu.ready
.. automethod:: pybricks.hubs::PrimeHub.imu.stationary
.. automethod:: pybricks.hubs::PrimeHub.imu.up .. automethod:: pybricks.hubs::PrimeHub.imu.up
.. automethod:: pybricks.hubs::PrimeHub.imu.tilt .. automethod:: pybricks.hubs::PrimeHub.imu.tilt
@@ -71,6 +75,12 @@ Prime Hub / Inventor Hub
.. automethod:: pybricks.hubs::PrimeHub.imu.reset_heading .. automethod:: pybricks.hubs::PrimeHub.imu.reset_heading
.. automethod:: pybricks.hubs::PrimeHub.imu.rotation
.. automethod:: pybricks.hubs::PrimeHub.imu.orientation
.. automethod:: pybricks.hubs::PrimeHub.imu.settings
.. rubric:: Using the speaker .. rubric:: Using the speaker
.. automethod:: pybricks.hubs::PrimeHub.speaker.volume .. automethod:: pybricks.hubs::PrimeHub.speaker.volume
@@ -79,6 +89,16 @@ Prime Hub / Inventor Hub
.. automethod:: pybricks.hubs::PrimeHub.speaker.play_notes .. automethod:: pybricks.hubs::PrimeHub.speaker.play_notes
.. rubric:: Using connectionless Bluetooth messaging
.. automethod:: pybricks.hubs::PrimeHub.ble.broadcast
.. automethod:: pybricks.hubs::PrimeHub.ble.observe
.. automethod:: pybricks.hubs::PrimeHub.ble.signal_strength
.. automethod:: pybricks.hubs::PrimeHub.ble.version
.. rubric:: Using the battery .. rubric:: Using the battery
.. automethod:: pybricks.hubs::PrimeHub.battery.voltage .. automethod:: pybricks.hubs::PrimeHub.battery.voltage
@@ -237,6 +257,23 @@ Reading acceleration and angular velocity on one axis
.. literalinclude:: .. literalinclude::
../../../examples/pup/hub_common/build/imu_read_scalar_primehub.py ../../../examples/pup/hub_common/build/imu_read_scalar_primehub.py
Bluetooth examples
------------------
Broadcasting data to other hubs
*******************************
.. literalinclude::
../../../examples/pup/hub_common/build/ble_broadcast_primehub.py
Observing data from other hubs
******************************
.. literalinclude::
../../../examples/pup/hub_common/build/ble_observe_primehub.py
System examples System examples
---------------------------------- ----------------------------------
+37
View File
@@ -21,6 +21,10 @@ Technic Hub
.. rubric:: Using the IMU .. rubric:: Using the IMU
.. automethod:: pybricks.hubs::TechnicHub.imu.ready
.. automethod:: pybricks.hubs::TechnicHub.imu.stationary
.. automethod:: pybricks.hubs::TechnicHub.imu.up .. automethod:: pybricks.hubs::TechnicHub.imu.up
.. automethod:: pybricks.hubs::TechnicHub.imu.tilt .. automethod:: pybricks.hubs::TechnicHub.imu.tilt
@@ -33,6 +37,22 @@ Technic Hub
.. automethod:: pybricks.hubs::TechnicHub.imu.reset_heading .. automethod:: pybricks.hubs::TechnicHub.imu.reset_heading
.. automethod:: pybricks.hubs::TechnicHub.imu.rotation
.. automethod:: pybricks.hubs::TechnicHub.imu.orientation
.. automethod:: pybricks.hubs::TechnicHub.imu.settings
.. rubric:: Using connectionless Bluetooth messaging
.. automethod:: pybricks.hubs::TechnicHub.ble.broadcast
.. automethod:: pybricks.hubs::TechnicHub.ble.observe
.. automethod:: pybricks.hubs::TechnicHub.ble.signal_strength
.. automethod:: pybricks.hubs::TechnicHub.ble.version
.. rubric:: Using the battery .. rubric:: Using the battery
.. automethod:: pybricks.hubs::TechnicHub.battery.voltage .. automethod:: pybricks.hubs::TechnicHub.battery.voltage
@@ -118,6 +138,23 @@ Reading acceleration and angular velocity on one axis
.. literalinclude:: .. literalinclude::
../../../examples/pup/hub_common/build/imu_read_scalar_technichub.py ../../../examples/pup/hub_common/build/imu_read_scalar_technichub.py
Bluetooth examples
------------------
Broadcasting data to other hubs
*******************************
.. literalinclude::
../../../examples/pup/hub_common/build/ble_broadcast_technichub.py
Observing data from other hubs
******************************
.. literalinclude::
../../../examples/pup/hub_common/build/ble_observe_technichub.py
Button and system examples Button and system examples
---------------------------------- ----------------------------------
-1
View File
@@ -60,7 +60,6 @@ above to reveal this menu.
parameters/index parameters/index
tools/index tools/index
robotics robotics
geometry
signaltypes signaltypes
.. toctree:: .. toctree::
+1 -1
View File
@@ -38,7 +38,7 @@ Powers and logarithms
.. autofunction:: umath.sqrt .. autofunction:: umath.sqrt
Trigonomety Trigonometry
------------------------------- -------------------------------
.. autodata:: umath.pi .. autodata:: umath.pi
+7
View File
@@ -0,0 +1,7 @@
.. pybricks-requirements:: stm32-float
Axis
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. autoclass:: pybricks.parameters.Axis
:no-members:
+3
View File
@@ -10,6 +10,7 @@
:maxdepth: 1 :maxdepth: 1
:hidden: :hidden:
axis
button button
color color
direction direction
@@ -18,6 +19,8 @@
side side
stop stop
.. pybricks-classlink:: Axis
.. pybricks-classlink:: Button .. pybricks-classlink:: Button
.. pybricks-classlink:: Color .. pybricks-classlink:: Color
+10 -2
View File
@@ -18,12 +18,12 @@ Motors with rotation sensors
.. rubric:: Measuring .. rubric:: Measuring
.. automethod:: pybricks.pupdevices.Motor.speed
.. automethod:: pybricks.pupdevices.Motor.angle .. automethod:: pybricks.pupdevices.Motor.angle
.. automethod:: pybricks.pupdevices.Motor.reset_angle .. automethod:: pybricks.pupdevices.Motor.reset_angle
.. automethod:: pybricks.pupdevices.Motor.speed
.. automethod:: pybricks.pupdevices.Motor.load .. automethod:: pybricks.pupdevices.Motor.load
.. automethod:: pybricks.pupdevices.Motor.stalled .. automethod:: pybricks.pupdevices.Motor.stalled
@@ -93,6 +93,14 @@ Motors with rotation sensors
The :meth:`done`, :meth:`stalled` and :meth:`load` methods have been The :meth:`done`, :meth:`stalled` and :meth:`load` methods have been
moved. moved.
.. pybricks-requirements:: pybricks-common-control
.. automethod:: pybricks.pupdevices.Motor.model.state
.. pybricks-requirements:: pybricks-common-control
.. automethod:: pybricks.pupdevices.Motor.model.settings
Initialization examples Initialization examples
----------------------- -----------------------
+37 -5
View File
@@ -1,11 +1,10 @@
.. pybricks-requirements::
:mod:`robotics <pybricks.robotics>` -- Robotics and drive bases :mod:`robotics <pybricks.robotics>` -- Robotics and drive bases
=============================================================== ===============================================================
.. automodule:: pybricks.robotics .. automodule:: pybricks.robotics
:no-members: :no-members:
.. pybricks-requirements::
.. autoclass:: pybricks.robotics.DriveBase .. autoclass:: pybricks.robotics.DriveBase
:no-members: :no-members:
@@ -103,9 +102,6 @@
the default speed and acceleration for straight maneuvers and turns. the default speed and acceleration for straight maneuvers and turns.
Use the following attributes to adjust more advanced control settings. Use the following attributes to adjust more advanced control settings.
You can only change the settings while the robot is stopped. This is
either before you begin driving or after you call :meth:`.stop`.
.. autoattribute:: pybricks.robotics.DriveBase.distance_control .. autoattribute:: pybricks.robotics.DriveBase.distance_control
:annotation: :annotation:
@@ -116,11 +112,47 @@
The :meth:`done` and :meth:`stalled` methods have been moved. The :meth:`done` and :meth:`stalled` methods have been moved.
.. pybricks-requirements:: gyro
.. class:: GyroDriveBase
This class works just like the :class:`DriveBase`, but it uses the hub's
built-in gyroscope to drive straight and turn more accurately.
If your hub is not mounted flat in your robot, make sure to specify
the ``top_side`` and ``front_side`` parameters when you initialize the
:class:`PrimeHub() <pybricks.hubs.PrimeHub>`,
:class:`InventorHub() <pybricks.hubs.PrimeHub>`,
:class:`EssentialHub() <pybricks.hubs.EssentialHub>`, or
:class:`TechnicHub() <pybricks.hubs.TechnicHub>`. This way your robot
knows which rotation to measure when turning.
The gyro in each hub is a bit different, which can cause it to be a few
degrees off for big turns, or many small turns in the same
direction. For example, you may need to use
:meth:`turn(357) <pybricks.robotics.DriveBase.turn>` or
:meth:`turn(362) <pybricks.robotics.DriveBase.turn>`
on your robot to make a full turn.
By default, this class tries to maintain the robot's position after a move
completes. This means the wheels will spin if you pick the robot up, in an
effort to maintain its heading angle. To avoid this, you can choose
``then=Stop.COAST`` in your last
:meth:`straight <pybricks.robotics.DriveBase.straight>`,
:meth:`turn <pybricks.robotics.DriveBase.turn>`, or
:meth:`curve <pybricks.robotics.DriveBase.curve>` command.
Examples Examples
------------------- -------------------
Driving straight and turning in place Driving straight and turning in place
********************************************** **********************************************
The following program shows the basics of driving and turning.
To use the built-in gyro, just replace the two occurrences of
:class:`DriveBase` with :class:`GyroDriveBase`.
.. literalinclude:: .. literalinclude::
../../examples/pup/robotics/drivebase_basics.py ../../examples/pup/robotics/drivebase_basics.py
+34 -1
View File
@@ -248,7 +248,7 @@ For example, you can choose the frequency of a beep to change the pitch.
temperature: °C temperature: °C
--------------- ---------------
Temperature is measured in degrees Celcius (°C). To convert to degrees Temperature is measured in degrees Celsius (°C). To convert to degrees
Fahrenheit (°F) or Kelvin (K), you can use the following conversion formulas: Fahrenheit (°F) or Kelvin (K), you can use the following conversion formulas:
:math:`^{\circ}\kern1pt\!F =\kern1pt^{\circ}\kern1pt\!C \cdot \frac{9}{5} + 32`. :math:`^{\circ}\kern1pt\!F =\kern1pt^{\circ}\kern1pt\!C \cdot \frac{9}{5} + 32`.
@@ -260,3 +260,36 @@ Fahrenheit (°F) or Kelvin (K), you can use the following conversion formulas:
hue: deg hue: deg
-------------- --------------
Hue of a color (0-359 degrees). Hue of a color (0-359 degrees).
.. _robotframe:
Reference frames
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The Pybricks module and this documentation use the following conventions:
- X: Positive means forward. Negative means backward.
- Y: Positive means to the left. Negative means to the right.
- Z: Positive means upward. Negative means downward.
To make sure that all hub measurements (such as acceleration) have the correct
value and sign, you can specify how the hub is mounted in your creation. This
adjust the measurements so that it is easy to see how your *robot* is moving,
rather than how the *hub* is moving.
For example, the hub may be mounted upside down in your design. If you
configure the settings as shown in :numref:`fig_imuexamples`, the hub
measurements will be adjusted accordingly. This way, a positive acceleration
value in the X direction means that your *robot* accelerates forward, even
though the *hub* accelerates backward.
.. _fig_imuexamples:
.. figure:: ../main/diagrams/imuexamples.png
:width: 100 %
How to configure the ``top_side`` and ``front_side`` settings for three
different robot designs. The same technique can be applied to other hubs
and other creations, by noting which way the top and
front :class:`Side <Side>` of the hub are pointing. The example
on the left is the default configuration.
+25 -1
View File
@@ -1,11 +1,14 @@
.. pybricks-requirements:: .. pybricks-requirements::
:mod:`tools <pybricks.tools>` -- Timing tools :mod:`tools <pybricks.tools>` -- General purpose tools
======================================================== ========================================================
.. automodule:: pybricks.tools .. automodule:: pybricks.tools
:no-members: :no-members:
Timing tools
---------------
.. autofunction:: wait .. autofunction:: wait
.. autoclass:: pybricks.tools.StopWatch .. autoclass:: pybricks.tools.StopWatch
@@ -19,3 +22,24 @@
.. automethod:: pybricks.tools.StopWatch.reset .. automethod:: pybricks.tools.StopWatch.reset
Linear algebra tools
--------------------
.. versionchanged:: 3.3
These tools were previously located in the ``pybricks.geometry`` module.
.. pybricks-requirements:: stm32-float
.. autoclass:: pybricks.tools.Matrix
:no-members:
.. autoattribute:: pybricks.tools::Matrix.T
.. autoattribute:: pybricks.tools::Matrix.shape
.. pybricks-requirements:: stm32-float
.. autofunction:: pybricks.tools.vector
.. autofunction:: pybricks.tools.cross
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env python3
from pybricks.messaging import BluetoothMailboxServer, TextMailbox
# This demo makes your PC talk to an EV3 over Bluetooth.
#
# This is identical to the EV3 server example in ../bluetooth_server
#
# The only difference is that it runs in Python3 on your computer, thanks to
# the Python3 implementation of the messaging module that is included here.
# As far as the EV3 is concerned, it thinks it just talks to an EV3 client.
#
# So, the EV3 client example needs no further modifications. The connection
# procedure is also the same as documented in the messaging module docs:
# https://docs.pybricks.com/en/latest/messaging.html
server = BluetoothMailboxServer()
mbox = TextMailbox("greeting", server)
# The server must be started before the client!
print("waiting for connection...")
server.wait_for_connection()
print("connected!")
# In this program, the server waits for the client to send the first message
# and then sends a reply.
mbox.wait()
print(mbox.read())
mbox.send("hello to you!")
+16 -56
View File
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
# Copyright (C) 2020 The Pybricks Authors # Copyright (C) 2020,2023 The Pybricks Authors
""" """
:class:`RFCOMMServer` can be used to communicate with other Bluetooth RFCOMM :class:`RFCOMMServer` can be used to communicate with other Bluetooth RFCOMM
@@ -10,29 +10,13 @@ remain a strict subset of that implementation when it comes to low-level
implementation details. implementation details.
""" """
from bluetooth import BluetoothSocket, RFCOMM from socket import socket, AF_BLUETOOTH, BTPROTO_RFCOMM, SOCK_STREAM
from socketserver import ThreadingMixIn from socketserver import ThreadingMixIn
BDADDR_ANY = ""
def str2ba(string, ba):
"""Convert string to Bluetooth address"""
for i, v in enumerate(string.split(":")):
ba.b[5 - i] = int(v, 16)
def ba2str(ba):
"""Convert Bluetooth address to string"""
string = []
for b in ba.b:
string.append("{:02X}".format(b))
string.reverse()
return ":".join(string).upper()
class RFCOMMServer: class RFCOMMServer:
"""Object that simplifies setting up an RFCOMM socket server. """
Object that simplifies setting up an RFCOMM socket server.
This is based on the ``socketserver.SocketServer`` class in the Python This is based on the ``socketserver.SocketServer`` class in the Python
standard library. standard library.
@@ -44,10 +28,10 @@ class RFCOMMServer:
self.server_address = server_address self.server_address = server_address
self.RequestHandlerClass = RequestHandlerClass self.RequestHandlerClass = RequestHandlerClass
self.socket = BluetoothSocket(RFCOMM) self.socket = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)
try: try:
self.socket.bind((server_address[0], server_address[1])) self.socket.bind(server_address)
# self.server_address = self.socket.getsockname() # self.server_address = self.socket.getsockname()
self.socket.listen(self.request_queue_size) self.socket.listen(self.request_queue_size)
except Exception: except Exception:
@@ -83,50 +67,22 @@ class RFCOMMServer:
self.socket.close() self.socket.close()
class StreamRequestHandler:
"""Class that handles incoming requests.
This is based on ``socketserver.StreamRequestHandler`` from the Python
standard library.
"""
def __init__(self, request, client_address, server):
self.request = request
self.client_address = client_address
self.server = server
self.setup()
try:
self.handle()
finally:
self.finish()
def setup(self):
self.wfile = self.request
self.rfile = self.request
def handle(self):
pass
def finish(self):
pass
class ThreadingRFCOMMServer(ThreadingMixIn, RFCOMMServer): class ThreadingRFCOMMServer(ThreadingMixIn, RFCOMMServer):
"""Version of :class:`RFCOMMServer` that handles connections in a new """
thread. Version of :class:`RFCOMMServer` that handles connections in a new thread.
""" """
pass daemon_threads = True
class RFCOMMClient: class RFCOMMClient:
def __init__(self, client_address, RequestHandlerClass): def __init__(self, client_address, RequestHandlerClass):
self.client_address = client_address self.client_address = client_address
self.RequestHandlerClass = RequestHandlerClass self.RequestHandlerClass = RequestHandlerClass
self.socket = BluetoothSocket(RFCOMM) self.socket = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)
def handle_request(self): def handle_request(self):
self.socket.connect((self.client_address[0], self.client_address[1])) self.socket.connect(self.client_address)
try: try:
self.process_request(self.socket, self.client_address) self.process_request(self.socket, self.client_address)
except Exception: except Exception:
@@ -145,4 +101,8 @@ class RFCOMMClient:
class ThreadingRFCOMMClient(ThreadingMixIn, RFCOMMClient): class ThreadingRFCOMMClient(ThreadingMixIn, RFCOMMClient):
pass """
Version of :class:`RFCOMMClient` that handles connections in a new thread.
"""
daemon_threads = True
+11 -14
View File
@@ -1,16 +1,13 @@
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
# Copyright (C) 2020 The Pybricks Authors # Copyright (C) 2020,2023 The Pybricks Authors
from _thread import allocate_lock
from errno import ECONNRESET from errno import ECONNRESET
from struct import pack, unpack from struct import pack, unpack
from socket import BDADDR_ANY
from socketserver import StreamRequestHandler
from threading import Lock
from .bluetooth import ( from .bluetooth import ThreadingRFCOMMServer, ThreadingRFCOMMClient
BDADDR_ANY,
ThreadingRFCOMMServer,
ThreadingRFCOMMClient,
StreamRequestHandler,
)
def resolve(brick): def resolve(brick):
@@ -151,7 +148,7 @@ class MailboxHandler(StreamRequestHandler):
self.server._clients[self.client_address[0]] = self.request self.server._clients[self.client_address[0]] = self.request
while True: while True:
try: try:
buf = self.rfile.recv(2) buf = self.rfile.read(2)
if len(buf) == 0: if len(buf) == 0:
break break
except OSError as ex: except OSError as ex:
@@ -160,7 +157,7 @@ class MailboxHandler(StreamRequestHandler):
break break
raise raise
(size,) = unpack("<H", buf) (size,) = unpack("<H", buf)
buf = self.rfile.recv(size) buf = self.rfile.read(size)
msg_count, cmd_type, cmd, name_size = unpack("<HBBB", buf[0:5]) msg_count, cmd_type, cmd, name_size = unpack("<HBBB", buf[0:5])
if cmd_type != SYSTEM_COMMAND_NO_REPLY: if cmd_type != SYSTEM_COMMAND_NO_REPLY:
raise ValueError("Bad message type") raise ValueError("Bad message type")
@@ -180,7 +177,7 @@ class MailboxHandler(StreamRequestHandler):
class MailboxHandlerMixIn: class MailboxHandlerMixIn:
def __init__(self): def __init__(self):
# protects against concurrent access of other attributes # protects against concurrent access of other attributes
self._lock = allocate_lock() self._lock = Lock()
# map of mailbox name to raw data # map of mailbox name to raw data
self._mailboxes = {} self._mailboxes = {}
# map of device name/address to object with send() method # map of device name/address to object with send() method
@@ -247,7 +244,7 @@ class MailboxHandlerMixIn:
def wait_for_mailbox_update(self, mbox): def wait_for_mailbox_update(self, mbox):
"""Waits until ``mbox`` receives a value.""" """Waits until ``mbox`` receives a value."""
lock = allocate_lock() lock = Lock()
lock.acquire() lock.acquire()
with self._lock: with self._lock:
self._updates[mbox] = lock self._updates[mbox] = lock
@@ -264,7 +261,7 @@ class BluetoothMailboxServer(MailboxHandlerMixIn, ThreadingRFCOMMServer):
EV3. EV3.
The remote EV3 can either be running MicroPython or the standard EV3 The remote EV3 can either be running MicroPython or the standard EV3
firmare. firmware.
""" """
super().__init__() super().__init__()
super(ThreadingRFCOMMServer, self).__init__( super(ThreadingRFCOMMServer, self).__init__(
@@ -307,7 +304,7 @@ class BluetoothMailboxClient(MailboxHandlerMixIn):
remote EV3s. remote EV3s.
The remote EV3s can either be running MicroPython or the standard EV3 The remote EV3s can either be running MicroPython or the standard EV3
firmare. firmware.
""" """
def __enter__(self): def __enter__(self):
+1 -1
View File
@@ -36,7 +36,7 @@ def scale(val, src, dst):
# Create a loop to react to events # Create a loop to react to events
# This loop reacte to all main PS4 button and stick events. I have left out # This loop reacts to all main PS4 button and stick events. I have left out
# buttons like share and options, but can easily be added in by referring # buttons like share and options, but can easily be added in by referring
# to the table at: https://github.com/codeadamca/python-connect-ps4 # to the table at: https://github.com/codeadamca/python-connect-ps4
+24
View File
@@ -0,0 +1,24 @@
# ThisHub = MoveHub CityHub TechnicHub PrimeHub EssentialHub
from pybricks.hubs import ThisHub
from pybricks.pupdevices import Motor
from pybricks.parameters import Port
from pybricks.tools import wait
# Initialize the hub.
hub = ThisHub(broadcast_channel=1)
# Initialize the motors.
left_motor = Motor(Port.A)
right_motor = Motor(Port.B)
while True:
# Read the motor angles to be sent to the other hub.
left_angle = left_motor.angle()
right_angle = right_motor.angle()
# Set the broadcast data and start broadcasting if not already doing so.
hub.ble.broadcast(left_angle, right_angle)
# Broadcasts are only sent every 100 milliseconds, so there is no reason
# to call the broadcast() method more often than that.
wait(100)
+39
View File
@@ -0,0 +1,39 @@
# ThisHub = MoveHub CityHub TechnicHub PrimeHub EssentialHub
from pybricks.hubs import ThisHub
from pybricks.pupdevices import Motor
from pybricks.parameters import Color, Port
from pybricks.tools import wait
# Initialize the hub.
hub = ThisHub(observe_channels=[1])
# Initialize the motors.
left_motor = Motor(Port.A)
right_motor = Motor(Port.B)
while True:
# Receive broadcast from the other hub.
data = hub.ble.observe(1)
if data is None:
# No data has been received in the last 1 second.
hub.light.on(Color.RED)
else:
# Data was received and is less that one second old.
hub.light.on(Color.GREEN)
# *data* contains the same values in the same order
# that were passed to hub.ble.broadcast() on the
# other hub.
left_angle = data[0]
right_angle = data[1]
# Make the motors on this hub mirror the position of the
# motors on the other hub.
left_motor.track_target(left_angle)
right_motor.track_target(right_angle)
# Broadcasts are only sent every 100 milliseconds, so there is
# no reason to call the observe() method more often than that.
wait(100)
+1 -1
View File
@@ -1,7 +1,7 @@
# ThisHub = TechnicHub PrimeHub EssentialHub # ThisHub = TechnicHub PrimeHub EssentialHub
from pybricks.hubs import ThisHub from pybricks.hubs import ThisHub
from pybricks.tools import wait from pybricks.tools import wait
from pybricks.geometry import Axis from pybricks.parameters import Axis
# Initialize the hub. # Initialize the hub.
hub = ThisHub() hub = ThisHub()
+1 -1
View File
@@ -1,7 +1,7 @@
# ThisHub = TechnicHub PrimeHub EssentialHub # ThisHub = TechnicHub PrimeHub EssentialHub
from pybricks.hubs import ThisHub from pybricks.hubs import ThisHub
from pybricks.tools import wait from pybricks.tools import wait
from pybricks.geometry import Axis from pybricks.parameters import Axis
# Initialize the hub. In this case, specify that the hub is mounted with the # Initialize the hub. In this case, specify that the hub is mounted with the
# top side facing forward and the front side facing to the right. # top side facing forward and the front side facing to the right.
+1 -2
View File
@@ -1,6 +1,5 @@
from pybricks.hubs import PrimeHub from pybricks.hubs import PrimeHub
from pybricks.tools import wait from pybricks.tools import wait, Matrix
from pybricks.geometry import Matrix
# Initialize the hub. # Initialize the hub.
hub = PrimeHub() hub = PrimeHub()
+21 -6
View File
@@ -4,19 +4,34 @@ from uerrno import ENODEV
# Dictionary of device identifiers along with their name. # Dictionary of device identifiers along with their name.
device_names = { device_names = {
34: "Wedo 2.0 Tilt Sensor", # pybricks.pupdevices.DCMotor
35: "Wedo 2.0 Infrared Sensor", 1: "Wedo 2.0 Medium Motor",
37: "BOOST Color Distance Sensor", 2: "Powered Up Train Motor",
# pybricks.pupdevices.Light
8: "Powered Up Light",
# pybricks.pupdevices.Motor
38: "BOOST Interactive Motor", 38: "BOOST Interactive Motor",
46: "Technic Large Motor", 46: "Technic Large Motor",
47: "Technic Extra Large Motor", 47: "Technic Extra Large Motor",
48: "SPIKE Medium Angular Motor", 48: "SPIKE Medium Angular Motor",
49: "SPIKE Large Angular Motor", 49: "SPIKE Large Angular Motor",
61: "SPIKE Color Sensor", 65: "SPIKE Small Angular Motor",
62: "SPIKE Ultrasonic Sensor",
63: "SPIKE Force Sensor",
75: "Technic Medium Angular Motor", 75: "Technic Medium Angular Motor",
76: "Technic Large Angular Motor", 76: "Technic Large Angular Motor",
# pybricks.pupdevices.TiltSensor
34: "Wedo 2.0 Tilt Sensor",
# pybricks.pupdevices.InfraredSensor
35: "Wedo 2.0 Infrared Motion Sensor",
# pybricks.pupdevices.ColorDistanceSensor
37: "BOOST Color Distance Sensor",
# pybricks.pupdevices.ColorSensor
61: "SPIKE Color Sensor",
# pybricks.pupdevices.UltrasonicSensor
62: "SPIKE Ultrasonic Sensor",
# pybricks.pupdevices.ForceSensor
63: "SPIKE Force Sensor",
# pybricks.pupdevices.ColorLightMatrix
64: "SPIKE 3x3 Color Light Matrix",
} }
# Make a list of known ports. # Make a list of known ports.
+2 -2
View File
@@ -14,10 +14,10 @@ drive_base = DriveBase(left_motor, right_motor, wheel_diameter=56, axle_track=11
# Drive forward by 500mm (half a meter). # Drive forward by 500mm (half a meter).
drive_base.straight(500) drive_base.straight(500)
# Turn around clockwise (180 degrees) # Turn around clockwise by 180 degrees.
drive_base.turn(180) drive_base.turn(180)
# Drive forward again to drive back. # Drive forward again to get back to the start.
drive_base.straight(500) drive_base.straight(500)
# Turn around counterclockwise. # Turn around counterclockwise.
+14
View File
@@ -4,6 +4,20 @@
## Unreleased ## Unreleased
## 1.8.0 - 2023-04-21
### Changed
- Updated `pybricks` package to v3.3.0b4.
## 1.7.0 - 2022-12-28
### Added
- Added `update_user_modules()` function for filtering on user modules.
### Fixed
- Fixed code completion for builtin types.
- Fixed code completion for names starting with `_`.
## 1.6.0 - 2022-12-09 ## 1.6.0 - 2022-12-09
### Changed ### Changed
+267 -267
View File
@@ -1,3 +1,5 @@
# This file is automatically @generated by Poetry and should not be changed by hand.
[[package]] [[package]]
name = "attrs" name = "attrs"
version = "22.1.0" version = "22.1.0"
@@ -5,6 +7,10 @@ description = "Classes Without Boilerplate"
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.5" python-versions = ">=3.5"
files = [
{file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"},
{file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"},
]
[package.extras] [package.extras]
dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"] dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy (>=0.900,!=0.940)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "sphinx", "sphinx-notfound-page", "zope.interface"]
@@ -19,252 +25,7 @@ description = "The uncompromising code formatter."
category = "dev" category = "dev"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [
[package.dependencies]
click = ">=8.0.0"
mypy-extensions = ">=0.4.3"
pathspec = ">=0.9.0"
platformdirs = ">=2"
tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""}
[package.extras]
colorama = ["colorama (>=0.4.3)"]
d = ["aiohttp (>=3.7.4)"]
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
uvloop = ["uvloop (>=0.15.2)"]
[[package]]
name = "click"
version = "8.1.3"
description = "Composable command line interface toolkit"
category = "dev"
optional = false
python-versions = ">=3.7"
[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}
[[package]]
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
category = "dev"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
[[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 = "exceptiongroup"
version = "1.0.4"
description = "Backport of PEP 654 (exception groups)"
category = "dev"
optional = false
python-versions = ">=3.7"
[package.extras]
test = ["pytest (>=6)"]
[[package]]
name = "flake8"
version = "4.0.1"
description = "the modular source code checker: pep8 pyflakes and co"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
mccabe = ">=0.6.0,<0.7.0"
pycodestyle = ">=2.8.0,<2.9.0"
pyflakes = ">=2.4.0,<2.5.0"
[[package]]
name = "iniconfig"
version = "1.1.1"
description = "iniconfig: brain-dead simple config-ini parsing"
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "jedi"
version = "0.18.1"
description = "An autocompletion tool for Python that can be used for text editors."
category = "main"
optional = false
python-versions = ">=3.6"
[package.dependencies]
parso = ">=0.8.0,<0.9.0"
[package.extras]
qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<7.0.0)"]
[[package]]
name = "mccabe"
version = "0.6.1"
description = "McCabe checker, plugin for flake8"
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "mypy-extensions"
version = "0.4.3"
description = "Experimental type system extensions for programs checked with the mypy typechecker."
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "packaging"
version = "21.3"
description = "Core utilities for Python packages"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
[[package]]
name = "parso"
version = "0.8.3"
description = "A Python Parser"
category = "main"
optional = false
python-versions = ">=3.6"
[package.extras]
qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
testing = ["docopt", "pytest (<6.0.0)"]
[[package]]
name = "pathspec"
version = "0.10.2"
description = "Utility library for gitignore style pattern matching of file paths."
category = "dev"
optional = false
python-versions = ">=3.7"
[[package]]
name = "platformdirs"
version = "2.5.4"
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
category = "dev"
optional = false
python-versions = ">=3.7"
[package.extras]
docs = ["furo (>=2022.9.29)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.4)"]
test = ["appdirs (==1.4.4)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"]
[[package]]
name = "pluggy"
version = "1.0.0"
description = "plugin and hook calling mechanisms for python"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.extras]
dev = ["pre-commit", "tox"]
testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "pybricks"
version = "3.2.0c1"
description = "Documentation and user-API stubs for Pybricks MicroPython"
category = "main"
optional = false
python-versions = "^3.8"
develop = true
[package.source]
type = "directory"
url = ".."
[[package]]
name = "pycodestyle"
version = "2.8.0"
description = "Python style guide checker"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "pyflakes"
version = "2.4.0"
description = "passive checker of Python programs"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[[package]]
name = "pyparsing"
version = "3.0.9"
description = "pyparsing module - Classes and methods to define and execute parsing grammars"
category = "dev"
optional = false
python-versions = ">=3.6.8"
[package.extras]
diagrams = ["jinja2", "railroad-diagrams"]
[[package]]
name = "pytest"
version = "7.2.0"
description = "pytest: simple powerful testing with Python"
category = "dev"
optional = false
python-versions = ">=3.7"
[package.dependencies]
attrs = ">=19.2.0"
colorama = {version = "*", markers = "sys_platform == \"win32\""}
exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
iniconfig = "*"
packaging = "*"
pluggy = ">=0.12,<2.0"
tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
[package.extras]
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]
[[package]]
name = "tomli"
version = "2.0.1"
description = "A lil' TOML parser"
category = "dev"
optional = false
python-versions = ">=3.7"
[[package]]
name = "typing-extensions"
version = "4.2.0"
description = "Backported and Experimental Type Hints for Python 3.7+"
category = "main"
optional = false
python-versions = ">=3.7"
[metadata]
lock-version = "1.1"
python-versions = ">= 3.10, < 3.11"
content-hash = "971c6d0dddd9bb8e33a942e2360905a7d80a1a9d33015c0defc08345df5c1019"
[metadata.files]
attrs = [
{file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"},
{file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"},
]
black = [
{file = "black-22.10.0-1fixedarch-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:5cc42ca67989e9c3cf859e84c2bf014f6633db63d1cbdf8fdb666dcd9e77e3fa"}, {file = "black-22.10.0-1fixedarch-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:5cc42ca67989e9c3cf859e84c2bf014f6633db63d1cbdf8fdb666dcd9e77e3fa"},
{file = "black-22.10.0-1fixedarch-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:5d8f74030e67087b219b032aa33a919fae8806d49c867846bfacde57f43972ef"}, {file = "black-22.10.0-1fixedarch-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:5d8f74030e67087b219b032aa33a919fae8806d49c867846bfacde57f43972ef"},
{file = "black-22.10.0-1fixedarch-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:197df8509263b0b8614e1df1756b1dd41be6738eed2ba9e9769f3880c2b9d7b6"}, {file = "black-22.10.0-1fixedarch-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:197df8509263b0b8614e1df1756b1dd41be6738eed2ba9e9769f3880c2b9d7b6"},
@@ -287,84 +48,323 @@ black = [
{file = "black-22.10.0-py3-none-any.whl", hash = "sha256:c957b2b4ea88587b46cf49d1dc17681c1e672864fd7af32fc1e9664d572b3458"}, {file = "black-22.10.0-py3-none-any.whl", hash = "sha256:c957b2b4ea88587b46cf49d1dc17681c1e672864fd7af32fc1e9664d572b3458"},
{file = "black-22.10.0.tar.gz", hash = "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1"}, {file = "black-22.10.0.tar.gz", hash = "sha256:f513588da599943e0cde4e32cc9879e825d58720d6557062d1098c5ad80080e1"},
] ]
click = [
[package.dependencies]
click = ">=8.0.0"
mypy-extensions = ">=0.4.3"
pathspec = ">=0.9.0"
platformdirs = ">=2"
tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""}
[package.extras]
colorama = ["colorama (>=0.4.3)"]
d = ["aiohttp (>=3.7.4)"]
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
uvloop = ["uvloop (>=0.15.2)"]
[[package]]
name = "click"
version = "8.1.3"
description = "Composable command line interface toolkit"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
{file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
] ]
colorama = [
[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}
[[package]]
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
category = "dev"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
] ]
docstring-parser = [
[[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"
files = [
{file = "docstring_parser-0.14.1-py3-none-any.whl", hash = "sha256:14ac6ec1f1ba6905c4d8cb90fd0bc55394f5678183752c90e44812bf28d7a515"}, {file = "docstring_parser-0.14.1-py3-none-any.whl", hash = "sha256:14ac6ec1f1ba6905c4d8cb90fd0bc55394f5678183752c90e44812bf28d7a515"},
{file = "docstring_parser-0.14.1.tar.gz", hash = "sha256:2c77522e31b7c88b1ab457a1f3c9ae38947ad719732260ba77ee8a3deb58622a"}, {file = "docstring_parser-0.14.1.tar.gz", hash = "sha256:2c77522e31b7c88b1ab457a1f3c9ae38947ad719732260ba77ee8a3deb58622a"},
] ]
exceptiongroup = [
[[package]]
name = "exceptiongroup"
version = "1.0.4"
description = "Backport of PEP 654 (exception groups)"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "exceptiongroup-1.0.4-py3-none-any.whl", hash = "sha256:542adf9dea4055530d6e1279602fa5cb11dab2395fa650b8674eaec35fc4a828"}, {file = "exceptiongroup-1.0.4-py3-none-any.whl", hash = "sha256:542adf9dea4055530d6e1279602fa5cb11dab2395fa650b8674eaec35fc4a828"},
{file = "exceptiongroup-1.0.4.tar.gz", hash = "sha256:bd14967b79cd9bdb54d97323216f8fdf533e278df937aa2a90089e7d6e06e5ec"}, {file = "exceptiongroup-1.0.4.tar.gz", hash = "sha256:bd14967b79cd9bdb54d97323216f8fdf533e278df937aa2a90089e7d6e06e5ec"},
] ]
flake8 = [
[package.extras]
test = ["pytest (>=6)"]
[[package]]
name = "flake8"
version = "4.0.1"
description = "the modular source code checker: pep8 pyflakes and co"
category = "dev"
optional = false
python-versions = ">=3.6"
files = [
{file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"},
{file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"},
] ]
iniconfig = [
[package.dependencies]
mccabe = ">=0.6.0,<0.7.0"
pycodestyle = ">=2.8.0,<2.9.0"
pyflakes = ">=2.4.0,<2.5.0"
[[package]]
name = "iniconfig"
version = "1.1.1"
description = "iniconfig: brain-dead simple config-ini parsing"
category = "dev"
optional = false
python-versions = "*"
files = [
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
] ]
jedi = [
[[package]]
name = "jedi"
version = "0.18.1"
description = "An autocompletion tool for Python that can be used for text editors."
category = "main"
optional = false
python-versions = ">=3.6"
files = [
{file = "jedi-0.18.1-py2.py3-none-any.whl", hash = "sha256:637c9635fcf47945ceb91cd7f320234a7be540ded6f3e99a50cb6febdfd1ba8d"}, {file = "jedi-0.18.1-py2.py3-none-any.whl", hash = "sha256:637c9635fcf47945ceb91cd7f320234a7be540ded6f3e99a50cb6febdfd1ba8d"},
{file = "jedi-0.18.1.tar.gz", hash = "sha256:74137626a64a99c8eb6ae5832d99b3bdd7d29a3850fe2aa80a4126b2a7d949ab"}, {file = "jedi-0.18.1.tar.gz", hash = "sha256:74137626a64a99c8eb6ae5832d99b3bdd7d29a3850fe2aa80a4126b2a7d949ab"},
] ]
mccabe = [
[package.dependencies]
parso = ">=0.8.0,<0.9.0"
[package.extras]
qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<7.0.0)"]
[[package]]
name = "mccabe"
version = "0.6.1"
description = "McCabe checker, plugin for flake8"
category = "dev"
optional = false
python-versions = "*"
files = [
{file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
{file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
] ]
mypy-extensions = [
[[package]]
name = "mypy-extensions"
version = "0.4.3"
description = "Experimental type system extensions for programs checked with the mypy typechecker."
category = "dev"
optional = false
python-versions = "*"
files = [
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
] ]
packaging = [
[[package]]
name = "packaging"
version = "21.3"
description = "Core utilities for Python packages"
category = "dev"
optional = false
python-versions = ">=3.6"
files = [
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
] ]
parso = [
[package.dependencies]
pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
[[package]]
name = "parso"
version = "0.8.3"
description = "A Python Parser"
category = "main"
optional = false
python-versions = ">=3.6"
files = [
{file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"},
{file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"},
] ]
pathspec = [
[package.extras]
qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
testing = ["docopt", "pytest (<6.0.0)"]
[[package]]
name = "pathspec"
version = "0.10.2"
description = "Utility library for gitignore style pattern matching of file paths."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "pathspec-0.10.2-py3-none-any.whl", hash = "sha256:88c2606f2c1e818b978540f73ecc908e13999c6c3a383daf3705652ae79807a5"}, {file = "pathspec-0.10.2-py3-none-any.whl", hash = "sha256:88c2606f2c1e818b978540f73ecc908e13999c6c3a383daf3705652ae79807a5"},
{file = "pathspec-0.10.2.tar.gz", hash = "sha256:8f6bf73e5758fd365ef5d58ce09ac7c27d2833a8d7da51712eac6e27e35141b0"}, {file = "pathspec-0.10.2.tar.gz", hash = "sha256:8f6bf73e5758fd365ef5d58ce09ac7c27d2833a8d7da51712eac6e27e35141b0"},
] ]
platformdirs = [
[[package]]
name = "platformdirs"
version = "2.5.4"
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "platformdirs-2.5.4-py3-none-any.whl", hash = "sha256:af0276409f9a02373d540bf8480021a048711d572745aef4b7842dad245eba10"}, {file = "platformdirs-2.5.4-py3-none-any.whl", hash = "sha256:af0276409f9a02373d540bf8480021a048711d572745aef4b7842dad245eba10"},
{file = "platformdirs-2.5.4.tar.gz", hash = "sha256:1006647646d80f16130f052404c6b901e80ee4ed6bef6792e1f238a8969106f7"}, {file = "platformdirs-2.5.4.tar.gz", hash = "sha256:1006647646d80f16130f052404c6b901e80ee4ed6bef6792e1f238a8969106f7"},
] ]
pluggy = [
[package.extras]
docs = ["furo (>=2022.9.29)", "proselint (>=0.13)", "sphinx (>=5.3)", "sphinx-autodoc-typehints (>=1.19.4)"]
test = ["appdirs (==1.4.4)", "pytest (>=7.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"]
[[package]]
name = "pluggy"
version = "1.0.0"
description = "plugin and hook calling mechanisms for python"
category = "dev"
optional = false
python-versions = ">=3.6"
files = [
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
{file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
] ]
pybricks = []
pycodestyle = [ [package.extras]
dev = ["pre-commit", "tox"]
testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "pybricks"
version = "3.3.0a4"
description = "Documentation and user-API stubs for Pybricks MicroPython"
category = "main"
optional = false
python-versions = "^3.8"
files = []
develop = true
[package.source]
type = "directory"
url = ".."
[[package]]
name = "pycodestyle"
version = "2.8.0"
description = "Python style guide checker"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
files = [
{file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"},
{file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"},
] ]
pyflakes = [
[[package]]
name = "pyflakes"
version = "2.4.0"
description = "passive checker of Python programs"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
files = [
{file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"}, {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"},
{file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"}, {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"},
] ]
pyparsing = [
[[package]]
name = "pyparsing"
version = "3.0.9"
description = "pyparsing module - Classes and methods to define and execute parsing grammars"
category = "dev"
optional = false
python-versions = ">=3.6.8"
files = [
{file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
{file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"},
] ]
pytest = [
[package.extras]
diagrams = ["jinja2", "railroad-diagrams"]
[[package]]
name = "pytest"
version = "7.2.0"
description = "pytest: simple powerful testing with Python"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "pytest-7.2.0-py3-none-any.whl", hash = "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71"}, {file = "pytest-7.2.0-py3-none-any.whl", hash = "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71"},
{file = "pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"}, {file = "pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"},
] ]
tomli = [
[package.dependencies]
attrs = ">=19.2.0"
colorama = {version = "*", markers = "sys_platform == \"win32\""}
exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
iniconfig = "*"
packaging = "*"
pluggy = ">=0.12,<2.0"
tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
[package.extras]
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]
[[package]]
name = "tomli"
version = "2.0.1"
description = "A lil' TOML parser"
category = "dev"
optional = false
python-versions = ">=3.7"
files = [
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
] ]
typing-extensions = [
[[package]]
name = "typing-extensions"
version = "4.2.0"
description = "Backported and Experimental Type Hints for Python 3.7+"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "typing_extensions-4.2.0-py3-none-any.whl", hash = "sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708"}, {file = "typing_extensions-4.2.0-py3-none-any.whl", hash = "sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708"},
{file = "typing_extensions-4.2.0.tar.gz", hash = "sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376"}, {file = "typing_extensions-4.2.0.tar.gz", hash = "sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376"},
] ]
[metadata]
lock-version = "2.0"
python-versions = ">= 3.10, < 3.12"
content-hash = "80c838121a027348930e78cdbd766f86e5180408c5ac682b3deba8d658b408d6"
+3 -3
View File
@@ -1,13 +1,13 @@
[tool.poetry] [tool.poetry]
name = "pybricks_jedi" name = "pybricks_jedi"
version = "1.6.0" version = "1.8.0"
description = "Code completion for Pybricks." description = "Code completion for Pybricks."
authors = ["The Pybricks Authors <team@pybricks.com>"] authors = ["The Pybricks Authors <team@pybricks.com>"]
license = "MIT" license = "MIT"
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = ">= 3.10, < 3.11" python = ">= 3.10, < 3.12"
pybricks = "3.2.0c1" pybricks = "3.3.0a4"
jedi = "0.18.1" jedi = "0.18.1"
typing-extensions = "4.2.0" typing-extensions = "4.2.0"
docstring-parser = "0.14.1" docstring-parser = "0.14.1"
+171 -90
View File
@@ -2,6 +2,7 @@ import io
import json import json
import re import re
from enum import IntEnum from enum import IntEnum
from typing import Iterable
import docstring_parser import docstring_parser
import jedi import jedi
@@ -12,7 +13,6 @@ from typing_extensions import NotRequired, TypedDict
PYBRICKS_CODE_PACKAGES = { PYBRICKS_CODE_PACKAGES = {
"micropython", "micropython",
"pybricks", "pybricks",
"pybricks.geometry",
"pybricks.hubs", "pybricks.hubs",
"pybricks.iodevices", "pybricks.iodevices",
"pybricks.parameters", "pybricks.parameters",
@@ -31,90 +31,153 @@ PYBRICKS_CODE_PACKAGES = {
# Subset of Python builtins included in Pybricks MicroPython. # Subset of Python builtins included in Pybricks MicroPython.
PYBRICKS_BUILTINS = { PYBRICKS_BUILTINS = {
"abs", "builtins.abs",
"all", "builtins.all",
"any", "builtins.any",
"ArithmeticError", "builtins.ArithmeticError",
"AssertionError", "builtins.AssertionError",
"AttributeError", "builtins.AttributeError",
"BaseException", "builtins.BaseException",
"bin", "builtins.bin",
"bool", "builtins.bool",
"bytearray", "builtins.bytearray",
"bytes", "builtins.bytes",
"callable", "builtins.callable",
"chr", "builtins.chr",
"classmethod", "builtins.classmethod",
"complex", "builtins.complex",
"dict", "builtins.dict",
"dir", "builtins.dir",
"divmod", "builtins.divmod",
"enumerate", "builtins.enumerate",
"EOFError", "builtins.EOFError",
"eval", "builtins.eval",
"Exception", "builtins.Exception",
"exec", "builtins.exec",
"float", "builtins.float",
"GeneratorExit", "builtins.GeneratorExit",
"getattr", "builtins.getattr",
"globals", "builtins.globals",
"hasattr", "builtins.hasattr",
"hash", "builtins.hash",
"help", "builtins.help",
"hex", "builtins.hex",
"id", "builtins.id",
"ImportError", "builtins.ImportError",
"IndentationError", "builtins.IndentationError",
"IndexError", "builtins.IndexError",
"input", "builtins.input",
"int", "builtins.int",
"isinstance", "builtins.isinstance",
"issubclass", "builtins.issubclass",
"iter", "builtins.iter",
"KeyboardInterrupt", "builtins.KeyboardInterrupt",
"KeyError", "builtins.KeyError",
"len", "builtins.len",
"list", "builtins.list",
"locals", "builtins.locals",
"LookupError", "builtins.LookupError",
"map", "builtins.map",
"max", "builtins.max",
"MemoryError", "builtins.MemoryError",
"min", "builtins.min",
"NameError", "builtins.NameError",
"next", "builtins.next",
"NotImplementedError", "builtins.NotImplementedError",
"object", "builtins.object",
"oct", "builtins.oct",
"ord", "builtins.ord",
"OSError", "builtins.OSError",
"OverflowError", "builtins.OverflowError",
"pow", "builtins.pow",
"print", "builtins.print",
"range", "builtins.range",
"repr", "builtins.repr",
"reversed", "builtins.reversed",
"round", "builtins.round",
"RuntimeError", "builtins.RuntimeError",
"set", "builtins.set",
"setattr", "builtins.setattr",
"slice", "builtins.slice",
"sorted", "builtins.sorted",
"staticmethod", "builtins.staticmethod",
"StopIteration", "builtins.StopIteration",
"str", "builtins.str",
"sum", "builtins.sum",
"super", "builtins.super",
"SyntaxError", "builtins.SyntaxError",
"SystemExit", "builtins.SystemExit",
"tuple", "builtins.tuple",
"type", "builtins.type",
"TypeError", "builtins.TypeError",
"ValueError", "builtins.ValueError",
"ZeroDivisionError", "builtins.ZeroDivisionError",
"zip", "builtins.zip",
"builtins.bytearray.append",
"builtins.bytearray.extend",
"builtins.dict.clear",
"builtins.dict.copy",
"builtins.dict.fromkeys",
"builtins.dict.get",
"builtins.dict.items",
"builtins.dict.keys",
"builtins.dict.pop",
"builtins.dict.popitem",
"builtins.dict.setdefault",
"builtins.dict.update",
"builtins.dict.values",
"builtins.int.from_bytes",
"builtins.int.to_bytes",
"builtins.list.append",
"builtins.list.clear",
"builtins.list.copy",
"builtins.list.count",
"builtins.list.extend",
"builtins.list.index",
"builtins.list.insert",
"builtins.list.pop",
"builtins.list.remove",
"builtins.list.reverse",
"builtins.list.sort",
"builtins.str.count",
"builtins.str.endswith",
"builtins.str.find",
"builtins.str.format",
"builtins.str.index",
"builtins.str.isalpha",
"builtins.str.isdigit",
"builtins.str.islower",
"builtins.str.isspace",
"builtins.str.isupper",
"builtins.str.join",
"builtins.str.lower",
"builtins.str.lstrip",
"builtins.str.replace",
"builtins.str.rfind",
"builtins.str.rindex",
"builtins.str.rsplit",
"builtins.str.rstrip",
"builtins.str.split",
"builtins.str.startswith",
"builtins.str.strip",
"builtins.str.upper",
"builtins.tuple.count",
"builtins.tuple.index",
} }
PYBRICKS_BUILTINS_NO_FULLNAME = {"items", "values"}
PYBRICKS_TYPING = {
"typing.MutableSequence.append",
"typing.MutableSequence.extend",
"typing.MutableMapping.pop",
"typing.Mapping.get",
}
MICROPY_NOT_SUPPORTED_DUNDER = {"__doc__", "__package__"}
user_modules = set()
# Types from monaco editor # Types from monaco editor
@@ -243,10 +306,14 @@ class SignatureHelp(TypedDict):
def _is_pybricks(c: Completion) -> bool: def _is_pybricks(c: Completion) -> bool:
# filter all "private" names (leading underscore) # filter all "private" names (leading underscore)
if (isinstance(c.name, str)) and c.name.startswith("_"): if c.name is not None:
return False if c.name.startswith("_") and c.module_name != "__main__":
return False
if isinstance(c.full_name, str): if c.name in MICROPY_NOT_SUPPORTED_DUNDER:
return False
if c.full_name is not None:
# this catches things like `from __future__ import annotations` # this catches things like `from __future__ import annotations`
if c.full_name.startswith("_") and c.module_name != "__main__": if c.full_name.startswith("_") and c.module_name != "__main__":
return False return False
@@ -256,16 +323,19 @@ def _is_pybricks(c: Completion) -> bool:
return False return False
# filter out typing types # filter out typing types
if c.full_name.startswith("typing."): if c.full_name.startswith("typing.") and c.full_name not in PYBRICKS_TYPING:
return False return False
# filter out packages/modules that are not included in Pybricks firmware # filter out packages/modules that are not included in Pybricks firmware
if c.type == "module" or c.type == "namespace": if c.type == "module" or c.type == "namespace":
return c.full_name in PYBRICKS_CODE_PACKAGES return c.full_name in PYBRICKS_CODE_PACKAGES or c.full_name in user_modules
# filter subset of builtins # filter subset of builtins
if c.module_name == "builtins" and c.type != "keyword": if c.module_name == "builtins" and c.type != "keyword":
return c.name in PYBRICKS_BUILTINS if c.full_name is None:
return c.name in PYBRICKS_BUILTINS_NO_FULLNAME
return c.full_name in PYBRICKS_BUILTINS
# this is a type alias, not a real type # this is a type alias, not a real type
if c.full_name == "pybricks.parameters.Number": if c.full_name == "pybricks.parameters.Number":
@@ -418,7 +488,6 @@ def initialize():
"pybricks._common", "pybricks._common",
"pybricks.ev3dev", "pybricks.ev3dev",
"pybricks.ev3dev.speaker", "pybricks.ev3dev.speaker",
"pybricks.geometry",
"pybricks.hubs", "pybricks.hubs",
"pybricks.iodevices", "pybricks.iodevices",
"pybricks.parameters", "pybricks.parameters",
@@ -473,3 +542,15 @@ def get_signatures(code: str, line: int, column: int) -> str:
""" """
signatures = jedi.Script(code).get_signatures(line, column - 1) signatures = jedi.Script(code).get_signatures(line, column - 1)
return json.dumps(_map_signatures(signatures)) return json.dumps(_map_signatures(signatures))
def update_user_modules(names: Iterable[str]) -> None:
"""
Updates the set of user module names used for filtering.
Args:
names:
An iterable of module names.
"""
user_modules.clear()
user_modules.update(names)
+143
View File
@@ -7,6 +7,9 @@ Tests for correct code completion of builtins.
import json import json
import pytest
from pybricks_jedi import CompletionItem, complete from pybricks_jedi import CompletionItem, complete
@@ -123,4 +126,144 @@ def test_empty_code():
"yield", "yield",
"ZeroDivisionError", "ZeroDivisionError",
"zip", "zip",
"__name__",
] ]
FUNCTION_PARAMS = [
pytest.param(
"''.",
[
"count",
"endswith",
"find",
"format",
"index",
"isalpha",
"isdigit",
"islower",
"isspace",
"isupper",
"join",
"lower",
"lstrip",
"replace",
"rfind",
"rindex",
"rsplit",
"rstrip",
"split",
"startswith",
"strip",
"upper",
],
),
pytest.param(
"str().",
[
"count",
"endswith",
"find",
"format",
"index",
"isalpha",
"isdigit",
"islower",
"isspace",
"isupper",
"join",
"lower",
"lstrip",
"replace",
"rfind",
"rindex",
"rsplit",
"rstrip",
"split",
"startswith",
"strip",
"upper",
],
),
pytest.param("(0).", ["from_bytes", "to_bytes"]),
pytest.param("int().", ["from_bytes", "to_bytes"]),
pytest.param(
"{}.",
[
"clear",
"copy",
"fromkeys",
"get",
"items",
"keys",
"pop",
"popitem",
"setdefault",
"update",
"values",
],
),
pytest.param(
"dict().",
[
"clear",
"copy",
"fromkeys",
"get",
"items",
"keys",
"pop",
"popitem",
"setdefault",
"update",
"values",
],
),
pytest.param(
"[].",
[
"append",
"clear",
"copy",
"count",
"extend",
"index",
"insert",
"pop",
"remove",
"reverse",
"sort",
],
),
pytest.param(
"list().",
[
"append",
"clear",
"copy",
"count",
"extend",
"index",
"insert",
"pop",
"remove",
"reverse",
"sort",
],
),
pytest.param("().", ["count", "index"]),
pytest.param("tuple().", ["count", "index"]),
pytest.param("bytearray().", ["append", "extend"]),
pytest.param("bytes().", []),
pytest.param("b''.", []),
pytest.param("float().", []),
pytest.param("(0.0).", []),
pytest.param("complex().", []),
pytest.param("type().", []),
]
@pytest.mark.parametrize("code,attributes", FUNCTION_PARAMS)
def test_get_completion_for_builtins(code: str, attributes: list[str]):
completions: list[CompletionItem] = json.loads(complete(code, 1, len(code) + 1))
assert [c["insertText"] for c in completions] == attributes
+1
View File
@@ -33,6 +33,7 @@ def test_hub_dot():
completions: list[CompletionItem] = json.loads(complete(code, 3, len(line) + 1)) completions: list[CompletionItem] = json.loads(complete(code, 3, len(line) + 1))
assert [c["insertText"] for c in completions] == [ assert [c["insertText"] for c in completions] == [
"battery", "battery",
"ble",
"button", "button",
"light", "light",
"system", "system",
@@ -33,6 +33,7 @@ def test_hub_dot():
completions: list[CompletionItem] = json.loads(complete(code, 3, len(line) + 1)) completions: list[CompletionItem] = json.loads(complete(code, 3, len(line) + 1))
assert [c["insertText"] for c in completions] == [ assert [c["insertText"] for c in completions] == [
"battery", "battery",
"ble",
"button", "button",
"charger", "charger",
"imu", "imu",
@@ -79,7 +80,12 @@ def test_hub_dot_imu_dot():
"acceleration", "acceleration",
"angular_velocity", "angular_velocity",
"heading", "heading",
"orientation",
"ready",
"reset_heading", "reset_heading",
"rotation",
"settings",
"stationary",
"tilt", "tilt",
"up", "up",
] ]
+34 -16
View File
@@ -6,7 +6,9 @@ Tests for correct code completion of import statements.
""" """
import json import json
from pybricks_jedi import CompletionItem, complete
import pytest
from pybricks_jedi import CompletionItem, complete, update_user_modules
def test_from(): def test_from():
@@ -27,11 +29,36 @@ def test_from():
] ]
@pytest.fixture
def user_modules():
update_user_modules(["jedi", "pytest"])
yield
update_user_modules([])
def test_from_with_user_modules(user_modules):
code = "from "
completions: list[CompletionItem] = json.loads(complete(code, 1, len(code) + 1))
assert [c["insertText"] for c in completions] == [
"jedi",
"micropython",
"pybricks",
"pytest",
"uerrno",
"uio",
"ujson",
"umath",
"urandom",
"uselect",
"ustruct",
"usys",
]
def test_from_pybricks_import(): def test_from_pybricks_import():
code = "from pybricks import " code = "from pybricks import "
completions: list[CompletionItem] = json.loads(complete(code, 1, len(code) + 1)) completions: list[CompletionItem] = json.loads(complete(code, 1, len(code) + 1))
assert [c["insertText"] for c in completions] == [ assert [c["insertText"] for c in completions] == [
"geometry",
"hubs", "hubs",
"iodevices", "iodevices",
"parameters", "parameters",
@@ -46,7 +73,6 @@ def test_from_pybricks_dot():
code = "from pybricks." code = "from pybricks."
completions: list[CompletionItem] = json.loads(complete(code, 1, len(code) + 1)) completions: list[CompletionItem] = json.loads(complete(code, 1, len(code) + 1))
assert [c["insertText"] for c in completions] == [ assert [c["insertText"] for c in completions] == [
"geometry",
"hubs", "hubs",
"iodevices", "iodevices",
"parameters", "parameters",
@@ -56,16 +82,6 @@ def test_from_pybricks_dot():
] ]
def test_from_pybricks_geometry_import():
code = "from pybricks.geometry import "
completions: list[CompletionItem] = json.loads(complete(code, 1, len(code) + 1))
assert [c["insertText"] for c in completions] == [
"Axis",
"Matrix",
"vector",
]
def test_from_pybricks_hubs_import(): def test_from_pybricks_hubs_import():
code = "from pybricks.hubs import " code = "from pybricks.hubs import "
completions: list[CompletionItem] = json.loads(complete(code, 1, len(code) + 1)) completions: list[CompletionItem] = json.loads(complete(code, 1, len(code) + 1))
@@ -99,6 +115,7 @@ def test_from_pybricks_parameters_import():
code = "from pybricks.parameters import " code = "from pybricks.parameters import "
completions: list[CompletionItem] = json.loads(complete(code, 1, len(code) + 1)) completions: list[CompletionItem] = json.loads(complete(code, 1, len(code) + 1))
assert [c["insertText"] for c in completions] == [ assert [c["insertText"] for c in completions] == [
"Axis",
"Button", "Button",
"Color", "Color",
"Direction", "Direction",
@@ -131,17 +148,18 @@ def test_from_pybricks_pupdevices_import():
def test_from_pybricks_robotics_import(): def test_from_pybricks_robotics_import():
code = "from pybricks.robotics import " code = "from pybricks.robotics import "
completions: list[CompletionItem] = json.loads(complete(code, 1, len(code) + 1)) completions: list[CompletionItem] = json.loads(complete(code, 1, len(code) + 1))
assert [c["insertText"] for c in completions] == [ assert [c["insertText"] for c in completions] == ["DriveBase", "GyroDriveBase"]
"DriveBase",
]
def test_from_pybricks_tools_import(): def test_from_pybricks_tools_import():
code = "from pybricks.tools import " code = "from pybricks.tools import "
completions: list[CompletionItem] = json.loads(complete(code, 1, len(code) + 1)) completions: list[CompletionItem] = json.loads(complete(code, 1, len(code) + 1))
assert [c["insertText"] for c in completions] == [ assert [c["insertText"] for c in completions] == [
"cross",
"DataLog", "DataLog",
"Matrix",
"StopWatch", "StopWatch",
"vector",
"wait", "wait",
] ]
+32
View File
@@ -0,0 +1,32 @@
import json
from pybricks_jedi import CompletionItem, complete
def test_get_completion_for_private_globals():
code = """
_X = 0
_
"""
completions: list[CompletionItem] = json.loads(complete(code, 4, 2))
assert [c["insertText"] for c in completions] == ["_X", "__name__"]
def test_get_completion_for_private_attributes():
code = """
class X:
def __init__(self):
self.public = 0
self._protected = 0
self.__private = 0
x = X()
x.
"""
completions: list[CompletionItem] = json.loads(complete(code, 10, 3))
assert [c["insertText"] for c in completions] == [
"public",
"_protected",
"__init__",
]
+1
View File
@@ -33,6 +33,7 @@ def test_hub_dot():
completions: list[CompletionItem] = json.loads(complete(code, 3, len(line) + 1)) completions: list[CompletionItem] = json.loads(complete(code, 3, len(line) + 1))
assert [c["insertText"] for c in completions] == [ assert [c["insertText"] for c in completions] == [
"battery", "battery",
"ble",
"button", "button",
"imu", "imu",
"light", "light",
+6
View File
@@ -33,6 +33,7 @@ def test_hub_dot():
completions: list[CompletionItem] = json.loads(complete(code, 3, len(line) + 1)) completions: list[CompletionItem] = json.loads(complete(code, 3, len(line) + 1))
assert [c["insertText"] for c in completions] == [ assert [c["insertText"] for c in completions] == [
"battery", "battery",
"ble",
"buttons", "buttons",
"charger", "charger",
"display", "display",
@@ -97,7 +98,12 @@ def test_hub_dot_imu_dot():
"acceleration", "acceleration",
"angular_velocity", "angular_velocity",
"heading", "heading",
"orientation",
"ready",
"reset_heading", "reset_heading",
"rotation",
"settings",
"stationary",
"tilt", "tilt",
"up", "up",
] ]
+6
View File
@@ -33,6 +33,7 @@ def test_hub_dot():
completions: list[CompletionItem] = json.loads(complete(code, 3, len(line) + 1)) completions: list[CompletionItem] = json.loads(complete(code, 3, len(line) + 1))
assert [c["insertText"] for c in completions] == [ assert [c["insertText"] for c in completions] == [
"battery", "battery",
"ble",
"button", "button",
"imu", "imu",
"light", "light",
@@ -67,7 +68,12 @@ def test_hub_dot_imu_dot():
"acceleration", "acceleration",
"angular_velocity", "angular_velocity",
"heading", "heading",
"orientation",
"ready",
"reset_heading", "reset_heading",
"rotation",
"settings",
"stationary",
"tilt", "tilt",
"up", "up",
] ]
+70 -9
View File
@@ -31,7 +31,7 @@ def _get_function_signature(module: str, function: str) -> SignatureHelp:
FUNCTION_PARAMS = [ FUNCTION_PARAMS = [
pytest.param("pybricks.tools", "wait", [(["time: Number"], "None")]), pytest.param("pybricks.tools", "wait", [(["time: Number"], "None")]),
pytest.param( pytest.param(
"pybricks.geometry", "pybricks.tools",
"vector", "vector",
[ [
(["x: float", "y: float"], "Matrix"), (["x: float", "y: float"], "Matrix"),
@@ -78,17 +78,51 @@ def _get_constructor_signature(module: str, type: str) -> SignatureHelp:
CONSTRUCTOR_PARAMS = [ CONSTRUCTOR_PARAMS = [
pytest.param("pybricks.hubs", "MoveHub", [[]]), pytest.param(
pytest.param("pybricks.hubs", "CityHub", [[]]), "pybricks.hubs",
"MoveHub",
[["broadcast_channel: int=0", "observe_channels: Sequence[int]=[]"]],
),
pytest.param(
"pybricks.hubs",
"CityHub",
[["broadcast_channel: int=0", "observe_channels: Sequence[int]=[]"]],
),
pytest.param( pytest.param(
"pybricks.hubs", "pybricks.hubs",
"TechnicHub", "TechnicHub",
[["top_side: Axis=Axis.Z", "front_side: Axis=Axis.X"]], [
[
"top_side: Axis=Axis.Z",
"front_side: Axis=Axis.X",
"broadcast_channel: int=0",
"observe_channels: Sequence[int]=[]",
]
],
), ),
pytest.param( pytest.param(
"pybricks.hubs", "pybricks.hubs",
"PrimeHub", "PrimeHub",
[["top_side: Axis=Axis.Z", "front_side: Axis=Axis.X"]], [
[
"top_side: Axis=Axis.Z",
"front_side: Axis=Axis.X",
"broadcast_channel: int=0",
"observe_channels: Sequence[int]=[]",
]
],
),
pytest.param(
"pybricks.hubs",
"EssentialHub",
[
[
"top_side: Axis=Axis.Z",
"front_side: Axis=Axis.X",
"broadcast_channel: int=0",
"observe_channels: Sequence[int]=[]",
]
],
), ),
pytest.param( pytest.param(
"pybricks.pupdevices", "pybricks.pupdevices",
@@ -104,6 +138,7 @@ CONSTRUCTOR_PARAMS = [
"positive_direction: Direction=Direction.CLOCKWISE", "positive_direction: Direction=Direction.CLOCKWISE",
"gears: Optional[Union[Collection[int], Collection[Collection[int]]]]=None", "gears: Optional[Union[Collection[int], Collection[Collection[int]]]]=None",
"reset_angle: bool=True", "reset_angle: bool=True",
"profile: Number=None",
] ]
], ],
), ),
@@ -151,7 +186,7 @@ CONSTRUCTOR_PARAMS = [
], ],
), ),
pytest.param( pytest.param(
"pybricks.geometry", "pybricks.tools",
"Matrix", "Matrix",
[["rows: Sequence[Sequence[float]]"]], [["rows: Sequence[Sequence[float]]"]],
), ),
@@ -306,12 +341,19 @@ METHOD_PARAMS = [
[(["axis: Axis"], "float"), ([], "Matrix")], [(["axis: Axis"], "float"), ([], "Matrix")],
), ),
pytest.param("pybricks.hubs", "TechnicHub", "imu.heading", [([], "float")]), pytest.param("pybricks.hubs", "TechnicHub", "imu.heading", [([], "float")]),
pytest.param("pybricks.hubs", "TechnicHub", "imu.orientation", [([], "Matrix")]),
pytest.param( pytest.param(
"pybricks.hubs", "pybricks.hubs",
"TechnicHub", "TechnicHub",
"imu.reset_heading", "imu.reset_heading",
[(["angle: Number"], "None")], [(["angle: Number"], "None")],
), ),
pytest.param(
"pybricks.hubs",
"TechnicHub",
"imu.rotation",
[(["axis: Axis"], "float")],
),
pytest.param("pybricks.hubs", "TechnicHub", "battery.voltage", [([], "int")]), pytest.param("pybricks.hubs", "TechnicHub", "battery.voltage", [([], "int")]),
pytest.param("pybricks.hubs", "TechnicHub", "battery.current", [([], "int")]), pytest.param("pybricks.hubs", "TechnicHub", "battery.current", [([], "int")]),
pytest.param( pytest.param(
@@ -398,12 +440,19 @@ METHOD_PARAMS = [
[(["axis: Axis"], "float"), ([], "Matrix")], [(["axis: Axis"], "float"), ([], "Matrix")],
), ),
pytest.param("pybricks.hubs", "PrimeHub", "imu.heading", [([], "float")]), pytest.param("pybricks.hubs", "PrimeHub", "imu.heading", [([], "float")]),
pytest.param("pybricks.hubs", "PrimeHub", "imu.orientation", [([], "Matrix")]),
pytest.param( pytest.param(
"pybricks.hubs", "pybricks.hubs",
"PrimeHub", "PrimeHub",
"imu.reset_heading", "imu.reset_heading",
[(["angle: Number"], "None")], [(["angle: Number"], "None")],
), ),
pytest.param(
"pybricks.hubs",
"PrimeHub",
"imu.rotation",
[(["axis: Axis"], "float")],
),
pytest.param( pytest.param(
"pybricks.hubs", "pybricks.hubs",
"PrimeHub", "PrimeHub",
@@ -481,12 +530,19 @@ METHOD_PARAMS = [
[(["axis: Axis"], "float"), ([], "Matrix")], [(["axis: Axis"], "float"), ([], "Matrix")],
), ),
pytest.param("pybricks.hubs", "EssentialHub", "imu.heading", [([], "float")]), pytest.param("pybricks.hubs", "EssentialHub", "imu.heading", [([], "float")]),
pytest.param("pybricks.hubs", "EssentialHub", "imu.orientation", [([], "Matrix")]),
pytest.param( pytest.param(
"pybricks.hubs", "pybricks.hubs",
"EssentialHub", "EssentialHub",
"imu.reset_heading", "imu.reset_heading",
[(["angle: Number"], "None")], [(["angle: Number"], "None")],
), ),
pytest.param(
"pybricks.hubs",
"EssentialHub",
"imu.rotation",
[(["axis: Axis"], "float")],
),
pytest.param("pybricks.hubs", "EssentialHub", "battery.voltage", [([], "int")]), pytest.param("pybricks.hubs", "EssentialHub", "battery.voltage", [([], "int")]),
pytest.param("pybricks.hubs", "EssentialHub", "battery.current", [([], "int")]), pytest.param("pybricks.hubs", "EssentialHub", "battery.current", [([], "int")]),
pytest.param("pybricks.hubs", "EssentialHub", "charger.connected", [([], "bool")]), pytest.param("pybricks.hubs", "EssentialHub", "charger.connected", [([], "bool")]),
@@ -520,7 +576,12 @@ METHOD_PARAMS = [
"settings", "settings",
[(["max_voltage: Number"], "None"), ([], "Tuple[int]")], [(["max_voltage: Number"], "None"), ([], "Tuple[int]")],
), ),
pytest.param("pybricks.pupdevices", "Motor", "speed", [([], "int")]), pytest.param(
"pybricks.pupdevices",
"Motor",
"speed",
[(["window: Number=100"], "int")],
),
pytest.param("pybricks.pupdevices", "Motor", "angle", [([], "int")]), pytest.param("pybricks.pupdevices", "Motor", "angle", [([], "int")]),
pytest.param( pytest.param(
"pybricks.pupdevices", "pybricks.pupdevices",
@@ -637,12 +698,12 @@ METHOD_PARAMS = [
"kp: Optional[Number]=None", "kp: Optional[Number]=None",
"ki: Optional[Number]=None", "ki: Optional[Number]=None",
"kd: Optional[Number]=None", "kd: Optional[Number]=None",
"reserved: Optional[Number]=None", "integral_deadzone: Optional[Number]=None",
"integral_rate: Optional[Number]=None", "integral_rate: Optional[Number]=None",
], ],
"None", "None",
), ),
([], "Tuple[int, int, int, None, int]"), ([], "Tuple[int, int, int, int, int]"),
], ],
), ),
pytest.param( pytest.param(
+10
View File
@@ -2,6 +2,16 @@
<!-- refer to https://keepachangelog.com/en/1.0.0/ for guidance --> <!-- refer to https://keepachangelog.com/en/1.0.0/ for guidance -->
## 2.9.0 - 2023-05-16
### Changed
- Updated docs to v3.3.0b5.
## 2.8.0 - 2023-04-21
### Changed
- Updated docs to v3.3.0b4.
## 2.7.0 - 2022-12-20 ## 2.7.0 - 2022-12-20
### Changed ### Changed
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2018-2021 The Pybricks Authors Copyright (c) 2018-2023 The Pybricks Authors
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@pybricks/ide-docs", "name": "@pybricks/ide-docs",
"version": "2.7.0", "version": "2.9.0",
"description": "Special build of Pybricks API docs for embedding in an IDE.", "description": "Special build of Pybricks API docs for embedding in an IDE.",
"repository": { "repository": {
"type": "git", "type": "git",
+10
View File
@@ -4,6 +4,16 @@
## Unreleased ## Unreleased
## 1.8.0 - 2023-04-21
### Changed
- Updated `pybricks_jedi` Python package to v1.8.0.
## 1.7.0 - 2022-12-28
### Changed
- Updated `pybricks_jedi` Python package to v1.7.0.
## 1.6.0 - 2022-12-09 ## 1.6.0 - 2022-12-09
### Changed ### Changed
+3 -3
View File
@@ -12,7 +12,7 @@ BUILD_DIR = (pathlib.Path(__file__).parent / "build").resolve()
package_json = { package_json = {
"name": "@pybricks/jedi", "name": "@pybricks/jedi",
"version": "1.6.0", "version": "1.8.0",
"description": "Binary distribution of pybricks-jedi Python package and dependencies for use with Pyodide.", "description": "Binary distribution of pybricks-jedi Python package and dependencies for use with Pyodide.",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -30,7 +30,7 @@ whl_map: dict[str, str] = {}
shutil.rmtree(BUILD_DIR, True) shutil.rmtree(BUILD_DIR, True)
BUILD_DIR.mkdir() BUILD_DIR.mkdir()
# download package and depedencies (*.whl files) # download package and dependencies (*.whl files)
subprocess.check_call( subprocess.check_call(
[ [
sys.executable, sys.executable,
@@ -38,7 +38,7 @@ subprocess.check_call(
"pip", "pip",
"download", "download",
"--only-binary=any", "--only-binary=any",
"pybricks-jedi==1.6.0", "pybricks-jedi==1.8.0",
], ],
cwd=BUILD_DIR, cwd=BUILD_DIR,
) )
Generated
+540 -540
View File
File diff suppressed because it is too large Load Diff
+4 -2
View File
@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "pybricks" name = "pybricks"
version = "3.2.0" version = "3.3.0a5"
description = "Documentation and user-API stubs for Pybricks MicroPython" description = "Documentation and user-API stubs for Pybricks MicroPython"
authors = ["The Pybricks Authors <dev@pybricks.com>"] authors = ["The Pybricks Authors <dev@pybricks.com>"]
maintainers = ["Laurens Valk <laurens@pybricks.com>", "David Lechner <david@pybricks.com>" ] maintainers = ["Laurens Valk <laurens@pybricks.com>", "David Lechner <david@pybricks.com>" ]
@@ -29,10 +29,12 @@ packages = [
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.8" python = "^3.8"
[tool.poetry.dev-dependencies] [tool.poetry.group.lint.dependencies]
black = "^22.3.0" black = "^22.3.0"
doc8 = "^0.8.1" doc8 = "^0.8.1"
flake8 = "^4.0" flake8 = "^4.0"
[tool.poetry.group.doc.dependencies]
Sphinx = { git = "https://github.com/pybricks/sphinx.git", rev = "b00124cb" } Sphinx = { git = "https://github.com/pybricks/sphinx.git", rev = "b00124cb" }
sphinx-rtd-theme = "^1.0.0" sphinx-rtd-theme = "^1.0.0"
toml = "^0.10.0" toml = "^0.10.0"
-5
View File
@@ -1,5 +0,0 @@
# This file is strictly for building docs on readthedocs.org
# See pyproject.toml for local development
git+https://github.com/pybricks/sphinx@b00124c#egg=Sphinx
sphinx-rtd-theme==1.0.0
toml
+266 -22
View File
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
# Copyright (c) 2018-2021 The Pybricks Authors # Copyright (c) 2018-2023 The Pybricks Authors
"""Generic cross-platform module for typical devices like lights, displays, """Generic cross-platform module for typical devices like lights, displays,
speakers, and batteries.""" speakers, and batteries."""
@@ -8,8 +8,8 @@ from __future__ import annotations
from typing import Union, Iterable, overload, Optional, Tuple, Collection, TYPE_CHECKING from typing import Union, Iterable, overload, Optional, Tuple, Collection, TYPE_CHECKING
from .geometry import Matrix, Axis from .tools import Matrix
from .parameters import Direction, Stop, Button, Port, Color, Side from .parameters import Axis, Direction, Stop, Button, Port, Color, Side
if TYPE_CHECKING: if TYPE_CHECKING:
from .parameters import Number from .parameters import Number
@@ -219,18 +219,18 @@ class Control:
kp: Optional[Number] = None, kp: Optional[Number] = None,
ki: Optional[Number] = None, ki: Optional[Number] = None,
kd: Optional[Number] = None, kd: Optional[Number] = None,
reserved: Optional[Number] = None, integral_deadzone: Optional[Number] = None,
integral_rate: Optional[Number] = None, integral_rate: Optional[Number] = None,
) -> None: ) -> None:
... ...
@overload @overload
def pid(self) -> Tuple[int, int, int, None, int]: def pid(self) -> Tuple[int, int, int, int, int]:
... ...
def pid(self, *args): def pid(self, *args):
"""pid(kp, ki, kd, reserved, integral_rate) """pid(kp, ki, kd, integral_deadzone, integral_rate)
pid() -> Tuple[int, int, int, None, int] pid() -> Tuple[int, int, int, int, int]
Gets or sets the PID values for position and speed control. Gets or sets the PID values for position and speed control.
@@ -245,7 +245,8 @@ class Control:
kd (int): Derivative position (or proportional speed) control kd (int): Derivative position (or proportional speed) control
constant. It is the feedback torque per constant. It is the feedback torque per
unit of speed: µNm/(deg/s). unit of speed: µNm/(deg/s).
reserved: This setting is not used. integral_deadzone (Number, deg or Number, mm): Zone around the
target where the error integral does not accumulate errors.
integral_rate (Number, deg/s or Number, mm/s): Maximum rate at integral_rate (Number, deg/s or Number, mm/s): Maximum rate at
which the error integral is allowed to grow. which the error integral is allowed to grow.
""" """
@@ -303,6 +304,52 @@ class Control:
""" """
class Model:
"""Class to interact with motor state observer and settings."""
def state(self) -> Tuple[float, float, float, bool]:
"""state() -> Tuple[float, float, float, bool]
Gets the estimated angle, speed, current, and stall state of the motor,
using a simulation model that mimics the real motor.
These estimates are updated faster than the real measurements,
which can be useful when building your own PID controllers.
For most applications it is better to used the *measured*
:meth:`angle <pybricks.pupdevices.Motor.angle>`,
:meth:`speed <pybricks.pupdevices.Motor.speed>`,
:meth:`load <pybricks.pupdevices.Motor.load>`, and
:meth:`stall <pybricks.pupdevices.Motor.stalled>` state instead.
Returns:
Tuple with the estimated angle (deg), speed (deg/s), current (mA),
and stall state (``True`` or ``False``).
"""
@overload
def settings(self, values: tuple) -> None:
...
@overload
def settings(self) -> tuple:
...
def settings(self, speed, time):
"""settings(values)
settings() -> Tuple
Gets or sets model settings as a tuple of integers. If no arguments are
given, this will return the current values. This method is mainly used
to debug the motor model class. Changing these settings should not be
needed in user programs.
.. _model settings: https://docs.pybricks.com/projects/pbio/en/latest/struct__pbio__observer__settings__t.html
Arguments:
values (Tuple): Tuple with `model settings`_.
"""
class Motor(DCMotor): class Motor(DCMotor):
"""Generic class to control motors with built-in rotation sensors.""" """Generic class to control motors with built-in rotation sensors."""
@@ -312,14 +359,18 @@ class Motor(DCMotor):
``control`` attribute of the motor. See :ref:`control` for an overview ``control`` attribute of the motor. See :ref:`control` for an overview
of available methods.""" of available methods."""
model = Model()
"""Model representing the observer that estimates the motor state."""
def __init__( def __init__(
self, self,
port: Port, port: Port,
positive_direction: Direction = Direction.CLOCKWISE, positive_direction: Direction = Direction.CLOCKWISE,
gears: Optional[Union[Collection[int], Collection[Collection[int]]]] = None, gears: Optional[Union[Collection[int], Collection[Collection[int]]]] = None,
reset_angle: bool = True, reset_angle: bool = True,
profile: Number = None,
): ):
"""__init__(port, positive_direction=Direction.CLOCKWISE, gears=None, reset_angle=True) """__init__(port, positive_direction=Direction.CLOCKWISE, gears=None, reset_angle=True, profile=None)
Arguments: Arguments:
port (Port): Port to which the motor is connected. port (Port): Port to which the motor is connected.
@@ -336,12 +387,16 @@ class Motor(DCMotor):
When you specify a gear train, all motor commands and settings When you specify a gear train, all motor commands and settings
are automatically adjusted to account for the resulting gear are automatically adjusted to account for the resulting gear
ratio. The motor direction remains unchanged by this. ratio. The motor direction remains unchanged by this.
reset_angle(bool): reset_angle (bool):
Choose ``True`` to reset the rotation sensor value to the Choose ``True`` to reset the rotation sensor value to the
absolute marker angle (between -180 and 179). absolute marker angle (between -180 and 179).
Choose ``False`` to keep the Choose ``False`` to keep the
current value, so your program knows where it left off last current value, so your program knows where it left off last
time. time.
profile (Number, deg): Precision profile. A lower value
means more precise movement; a larger value means
smoother movement. If no value is given, a suitable profile for
this motor type will be selected automatically.
""" """
def angle(self) -> int: def angle(self) -> int:
@@ -353,11 +408,19 @@ class Motor(DCMotor):
Motor angle. Motor angle.
""" """
def speed(self) -> int: def speed(self, window: Number = 100) -> int:
"""speed() -> int: deg/s """speed(window=100) -> int: deg/s
Gets the speed of the motor. Gets the speed of the motor.
The speed is measured as the change in the motor angle during the
given time window. A short window makes the speed value more
responsive to motor movement, but less steady. A long window makes the
speed value less responsive, but more steady.
Arguments:
window (Number, ms): The time window used to determine the speed.
Returns: Returns:
Motor speed. Motor speed.
@@ -731,7 +794,7 @@ class LightMatrix:
Arguments: Arguments:
matrices (iter): Sequence of matrices (iter): Sequence of
:class:`Matrix <pybricks.geometry.Matrix>` of intensities. :class:`Matrix <pybricks.tools.Matrix>` of intensities.
interval (Number, ms): Time to display each image in the list. interval (Number, ms): Time to display each image in the list.
""" """
@@ -928,22 +991,94 @@ class Accelerometer(SimpleAccelerometer):
along the x-axis. along the x-axis.
Returns: Returns:
Tuple of pitch and roll angles. Tuple of pitch and roll angles in degrees.
""" """
class IMU(Accelerometer): class IMU(Accelerometer):
def ready(self) -> bool:
"""ready() -> bool
Checks if the device is calibrated and ready for use.
This becomes ``True`` when the robot has been sitting stationary for a
few seconds, which allows the device to re-calibrate. It is ``False``
if the hub has just been started, or if it hasn't had a chance to
calibrate for more than 10 minutes.
Returns:
``True`` if it is ready for use, ``False`` if not.
"""
def stationary(self) -> bool:
"""stationary() -> bool
Checks if the device is currently stationary (not moving).
Returns:
``True`` if stationary for at least a second, ``False`` if it is
moving.
"""
@overload
def settings(
self,
angular_velocity_threshold: float = None,
acceleration_threshold: float = None,
) -> None:
...
@overload
def settings(self) -> Tuple[float, float]:
...
def settings(self, *args):
"""
settings(angular_velocity_threshold, acceleration_threshold)
settings() -> Tuple[float, float]
Configures the IMU settings. If no arguments are given,
this returns the current values.
The ``angular_velocity_threshold`` and ``acceleration_threshold``
define when the hub is considered stationary. If all
measurements stay below these thresholds for one second, the IMU
will recalibrate itself.
In a noisy room with high ambient vibrations (such as a
competition hall), it is recommended to increase the thresholds
slightly to give your robot the chance to calibrate.
To verify that your settings are working as expected, test that
the ``stationary()`` method gives ``False`` if your robot is moving,
and ``True`` if it is sitting still for at least a second.
Arguments:
angular_velocity_threshold (Number, deg/s): The threshold for
angular velocity. The default value is 1.5 deg/s.
acceleration_threshold (Number, mm/): The threshold for angular
velocity. The default value is 250 mm/.
"""
def heading(self) -> float: def heading(self) -> float:
"""heading() -> float: deg """heading() -> float: deg
Gets the heading angle relative to the starting orientation. It is a Gets the heading angle of your robot. A positive value means a
positive rotation around the :ref:`z-axis in the robot clockwise turn.
frame <robotframe>`, prior to applying any tilt rotation.
For a vehicle viewed from the top, this means that The heading is 0 when your program starts. The value continues to grow
a positive heading value corresponds to a counterclockwise rotation. even as the robot turns more than 180 degrees. It does not wrap around
to -180 like it does in some apps.
.. note:: This method is not yet implemented.
.. note:: *For now, this method only keeps track of the heading while
the robot is on a flat surface.*
This means that the value is
no longer correct if you lift it from the table. To solve
this, you can call ``reset_heading`` to reset the heading to
a known value *after* you put it back down. For example, you
could align your robot with the side of the competition table
and reset the heading 90 degrees as the new starting point.
Returns: Returns:
Heading angle relative to starting orientation. Heading angle relative to starting orientation.
@@ -955,8 +1090,6 @@ class IMU(Accelerometer):
Resets the accumulated heading angle of the robot. Resets the accumulated heading angle of the robot.
.. note:: This method is not yet implemented.
Arguments: Arguments:
angle (Number, deg): Value to which the heading should be reset. angle (Number, deg): Value to which the heading should be reset.
""" """
@@ -985,6 +1118,41 @@ class IMU(Accelerometer):
this returns a vector of accelerations along all axes. this returns a vector of accelerations along all axes.
""" """
def rotation(self, axis: Axis) -> float:
"""
rotation(axis) -> float: deg
Gets the rotation of the device along a given axis in
the :ref:`robot reference frame <robotframe>`.
This value is useful if your robot *only* rotates along the requested
axis. For general three-dimensional motion, use the
``orientation()`` method instead.
The value starts counting from ``0`` when you initialize this class.
Arguments:
axis (Axis): Axis along which the rotation should be measured.
Returns:
The rotation angle.
"""
def orientation(self) -> Matrix:
"""
orientation() -> Matrix
Gets the three-dimensional orientation of the robot in
the :ref:`robot reference frame <robotframe>`.
It returns a rotation matrix whose columns represent the ``X``, ``Y``,
and ``Z`` axis of the robot.
.. note:: This method is not yet implemented.
Returns:
The rotation matrix.
"""
class CommonColorSensor: class CommonColorSensor:
"""Generic color sensor that supports Pybricks color calibration.""" """Generic color sensor that supports Pybricks color calibration."""
@@ -1117,3 +1285,79 @@ class AmbientColorSensor(CommonColorSensor):
Measured color. The color is described by a hue (0--359), a Measured color. The color is described by a hue (0--359), a
saturation (0--100), and a brightness value (0--100). saturation (0--100), and a brightness value (0--100).
""" """
class BLE:
"""
Bluetooth Low Energy.
.. versionadded:: 3.3
"""
def broadcast(self, *args: Union[None, bool, int, float, str, bytes]) -> None:
"""broadcast(data0, data1, ...)
Starts broadcasting the given data values.
Each value can be any of ``int``, ``float``, ``str`, ``bytes``,
``None``, ``True``, or ``False``. The data is broadcasted on the
*broadcast_channel* you selected when initializing the hub.
The total data size is quite limited (26 bytes). ``None``, ``True`` and
``False`` take 1 byte each. ``float`` takes 5 bytes. ``int`` takes 2 to
5 bytes depending on how big the number is. ``str`` and ``bytes`` take
the number of bytes in the object plus one extra byte.
Params:
args: Zero or more values to be broadcast.
.. versionadded:: 3.3
"""
def observe(
self, channel: int
) -> Optional[Tuple[Union[None, bool, int, float, str, bytes], ...]]:
"""observe(channel) -> tuple | None
Retrieves the last observed data for a given channel.
Args:
channel (int): The channel to observe (0 to 255).
Returns:
A tuple of the received data or ``None`` if no recent data is
available.
.. tip:: Receiving data is more reliable when the hub is not connected
to a computer or other devices at the same time.
.. versionadded:: 3.3
"""
def signal_strength(self, channel: int) -> int:
"""signal_strength(channel) -> int: dBm
Gets the average signal strength in dBm for the given channel.
This is useful for detecting how near the broadcasting device is. A close
device may have a signal strength around -40 dBm while a far away device
might have a signal strength around -70 dBm.
Args:
channel (int): The channel number (0 to 255).
Returns:
The signal strength or ``-128`` if there is no recent observed data.
.. versionadded:: 3.3
"""
def version(self) -> str:
"""version() -> str
Gets the firmware version from the Bluetooth chip.
.. versionadded:: 3.3
"""
+2 -4
View File
@@ -168,14 +168,12 @@ class InfraredSensor:
class GyroSensor: class GyroSensor:
"""LEGO® MINDSTORMS® EV3 Gyro Sensor.""" """LEGO® MINDSTORMS® EV3 Gyro Sensor."""
def __init__( def __init__(self, port: _Port, direction: _Direction = _Direction.CLOCKWISE):
self, port: _Port, positive_direction: _Direction = _Direction.CLOCKWISE
):
"""GyroSensor(port) """GyroSensor(port)
Arguments: Arguments:
port (Port): Port to which the sensor is connected. port (Port): Port to which the sensor is connected.
positive_direction (Direction): direction (Direction):
Positive rotation direction when looking at the red dot on top Positive rotation direction when looking at the red dot on top
of the sensor. of the sensor.
-132
View File
@@ -1,132 +0,0 @@
# SPDX-License-Identifier: MIT
# Copyright (c) 2018-2022 The Pybricks Authors
"""Core linear algebra functionality for orientation sensors and robotics."""
from __future__ import annotations
from typing import Sequence, Tuple, overload
class Matrix:
"""Mathematical representation of a matrix. It supports
addition (``A + B``), subtraction (``A - B``),
and matrix multiplication (``A * B``) for matrices of compatible size.
It also supports scalar multiplication (``c * A`` or ``A * c``)
and scalar division (``A / c``).
A :class:`.Matrix` object is immutable."""
def __add__(self, other) -> Matrix:
...
def __iadd__(self, other) -> Matrix:
...
def __sub__(self, other) -> Matrix:
...
def __isub__(self, other) -> Matrix:
...
def __mul__(self, other) -> Matrix:
...
def __rmul__(self, other) -> Matrix:
...
def __imul__(self, other) -> Matrix:
...
def __truediv__(self, other) -> Matrix:
...
def __itruediv__(self, other) -> Matrix:
...
def __floordiv__(self, other) -> Matrix:
...
def __ifloordiv__(self, other) -> Matrix:
...
def __init__(self, rows: Sequence[Sequence[float]]):
"""Matrix(rows)
Arguments:
rows (list): List of rows. Each row is itself a list of numbers.
"""
@property
def T(self) -> Matrix: # noqa: N802
"""Returns a new :class:`.Matrix` that is the transpose of the
original."""
@property
def shape(self) -> Tuple[int, int]:
"""Returns a tuple (``m``, ``n``),
where ``m`` is the number of rows and ``n`` is the number of columns.
"""
@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):
"""
vector(x, y) -> Matrix
vector(x, y, z) -> Matrix
Convenience function to create a :class:`.Matrix` with the
shape (``2``, ``1``) or (``3``, ``1``).
Arguments:
x (float): x-coordinate of the vector.
y (float): y-coordinate of the vector.
z (float): z-coordinate of the vector (optional).
Returns:
A matrix with the shape of a column vector.
"""
class Axis:
"""Unit axes of a coordinate system.
.. data:: X = vector(1, 0, 0)
.. data:: Y = vector(0, 1, 0)
.. data:: Z = vector(0, 0, 1)
"""
X: Matrix = vector(1, 0, 0)
Y: Matrix = vector(0, 1, 0)
Z: Matrix = vector(0, 0, 1)
+100 -9
View File
@@ -1,12 +1,14 @@
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
# Copyright (c) 2018-2022 The Pybricks Authors # Copyright (c) 2018-2023 The Pybricks Authors
"""LEGO® Programmable Hubs.""" """LEGO® Programmable Hubs."""
from typing import Sequence
from . import _common from . import _common
from .ev3dev import _speaker from .ev3dev import _speaker
from .geometry import Axis
from .media.ev3dev import Image as _Image from .media.ev3dev import Image as _Image
from .parameters import Button as _Button from .parameters import Button as _Button, Axis
class EV3Brick: class EV3Brick:
@@ -39,6 +41,25 @@ class MoveHub:
imu = _common.SimpleAccelerometer() imu = _common.SimpleAccelerometer()
system = _common.System() system = _common.System()
button = _common.Keypad([_Button.CENTER]) button = _common.Keypad([_Button.CENTER])
ble = _common.BLE()
def __init__(
self, broadcast_channel: int = 0, observe_channels: Sequence[int] = []
):
"""MoveHub(broadcast_channel=0, observe_channels=[])
Arguments:
broadcast_channel:
A value from 0 to 255 indicating which channel ``hub.ble.broadcast()``
will use. Default is channel 0.
observe_channels:
A list of channels to listen to when ``hub.ble.observe()`` is
called. Listening to more channels requires more memory.
Default is an empty list (no channels).
.. versionchanged:: 3.3
Added *broadcast_channel* and *observe_channels* arguments.
"""
class CityHub: class CityHub:
@@ -50,6 +71,25 @@ class CityHub:
light = _common.ColorLight() light = _common.ColorLight()
system = _common.System() system = _common.System()
button = _common.Keypad([_Button.CENTER]) button = _common.Keypad([_Button.CENTER])
ble = _common.BLE()
def __init__(
self, broadcast_channel: int = 0, observe_channels: Sequence[int] = []
):
"""CityHub(broadcast_channel=0, observe_channels=[])
Arguments:
broadcast_channel:
A value from 0 to 255 indicating which channel ``hub.ble.broadcast()``
will use. Default is channel 0.
observe_channels:
A list of channels to listen to when ``hub.ble.observe()`` is
called. Listening to more channels requires more memory.
Default is an empty list (no channels).
.. versionchanged:: 3.3
Added *broadcast_channel* and *observe_channels* arguments.
"""
class TechnicHub: class TechnicHub:
@@ -62,9 +102,16 @@ class TechnicHub:
imu = _common.IMU() imu = _common.IMU()
system = _common.System() system = _common.System()
button = _common.Keypad([_Button.CENTER]) button = _common.Keypad([_Button.CENTER])
ble = _common.BLE()
def __init__(self, top_side: Axis = Axis.Z, front_side: Axis = Axis.X): def __init__(
"""TechnicHub(top_side=Axis.Z, front_side=Axis.X) self,
top_side: Axis = Axis.Z,
front_side: Axis = Axis.X,
broadcast_channel: int = 0,
observe_channels: Sequence[int] = [],
):
"""TechnicHub(top_side=Axis.Z, front_side=Axis.X, broadcast_channel=0, observe_channels=[])
Initializes the hub. Optionally, specify how the hub is Initializes the hub. Optionally, specify how the hub is
:ref:`placed in your design <robotframe>` by saying in which :ref:`placed in your design <robotframe>` by saying in which
@@ -76,6 +123,16 @@ class TechnicHub:
the hub. the hub.
front_side (Axis): The axis that passes through the *front side* of front_side (Axis): The axis that passes through the *front side* of
the hub. the hub.
broadcast_channel:
A value from 0 to 255 indicating which channel ``hub.ble.broadcast()``
will use. Default is channel 0.
observe_channels:
A list of channels to listen to when ``hub.ble.observe()`` is
called. Listening to more channels requires more memory.
Default is an empty list (no channels).
.. versionchanged:: 3.3
Added *broadcast_channel* and *observe_channels* arguments.
""" """
@@ -90,9 +147,16 @@ class EssentialHub:
light = _common.ColorLight() light = _common.ColorLight()
imu = _common.IMU() imu = _common.IMU()
system = _common.System() system = _common.System()
ble = _common.BLE()
def __init__(self, top_side=Axis.Z, front_side=Axis.X): def __init__(
"""__init__(top_side=Axis.Z, front_side=Axis.X) self,
top_side: Axis = Axis.Z,
front_side: Axis = Axis.X,
broadcast_channel: int = 0,
observe_channels: Sequence[int] = [],
):
"""EssentialHub(top_side=Axis.Z, front_side=Axis.X, broadcast_channel=0, observe_channels=[])
Initializes the hub. Optionally, specify how the hub is Initializes the hub. Optionally, specify how the hub is
:ref:`placed in your design <robotframe>` by saying in which :ref:`placed in your design <robotframe>` by saying in which
@@ -104,6 +168,16 @@ class EssentialHub:
the hub. the hub.
front_side (Axis): The axis that passes through the *front side* of front_side (Axis): The axis that passes through the *front side* of
the hub. the hub.
broadcast_channel:
A value from 0 to 255 indicating which channel ``hub.ble.broadcast()``
will use. Default is channel 0.
observe_channels:
A list of channels to listen to when ``hub.ble.observe()`` is
called. Listening to more channels requires more memory.
Default is an empty list (no channels).
.. versionchanged:: 3.3
Added *broadcast_channel* and *observe_channels* arguments.
""" """
pass pass
@@ -128,9 +202,16 @@ class PrimeHub:
speaker = _common.Speaker() speaker = _common.Speaker()
imu = _common.IMU() imu = _common.IMU()
system = _common.System() system = _common.System()
ble = _common.BLE()
def __init__(self, top_side: Axis = Axis.Z, front_side: Axis = Axis.X): def __init__(
"""PrimeHub(top_side=Axis.Z, front_side=Axis.X) self,
top_side: Axis = Axis.Z,
front_side: Axis = Axis.X,
broadcast_channel: int = 0,
observe_channels: Sequence[int] = [],
):
"""PrimeHub(top_side=Axis.Z, front_side=Axis.X, broadcast_channel=0, observe_channels=[])
Initializes the hub. Optionally, specify how the hub is Initializes the hub. Optionally, specify how the hub is
:ref:`placed in your design <robotframe>` by saying in which :ref:`placed in your design <robotframe>` by saying in which
@@ -142,6 +223,16 @@ class PrimeHub:
the hub. the hub.
front_side (Axis): The axis that passes through the *front side* of front_side (Axis): The axis that passes through the *front side* of
the hub. the hub.
broadcast_channel:
A value from 0 to 255 indicating which channel ``hub.ble.broadcast()``
will use. Default is channel 0.
observe_channels:
A list of channels to listen to when ``hub.ble.observe()`` is
called. Listening to more channels requires more memory.
Default is an empty list (no channels).
.. versionchanged:: 3.3
Added *broadcast_channel* and *observe_channels* arguments.
""" """
+15 -1
View File
@@ -9,7 +9,7 @@ from enum import Enum
from typing import Union, TYPE_CHECKING from typing import Union, TYPE_CHECKING
import os import os
from .geometry import Matrix as _Matrix from .tools import Matrix as _Matrix, vector as _vector
if TYPE_CHECKING or os.environ.get("SPHINX_BUILD") == "True": if TYPE_CHECKING or os.environ.get("SPHINX_BUILD") == "True":
Number = Union[int, float] Number = Union[int, float]
@@ -57,6 +57,20 @@ class _PybricksEnum(Enum, metaclass=_PybricksEnumMeta):
return str(self) return str(self)
class Axis:
"""Unit axes of a coordinate system.
.. data:: X = vector(1, 0, 0)
.. data:: Y = vector(0, 1, 0)
.. data:: Z = vector(0, 0, 1)
"""
X: _Matrix = _vector(1, 0, 0)
Y: _Matrix = _vector(0, 1, 0)
Z: _Matrix = _vector(0, 0, 1)
class Color: class Color:
"""Light or surface color.""" """Light or surface color."""
+8 -3
View File
@@ -38,8 +38,9 @@ class Motor(_common.Motor):
positive_direction: Direction = Direction.CLOCKWISE, positive_direction: Direction = Direction.CLOCKWISE,
gears: Optional[Union[Collection[int], Collection[Collection[int]]]] = None, gears: Optional[Union[Collection[int], Collection[Collection[int]]]] = None,
reset_angle: bool = True, reset_angle: bool = True,
profile: Number = None,
): ):
"""__init__(port, positive_direction=Direction.CLOCKWISE, gears=None, reset_angle=True) """__init__(port, positive_direction=Direction.CLOCKWISE, gears=None, reset_angle=True, profile=None)
Arguments: Arguments:
port (Port): Port to which the motor is connected. port (Port): Port to which the motor is connected.
@@ -56,12 +57,16 @@ class Motor(_common.Motor):
When you specify a gear train, all motor commands and settings When you specify a gear train, all motor commands and settings
are automatically adjusted to account for the resulting gear are automatically adjusted to account for the resulting gear
ratio. The motor direction remains unchanged by this. ratio. The motor direction remains unchanged by this.
reset_angle(bool): reset_angle (bool):
Choose ``True`` to reset the rotation sensor value to the Choose ``True`` to reset the rotation sensor value to the
absolute marker angle (between -180 and 179). absolute marker angle (between -180 and 179).
Choose ``False`` to keep the Choose ``False`` to keep the
current value, so your program knows where it left off last current value, so your program knows where it left off last
time. time.
profile (Number, deg): Precision profile. A lower value
means more precise movement; a larger value means
smoother movement. If no value is given, a suitable profile for
this motor type will be selected automatically.
""" """
def reset_angle(self, angle: Optional[Number] = None) -> None: def reset_angle(self, angle: Optional[Number] = None) -> None:
@@ -92,7 +97,7 @@ class Remote:
Button.RIGHT_PLUS, Button.RIGHT_PLUS,
) )
) )
addresss: Union[str, None] address: Union[str, None]
def __init__(self, name: Optional[str] = None, timeout: int = 10000): def __init__(self, name: Optional[str] = None, timeout: int = 10000):
"""Remote(name=None, timeout=10000) """Remote(name=None, timeout=10000)
+18 -9
View File
@@ -28,9 +28,7 @@ class DriveBase:
**Positive** angles and turn rates mean turning **right**. **Positive** angles and turn rates mean turning **right**.
**Negative** means **left**. So when viewed from the top, **Negative** means **left**. So when viewed from the top,
positive means clockwise and negative means counterclockwise. If desired, positive means clockwise and negative means counterclockwise.
you can flip this convention by reversing the ``left_motor`` and
``right_motor`` when you initialize this class.
See the `measuring`_ section for tips to measure and adjust the diameter See the `measuring`_ section for tips to measure and adjust the diameter
and axle track values. and axle track values.
@@ -135,25 +133,30 @@ class DriveBase:
... ...
def settings(self, *args): def settings(self, *args):
"""settings(straight_speed, straight_acceleration, turn_rate, turn_acceleration) """
settings(straight_speed, straight_acceleration, turn_rate, turn_acceleration)
settings() -> Tuple[int, int, int, int] settings() -> Tuple[int, int, int, int]
Configures the speed and acceleration used Configures the drive base speed and acceleration.
by :meth:`.straight`, :meth:`.turn`, and :meth:`.curve`.
If you give no arguments, this returns the current values as a tuple. If you give no arguments, this returns the current values as a tuple.
The default values are automatically configured based on your wheel The initial values are automatically configured based on your wheel
diameter and axle track. They are selected such that your robot diameter and axle track. They are selected such that your robot
drives at about 40% of its maximum speed. drives at about 40% of its maximum speed.
The speed values given here do not apply to the :meth:`.drive` method,
since you provide your own speed values as arguments in that method.
Arguments: Arguments:
straight_speed (Number, mm/s): Straight-line speed of the robot. straight_speed (Number, mm/s): Straight-line speed of the robot.
straight_acceleration (Number, mm/): Straight-line straight_acceleration (Number, mm/): Straight-line
acceleration and deceleration of the robot. acceleration and deceleration of the robot. Provide a tuple with
two values to set acceleration and deceleration separately.
turn_rate (Number, deg/s): Turn rate of the robot. turn_rate (Number, deg/s): Turn rate of the robot.
turn_acceleration (Number, deg/): Angular acceleration and turn_acceleration (Number, deg/): Angular acceleration and
deceleration of the robot. deceleration of the robot. Provide a tuple with
two values to set acceleration and deceleration separately.
""" """
def straight( def straight(
@@ -219,6 +222,12 @@ class DriveBase:
""" """
class GyroDriveBase(DriveBase):
"""A robotic vehicle with two powered wheels and an optional support
wheel or caster. It measures the heading using the hub's built-in gyroscope,
which can make turning and driving straight more accurate."""
# HACK: hide from jedi # HACK: hide from jedi
if TYPE_CHECKING: if TYPE_CHECKING:
del Motor del Motor
+128 -3
View File
@@ -1,11 +1,11 @@
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
# Copyright (c) 2018-2022 The Pybricks Authors # Copyright (c) 2018-2023 The Pybricks Authors
"""Common tools for timing and data logging.""" """Common tools for timing, data logging, and linear algebra."""
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING, Any from typing import TYPE_CHECKING, Any, Sequence, Tuple, overload
if TYPE_CHECKING: if TYPE_CHECKING:
from .parameters import Number from .parameters import Number
@@ -97,6 +97,131 @@ class DataLog:
""" """
class Matrix:
"""Mathematical representation of a matrix. It supports
addition (``A + B``), subtraction (``A - B``),
and matrix multiplication (``A * B``) for matrices of compatible size.
It also supports scalar multiplication (``c * A`` or ``A * c``)
and scalar division (``A / c``).
A :class:`.Matrix` object is immutable."""
def __add__(self, other) -> Matrix:
...
def __iadd__(self, other) -> Matrix:
...
def __sub__(self, other) -> Matrix:
...
def __isub__(self, other) -> Matrix:
...
def __mul__(self, other) -> Matrix:
...
def __rmul__(self, other) -> Matrix:
...
def __imul__(self, other) -> Matrix:
...
def __truediv__(self, other) -> Matrix:
...
def __itruediv__(self, other) -> Matrix:
...
def __floordiv__(self, other) -> Matrix:
...
def __ifloordiv__(self, other) -> Matrix:
...
def __init__(self, rows: Sequence[Sequence[float]]):
"""Matrix(rows)
Arguments:
rows (list): List of rows. Each row is itself a list of numbers.
"""
@property
def T(self) -> Matrix: # noqa: N802
"""Returns a new :class:`.Matrix` that is the transpose of the
original."""
@property
def shape(self) -> Tuple[int, int]:
"""Returns a tuple (``m``, ``n``),
where ``m`` is the number of rows and ``n`` is the number of columns.
"""
@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):
"""
vector(x, y) -> Matrix
vector(x, y, z) -> Matrix
Convenience function to create a :class:`.Matrix` with the
shape (``2``, ``1``) or (``3``, ``1``).
Arguments:
x (float): x-coordinate of the vector.
y (float): y-coordinate of the vector.
z (float): z-coordinate of the vector (optional).
Returns:
A matrix with the shape of a column vector.
"""
def cross(a: Matrix, b: Matrix) -> Matrix:
"""
cross(a, b) -> Matrix
Gets the cross product ``a`` × ``b`` of two vectors.
Arguments:
a (Matrix): A three-dimensional vector.
b (Matrix): A three-dimensional vector.
Returns:
The cross product, also a three-dimensional vector.
"""
# HACK: hide from jedi # HACK: hide from jedi
if TYPE_CHECKING: if TYPE_CHECKING:
del Number del Number
+4 -3
View File
@@ -1074,8 +1074,9 @@ def round(*args):
truncate trailing zeros. To print numbers nicely, format strings instead:: truncate trailing zeros. To print numbers nicely, format strings instead::
# print two decimal places # print two decimal places
print('my number: %.2f' % number) print('my number: print('my number: %.2f' % number)
{:.2f}'.format(number)) print('my number: {:.2f}'.format(number))
print(f'my number: {number:.2f}')
Arguments: Arguments:
number (float): The number to be rounded. number (float): The number to be rounded.
@@ -1169,7 +1170,7 @@ class str:
If no argument is given, this creates an empty ``str`` object. If no argument is given, this creates an empty ``str`` object.
Arguments: Arguments:
object: If only this argument is given, this returns the stirng object: If only this argument is given, this returns the string
representation of the object. representation of the object.
encoding (str): If the first argument is a ``bytearray`` or ``bytes`` encoding (str): If the first argument is a ``bytearray`` or ``bytes``
object and the encoding argument is ``"utf-8"``, this will decode object and the encoding argument is ``"utf-8"``, this will decode
+1 -1
View File
@@ -88,7 +88,7 @@ def getrandbits(k: int) -> int:
""" """
getrandbits(k) -> int getrandbits(k) -> int
Gets a random integer :math:`N` satisfying :math:`0 \\leq N < 2^{\\text{bits}}`. Gets a random integer :math:`N` satisfying :math:`0 \\leq N < 2^{\\text{k}}`.
Arguments: Arguments:
k (int): How many bits to use for the result. k (int): How many bits to use for the result.